SlideShare a Scribd company logo
1 of 89
Browser Extensions
        for Web Hackers
        JSConf EU 2010, Berlin, Germany
        Mark Wubben




A talk about browser extensions in Chrome & Safari, why they’re so great for web hackers
and how to build them.

Given at JSConf EU on September 26th, 2010 in Berlin, Germany.

Licensed under Creative Commons Attribution-Share Alike 2.5
http://creativecommons.org/licenses/by-sa/2.5/dk/

Photo by Steve Peck, http://www.flickr.com/photos/stevepeck/4615314670/. CC-BY-2.0.
I’m a Web Hacker
I’m a Web Hacker. That might scare some people, but I don’t hack highway signs or break
into computers. I (try to) build cool shit.

I think you’re web hackers as well, otherwise, why would you be here? But if you’re a web
hacker, why limit yourself to building websites? Why not hack other systems? Build, say,
browser extensions?

Photo by James Kim, http://www.flickr.com/photos/underbiteman/2638246638/. CC-
BY-2.0.
Add-ons, Plugins, Extensions
My question to you is, have you ever build a browser extension? Did you try? Did you look
into it and got scared? That’s been my story—Firefox extension have always looked too
bewildering. You’d have to know about RDF, XUL, XPCOM. You had to restart your browser
every time you wanted to try something. Bah!

But luckily, times have changed. Chrome and Safari are showing a new way of building
extensions. Easy, based on Open Web Technology.

Photo by Jon Fife, http://www.flickr.com/photos/good-karma/652486713/. CC-BY-SA-2.0.
Open Web Technology
See, these new extension platforms are based on JavaScript, mostly. But also HTML and CSS.
And the cool new HTML5ish APIs like localStorage or geo-location.

Let’s dive in.

Photo by Kevin Dooley, http://www.flickr.com/photos/pagedooley/4126491780/. CC-
BY-2.0.
Diving in to… what?
We’ll have a look soon at the underlying concepts of these new extension platforms. But you
might wonder, I’ve mentioned Chrome & Safari, what about Mozilla?

The good news is that Mozilla is renewing its platform — through Jetpack. However the
current stage of development is focused on building out the fundamentals of its platform.
They also take a different approach that I can’t fit into the 30 minute time slot. We therefore
won’t cover Jetpack much in this talk.

Photo by Justin De La Ornellas, http://www.flickr.com/photos/ornellas/4380104720/. CC-BY
2.0.
We’re going to start off with Chrome. Later during this talk we’ll look at how the concepts
used in Chrome translate to Safari.

Photo by Matt Biddulph, http://www.flickr.com/photos/mbiddulph/2924278682/. CC-BY-SA
2.0.
This is the Extensions page in Chrome. It shows you which extensions you have installed. You
can disable or uninstall them, see their web page, allow them to run in Incognito mode.

At the bottom is a link to the Google Chrome Extensions Gallery. At the top is an option to
enable Developer mode.
Developer mode lets you load a new extension from your local file system. You can also
package extensions for release or force the installed extensions to be updated. Normally this
is done automatically when the browser is restarted.
So, what is an extension?!




Now the obvious question is, what *is* an extension?!
An extension is a folder
         with a manifest.json file




In essence, an extension isn’t much more than a folder that contains a manifest.json file.

Let’s try loading a few folders as an extension.
Any folder? Any JSON?
{
     "name": "Chrome Extensions Rock!",
     "version": "1.0"
 }




manifest.json
And there’s the manifest for our very simple extension. These are the only two required
properties, a name for your extension and a version number.

Admittedly, this extension doesn’t do much.

For more about the manifest file, see http://code.google.com/chrome/extensions/
manifest.html.
Extensions can have
        content scripts.




One of the things a Chrome Extension can do is to run scripts on the pages you visit. These
are content scripts and should be familiar, because its a concept from the Firefox-based
Greasemonkey add-on.
Photo by Brian Sawyer, http://www.flickr.com/photos/olivepress/61618336/. CC-BY-SA 2.0.
Aaron Boodman / Greasemonkey
In fact, the guy who invented Greasemonkey, Aaron Boodman, has been at Google for a few
years now and is one of the guys behind the new Extensions platform. To put it differently,
Chrome Extensions is Greasemonkey on steroids.

Photo by Mark Wubben, http://www.flickr.com/photos/novemberborn/230693761/. CC-BY-
SA-2.0.
Fixing Twitter opening
        links in new windows




You might have noticed how external links on Twitter always open in a new window. I find
this annoying, so I figured I’d write an extension to fix it.
{
     "name": "Twitter Fixer",
     "version": "1.0.0",
     "description": "Fix the external links…",
     "content_scripts": [{
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }




manifest.json
The manifest for our new extension, dubbed Twitter Fixer.
{
     "name": "Twitter Fixer",
     "version": "1.0.0",
     "description": "Fix the external links…",
     "content_scripts": [{
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }




manifest.json
Note how I’ve added a description.
{
     "name": "Twitter Fixer",
     "version": "1.0.0",
     "description": "Fix the external links…",
     "content_scripts": [{
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }




manifest.json
You can specify multiple content scripts per extension.
{
     "name": "Twitter Fixer",
     "version": "1.0.0",
     "description": "Fix the external links…",
     "content_scripts": [{
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }




manifest.json
A content scripts needs to match a page. This is done through match patterns. Here we
specify our extension will run on any page or subdomain on twitter.com, over HTTP as well as
HTTPS.

Keep in mind that the user is warned about the sites you might match. The more restrictive
your match pattern, the better.

To learn more, see http://code.google.com/chrome/extensions/match_patterns.html.
{
     "name": "Twitter Fixer",
     "version": "1.0.0",
     "description": "Fix the external links…",
     "content_scripts": [{
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }




manifest.json
A content script itself can exist of any number of JavaScript files. They’re loaded in the same
order as you specify in the manifest.

You can also specify CSS files that need to be added to the page your content script runs on.

To learn more, see http://code.google.com/chrome/extensions/content_scripts.html.
var links = document.querySelectorAll(
      "a[target=_blank]");

 Array.prototype.slice.call(links).forEach(
     function(a){
       a.removeAttribute("target");
     });




fixer.js
And the actual content script.
Demo time
{
    "name": "Twitter Fixer",
    "version": "1.0.0",
    "description": "Fix the external links…",
    "content_scripts": [{
       "matches": ["http://*.twitter.com/*",
                   "https://*.twitter.com/*"],
       "js": ["vapor.js", "fixer.js"]
    }]
}




manifest.json
Isolated Worlds
Chrome has learned from the security problems that existed with Greasemonkey, and even
with Firefox add-ons as a whole. Each extension lives in a so-called “isolated world”,
meaning it’s isolated from other extensions save for a few tightly controlled communication
bridges.

Photo by F.H. Mira, http://www.flickr.com/photos/fhmira/3204656258/sizes/o/. CC-BY-
SA-2.0.
Content scripts run in
        separate contexts




For example, the JavaScript inside your content scripts is evaluated in a separate context
from the page JavaScript. This means your code won’t affect the page code, and vice versa.
You can’t directly call page code, and it can’t directly call your code.
Shared DOM




Luckily the page document is shared between the various content scripts that might be
running on it. That way, you can change it!
Communicating with page
         JavaScript




But with these isolated worlds, how can your content scripts talk to the page JavaScript? Well,
you’ve got access to the DOM, so you can insert your own JavaScript into the page! And, you
can use DOM events so the inserted JavaScript can talk back to you.
document.documentElement.addEventListener(
    "ExtensionNotify",
    function(){ alert("Notified!"); },
    false
 );

 var s = document.createElement("script");
 s.textContent = 'function notifyContentScript(){
   var evt = document.createEvent("Event");
   evt.initEvent("ExtensionNotify", false, false);
   document.documentElement.dispatchEvent(evt);
 }';
 document.body.appendChild(s);



communication.js
This sets up a content script that insert the `notifyContentScript` method into the page.
When this method is called, a custom DOM event is dispatched on the document element,
which is used to notify the content script.

While you can’t send data along with the event, you can store it in the DOM. The content
script can then look it up.
document.documentElement.addEventListener(
    "ExtensionNotify",
    function(){ alert("Notified!"); },
    false
 );

 var s = document.createElement("script");
 s.textContent = 'function notifyContentScript(){
   var evt = document.createEvent("Event");
   evt.initEvent("ExtensionNotify", false, false);
   document.documentElement.dispatchEvent(evt);
 }';
 document.body.appendChild(s);



communication.js
This sets up a content script that insert the `notifyContentScript` method into the page.
When this method is called, a custom DOM event is dispatched on the document element,
which is used to notify the content script.

While you can’t send data along with the event, you can store it in the DOM. The content
script can then look it up.
document.documentElement.addEventListener(
    "ExtensionNotify",
    function(){ alert("Notified!"); },
    false
 );

 var s = document.createElement("script");
 s.textContent = 'function notifyContentScript(){
   var evt = document.createEvent("Event");
   evt.initEvent("ExtensionNotify", false, false);
   document.documentElement.dispatchEvent(evt);
 }';
 document.body.appendChild(s);



communication.js
This sets up a content script that insert the `notifyContentScript` method into the page.
When this method is called, a custom DOM event is dispatched on the document element,
which is used to notify the content script.

While you can’t send data along with the event, you can store it in the DOM. The content
script can then look it up.
document.documentElement.addEventListener(
    "ExtensionNotify",
    function(){ alert("Notified!"); },
    false
 );

 var s = document.createElement("script");
 s.textContent = 'function notifyContentScript(){
   var evt = document.createEvent("Event");
   evt.initEvent("ExtensionNotify", false, false);
   document.documentElement.dispatchEvent(evt);
 }';
 document.body.appendChild(s);



communication.js
This sets up a content script that insert the `notifyContentScript` method into the page.
When this method is called, a custom DOM event is dispatched on the document element,
which is used to notify the content script.

While you can’t send data along with the event, you can store it in the DOM. The content
script can then look it up.
Demo time
Content scripts are limited.
        Background pages!




Content scripts are fairly limited though. They exist only as long as the page they run on
exists. They don’t have access to any permanent storage, so you can’t configure them. Nor
can they talk to other websites, so you can’t look up anything through an API.

Luckily, Chrome Extensions let you build background pages. These are normal HTML pages,
except that they’re not rendered. They’re loaded when the browser starts, and won’t be
unloaded until it’s closed.

Let’s build a more complicated extension.
Expanding bit.ly URLs on
         Twitter




Due to character constraints URLs in Tweets are often shortened. But, I’d like to see where
I’m going! Let’s write Chrome Extension that can expand bit.ly URLs.
{
    "name": "Twitter Fixer",
    "version": "1.1.0",
    "description": "Expands shortened URLs…",
    "permissions": ["http://api.bit.ly/*"],
    "background_page": "background.html",
    "content_scripts": [{
       "run_at": "document_end",
       "matches": ["http://*.twitter.com/*",
                   "https://*.twitter.com/*"],
       "js": ["fixer.js"]
    }]
}



manifest.json
{
     "name": "Twitter Fixer",
     "version": "1.1.0",
     "description": "Expands shortened URLs…",
     "permissions": ["http://api.bit.ly/*"],
     "background_page": "background.html",
     "content_scripts": [{
        "run_at": "document_end",
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }



manifest.json
I’ve made two major modifications to the manifest.json we used previously. First is loading
the background page, this is done using the background_page property whose value is the
relative path (from the manifest.json file) to the background page. By convention this is
named background.html, but you can name it whatever you like.

The other change is that I’m now requesting permission to talk to the Bit.ly API. Chrome
forces the extension developer to request permission for almost anything. When the user
installs your extension he’s made aware of what you’re extension will have permission to,
therefore making it harder for nefarious Extension developers to sneak bad stuff into their
extensions without the users knowing about it.
{
     "name": "Twitter Fixer",
     "version": "1.1.0",
     "description": "Expands shortened URLs…",
     "permissions": ["http://api.bit.ly/*"],
     "background_page": "background.html",
     "content_scripts": [{
        "run_at": "document_end",
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }



manifest.json
Another change I made is to specify the `run_at` property for the content script. This way I
can make sure it runs right after the page document has finished parsing, so we don’t have
to wait too long before we can expand the bit.ly URLs.
var parsed = parseUrls();
 chrome.extension.sendRequest(
    parsed.hashes,
    function(mapping){
      for(hash in mapping){
        parsed.links[hash].forEach(function(link){
          link.textContent = mapping[hash];
        });
      }
    }
 );




fixer.js
The code to find the URLs in the page isn’t terribly important so I’ve not put it in this slide.
Suffice to say, `parsed` contains a list of bit.ly hashes, and a mapping from a hash to one or
more link elements.
var parsed = parseUrls();
 chrome.extension.sendRequest(
    parsed.hashes,
    function(mapping){
      for(hash in mapping){
        parsed.links[hash].forEach(function(link){
          link.textContent = mapping[hash];
        });
      }
    }
 );




fixer.js
The content script needs to talk to the background page to expand the hashes. This is done
through the `chrome.extension.sendRequest` API.
var parsed = parseUrls();
 chrome.extension.sendRequest(
    parsed.hashes,
    function(mapping){
      for(hash in mapping){
        parsed.links[hash].forEach(function(link){
          link.textContent = mapping[hash];
        });
      }
    }
 );




fixer.js
Also note how I can use forEach on an array. Chrome has a fully up-to-date JavaScript engine
so native forEach is available.

Same for using textContent to set the link text value.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 chrome.extension.onRequest.addListener(
    function(hashes, sender, sendResponse){
      // …
      sendResponse(mapping);
    }
 );
 </script>




background.html
I won’t go into the specifics of how to talk to bit.ly and get the expanded URLs.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 chrome.extension.onRequest.addListener(
    function(hashes, sender, sendResponse){
      // …
      sendResponse(mapping);
    }
 );
 </script>




background.html
Note how I can pull other code into the background page. It’s a web page, after all.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 chrome.extension.onRequest.addListener(
    function(hashes, sender, sendResponse){
      // …
      sendResponse(mapping);
    }
 );
 </script>




background.html
The important bit is how you register a handler for requests from the content script. You get
the request object, a reference to the tab from which the request was sent, and a callback
function to call once you have a response.

This communication mechanism is completely asynchronous.
Demo time
Debugging
        Web Inspector




You can easily debug your extension using the Web Inspector you would normally use to
debug your web pages. You can set break points or use the debugger keyword. An inspector
for the background page can be opened by clicking on the “background.html” link in the
Extensions page (if you have Developer mode enabled).

You may also notice how the background page is actually at “chrome-extension://some-
unique-identifier/background.html”. This is the domain it runs in, so the extension can
piggyback on the normal same-origin restrictions!
Quick Recap




To recap:

* Chrome extensions are folders containing a manifest.json file
* You can have Greasemonkey-like content scripts that act on the page
* You can have HTML-driven background pages for generic/shared logic, talking to other
  sites
* You can easily communicate between content scripts and background pages
So, how do these principles translate to Safari?

Photo by William Warby, http://www.flickr.com/photos/wwarby/2405490902/. CC-BY 2.0.
Here’s Safari’s Extensions preference pane. It lists the extensions you have installed.
In order to start developing extensions, make sure to enable the Develop menu. Then, under
the Develop menu, you’ll find an “Extension Builder” item.
This then is the Extension Builder, which initially is really quite barren. Let’s make a very
simple extension by clicking the “+” icon in the bottom left.
Safari lets you manage your extension configuration through a nice enough UI, whereas
Chrome makes you edit a JSON file. Another difference is that Apple requires you to register
with their Safari developer program (for free). This then gives you access to your own Safari
Developer Certificate. Without the certificate, you can’t even *test* your extension locally.
developer.apple.com




Just go to developer.apple.com/safari and follow the steps to obtain your certificate.
And once you’ve done so, the Install button becomes active. Click Install and your extension
is installed!
See?
Content scripts?




Yes, Safari also has content scripts, although it’s called “Injected Extension Content”. You
configure them through the Extension Builder. Let’s see about implementing the Twitter Fixer
extension in Safari.
First we set the permissions for our extension. It can access some domains, namely
twitter.com and subdomains, including secure pages.
First we set the permissions for our extension. It can access some domains, namely
twitter.com and subdomains, including secure pages.
Then we add the content script, setting it to run when the document has loaded.
Info.plist is Safari’s manifest.json. It’s a typical Apple file, that isn’t nice to edit manually. Yay
for Extension Builder!

fixer.js is of course our content script. In fact, it’s the exact same script we used for Chrome!
Background pages?




Safari has background pages too, except that they’re called “global pages”.
You specify the global page file in the Extension Builder as well.
You specify the global page file in the Extension Builder as well.

Note that we also need to request permission to connect to the bit.ly API.
Demo time




Let’s have a look at the Twitter Fixer in Safari.
Differences…




Of course the Chrome extension didn’t *just* work in Safari. The extension APIs are different!

For this extension, the only platform specific APIs we used were for communicating between
the content script and the background page. Let’s compare Chrome to Safari.
var parsed = parseUrls();
 chrome.extension.sendRequest(
    parsed.hashes,
    function(mapping){
      for(hash in mapping){
        parsed.links[hash].forEach(function(link){
          link.textContent = mapping[hash];
        });
      }
    }
 );




fixer.js
This is the original content script as used in Chrome.
var parsed = parseUrls();
 chrome.extension.sendRequest(
    parsed.hashes,
    function(mapping){
      for(hash in mapping){
        parsed.links[hash].forEach(function(link){
          link.textContent = mapping[hash];
        });
      }
    }
 );




fixer.js
Let’s focus on the communication to the background page. We send a payload to the
background page and provide a callback that can be called by the background page when it
has a response.
var parsed = parseUrls();
 safari.self.tab.dispatchMessage("expandHashes",
     parsed.hashes);

 safari.self.addEventListener("message",
     function(evt){
       if(evt.name == "mappingComplete"){
         var mapping = evt.message;
         // …
       }
     }, false);




fixer.js
Safari takes a different approach. It has a special message event that content scripts and
global pages (etc) can listen to. The event has a name and a message property which contains
the original message object. `dispatchMessage()` is used to send these events.

This example is for content scripts. The messages arrive at different objects for global pages.
var parsed = parseUrls();
 safari.self.tab.dispatchMessage("expandHashes",
     parsed.hashes);

 safari.self.addEventListener("message",
     function(evt){
       if(evt.name == "mappingComplete"){
         var mapping = evt.message;
         // …
       }
     }, false);




fixer.js
Safari takes a different approach. It has a special message event that content scripts and
global pages (etc) can listen to. The event has a name and a message property which contains
the original message object. `dispatchMessage()` is used to send these events.
var parsed = parseUrls();
 safari.self.tab.dispatchMessage("expandHashes",
     parsed.hashes);

 safari.self.addEventListener("message",
     function(evt){
       if(evt.name == "mappingComplete"){
         var mapping = evt.message;
         // …
       }
     }, false);




fixer.js
Safari takes a different approach. It has a special message event that content scripts and
global pages (etc) can listen to. The event has a name and a message property which contains
the original message object. `dispatchMessage()` is used to send these events.
var parsed = parseUrls();
 safari.self.tab.dispatchMessage("expandHashes",
     parsed.hashes);

 safari.self.addEventListener("message",
     function(evt){
       if(evt.name == "mappingComplete"){
         var mapping = evt.message;
         // …
       }
     }, false);




fixer.js
Safari takes a different approach. It has a special message event that content scripts and
global pages (etc) can listen to. The event has a name and a message property which contains
the original message object. `dispatchMessage()` is used to send these events.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 chrome.extension.onRequest.addListener(
    function(hashes, sender, sendResponse){
      // …
      sendResponse(mapping);
    }
 );
 </script>




background.html
Here then the original background page script as used in Chrome.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 chrome.extension.onRequest.addListener(
    function(hashes, sender, sendResponse){
      // …
      sendResponse(mapping);
    }
 );
 </script>




background.html
Here then the original background page script as used in Chrome.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 safari.application.addEventListener("message",
     function(evt){
        if(evt.name != "expandHashes"){ return; }
        // …
        evt.target.page.dispatchMessage(
             "mappingComplete", mapping);
     },
     false);
 </script>



background.html
For the background page, we listen to the message event on `safari.application`. We can
send a response by dispatching a message to the content script the message event originates
from.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 safari.application.addEventListener("message",
     function(evt){
        if(evt.name != "expandHashes"){ return; }
        // …
        evt.target.page.dispatchMessage(
             "mappingComplete", mapping);
     },
     false);
 </script>



background.html
For the background page, we listen to the message event on `safari.application`. We can
send a response by dispatching a message to the content script the message event originates
from.
<!doctype html>

 <script src="dojo.js"></script>
 <script>
 safari.application.addEventListener("message",
     function(evt){
        if(evt.name != "expandHashes"){ return; }
        // …
        evt.target.page.dispatchMessage(
             "mappingComplete", mapping);
     },
     false);
 </script>



background.html
For the background page, we listen to the message event on `safari.application`. We can
send a response by dispatching a message to the content script the message event originates
from.
Extension Settings




Now for something more advanced: can we provide settings for our extension?
Yes we can! There’s a nice UI for it in the Extension Builder, even.
And here’s the end result.
safari.extension.settings




You can read the settings from `safari.extension.settings`, though not in content scripts.
You’ll have to use the communication APIs to retrieve the settings from the background page.
Can we do this in Chrome, too? Well, yes, except that Chrome doesn’t have a nice UI and API
for managing settings. You’ll have to do it yourself, with the help of a special background
page and localStorage.

Photo by Matt Biddulph, http://www.flickr.com/photos/mbiddulph/2924278682/. CC-BY-SA
2.0.
{
     "name": "Twitter Fixer",
     "version": "1.2.0",
     "description": "Expands shortened URLs…",
     "permissions": ["http://api.bit.ly/*"],
     "options_page": "options.html",
     "background_page": "background.html",
     "content_scripts": [{
        "run_at": "document_end",
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }


manifest.json
Chrome supports Options pages, which are just like background pages except that they can
be opened directly.
{
     "name": "Twitter Fixer",
     "version": "1.2.0",
     "description": "Expands shortened URLs…",
     "permissions": ["http://api.bit.ly/*"],
     "options_page": "options.html",
     "background_page": "background.html",
     "content_scripts": [{
        "run_at": "document_end",
        "matches": ["http://*.twitter.com/*",
                    "https://*.twitter.com/*"],
        "js": ["fixer.js"]
     }]
 }


manifest.json
Chrome supports Options pages, which are just like background pages except that they can
be opened directly.
Demo time
So, about Jetpack…




The approach taken by both Chrome and Safari is one of content scripts and background
pages, in essence normal HTML. Jetpack is taking a CommonJS approach where your
extension consists of a main script that adds the UI elsewhere. I do encourage you to check it
out.
Bonus!
         Safari/Chrome hybrid extension




Given that both Chrome and Safari use folders for their extensions, it’s quite possible to build
a hybrid extension that can be loaded into both Chrome *and* Safari.

(Of course, once compiled for release, this would no longer be the case).

I’ve combined the latest version of Twitter Fixer so it works in both browsers. This is done by
abstracting the communication and settings logic. For Chrome, the Options page is built
dynamically based on the Settings configured for Safari.
Thank you



        Mark Wubben

        supercollider.dk & novemberborn.net
        twitter.com/novemberborn

        Slides: 11born.net/jsconfeu/slides
        Code: 11born.net/jsconfeu/code


                                               Licensed under Creative Commons Attribution-Share Alike 2.5
                                                        http://creativecommons.org/licenses/by-sa/2.5/dk/




And that concludes this talk. Thank you for your attention.
Steve Peck
   James Kim
   Jon Fife
   Kevin Dooley                                F H Mira
   Justin De La Ornellas                       William Warby
   Matt Biddulph                               Jeff Kubina
   Brian Sawyer                                Matt Jones



Many, many thanks to the wonderful people on Flickr who licensed their photos under
Creative Commons.

Photo by Jeff Kubina, http://flickr.com/photos/kubina/903033693/. CC-BY-SA 2.0.
Now, as Matt Jones would put it, GET EXCITED and MAKE THINGS!

Illustration by Matt Jones, CC-BY-SA-NC, http://www.flickr.com/photos/blackbeltjones/
3365682994/.

More Related Content

What's hot

Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJRealize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJLeonardo Balter
 
Even faster web sites
Even faster web sitesEven faster web sites
Even faster web sitesFelipe Lavín
 
Opening up the Social Web - Standards that are bridging the Islands
Opening up the Social Web - Standards that are bridging the IslandsOpening up the Social Web - Standards that are bridging the Islands
Opening up the Social Web - Standards that are bridging the IslandsBastian Hofmann
 
.htaccess for SEOs - A presentation by Roxana Stingu
.htaccess for SEOs - A presentation by Roxana Stingu.htaccess for SEOs - A presentation by Roxana Stingu
.htaccess for SEOs - A presentation by Roxana StinguRoxana Stingu
 
Advanced SEO for Web Developers
Advanced SEO for Web DevelopersAdvanced SEO for Web Developers
Advanced SEO for Web DevelopersNathan Buggia
 
Your Presentation Name Here
Your Presentation Name HereYour Presentation Name Here
Your Presentation Name HereFreedSoftwares
 
Finding things on the web with BOSS
Finding things on the web with BOSSFinding things on the web with BOSS
Finding things on the web with BOSSChristian Heilmann
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?brynary
 
REST, the internet as a database?
REST, the internet as a database?REST, the internet as a database?
REST, the internet as a database?Andrej Koelewijn
 
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaReno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaDre Armeda
 
Fronteers 20091105 (1)
Fronteers 20091105 (1)Fronteers 20091105 (1)
Fronteers 20091105 (1)guestbf04d7
 
jQuery from the very beginning
jQuery from the very beginningjQuery from the very beginning
jQuery from the very beginningAnis Ahmad
 
Application Security for RIAs
Application Security for RIAsApplication Security for RIAs
Application Security for RIAsjohnwilander
 
The internet for SEOs by Roxana Stingu
The internet for SEOs by Roxana StinguThe internet for SEOs by Roxana Stingu
The internet for SEOs by Roxana StinguRoxana Stingu
 
Introduction to WordPress Theme Development
Introduction to WordPress Theme DevelopmentIntroduction to WordPress Theme Development
Introduction to WordPress Theme DevelopmentSitdhibong Laokok
 

What's hot (20)

Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJRealize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
Realize mais com HTML 5 e CSS 3 - 16 EDTED - RJ
 
Even faster web sites
Even faster web sitesEven faster web sites
Even faster web sites
 
PHP
PHPPHP
PHP
 
Opening up the Social Web - Standards that are bridging the Islands
Opening up the Social Web - Standards that are bridging the IslandsOpening up the Social Web - Standards that are bridging the Islands
Opening up the Social Web - Standards that are bridging the Islands
 
.htaccess for SEOs - A presentation by Roxana Stingu
.htaccess for SEOs - A presentation by Roxana Stingu.htaccess for SEOs - A presentation by Roxana Stingu
.htaccess for SEOs - A presentation by Roxana Stingu
 
Advanced SEO for Web Developers
Advanced SEO for Web DevelopersAdvanced SEO for Web Developers
Advanced SEO for Web Developers
 
Your Presentation Name Here
Your Presentation Name HereYour Presentation Name Here
Your Presentation Name Here
 
JSON and the APInauts
JSON and the APInautsJSON and the APInauts
JSON and the APInauts
 
Finding things on the web with BOSS
Finding things on the web with BOSSFinding things on the web with BOSS
Finding things on the web with BOSS
 
What's new in Rails 2?
What's new in Rails 2?What's new in Rails 2?
What's new in Rails 2?
 
REST, the internet as a database?
REST, the internet as a database?REST, the internet as a database?
REST, the internet as a database?
 
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre ArmedaReno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
Reno-Tahoe WordCamp 2011 - WordPress End User Security - Dre Armeda
 
Fronteers 20091105 (1)
Fronteers 20091105 (1)Fronteers 20091105 (1)
Fronteers 20091105 (1)
 
jQuery from the very beginning
jQuery from the very beginningjQuery from the very beginning
jQuery from the very beginning
 
The Loop
The LoopThe Loop
The Loop
 
Application Security for RIAs
Application Security for RIAsApplication Security for RIAs
Application Security for RIAs
 
The internet for SEOs by Roxana Stingu
The internet for SEOs by Roxana StinguThe internet for SEOs by Roxana Stingu
The internet for SEOs by Roxana Stingu
 
SlideShare Instant
SlideShare InstantSlideShare Instant
SlideShare Instant
 
SlideShare Instant
SlideShare InstantSlideShare Instant
SlideShare Instant
 
Introduction to WordPress Theme Development
Introduction to WordPress Theme DevelopmentIntroduction to WordPress Theme Development
Introduction to WordPress Theme Development
 

Viewers also liked

Theory from Practice: Stories and Takeaways from Hacker Camps
Theory from Practice: Stories and Takeaways from Hacker CampsTheory from Practice: Stories and Takeaways from Hacker Camps
Theory from Practice: Stories and Takeaways from Hacker CampsMark Wubben
 
Homemade Ubicomp at Reboot 11
Homemade Ubicomp at Reboot 11Homemade Ubicomp at Reboot 11
Homemade Ubicomp at Reboot 11Mark Wubben
 
Building Installations in Five Days (and a bit) at Ignite London 4
Building Installations in Five Days (and a bit) at Ignite London 4Building Installations in Five Days (and a bit) at Ignite London 4
Building Installations in Five Days (and a bit) at Ignite London 4Mark Wubben
 
Economia popular y solidaria
Economia popular y solidariaEconomia popular y solidaria
Economia popular y solidariaGabriela Vargas
 
ÐDaM and Equally presentation at the EDCon conference
ÐDaM and Equally presentation at the EDCon conferenceÐDaM and Equally presentation at the EDCon conference
ÐDaM and Equally presentation at the EDCon conferenceQuentin de Beauchesne
 
Tipos de memoria
Tipos de memoriaTipos de memoria
Tipos de memoriaAlexa Lara
 
Llilum 161108 at MVP Global Summit 2016
Llilum 161108 at MVP Global Summit 2016Llilum 161108 at MVP Global Summit 2016
Llilum 161108 at MVP Global Summit 2016Atomu Hidaka
 
RIsk Management for Events Lesson 4 alcohol, crowd control & emergencies
RIsk Management for Events Lesson 4   alcohol, crowd control & emergenciesRIsk Management for Events Lesson 4   alcohol, crowd control & emergencies
RIsk Management for Events Lesson 4 alcohol, crowd control & emergenciesMervyn Maico Aldana
 

Viewers also liked (18)

Promoting Scholarship with Services, Programs and Publishing
Promoting Scholarship with Services, Programs and PublishingPromoting Scholarship with Services, Programs and Publishing
Promoting Scholarship with Services, Programs and Publishing
 
Theory from Practice: Stories and Takeaways from Hacker Camps
Theory from Practice: Stories and Takeaways from Hacker CampsTheory from Practice: Stories and Takeaways from Hacker Camps
Theory from Practice: Stories and Takeaways from Hacker Camps
 
Florall
FlorallFlorall
Florall
 
Homemade Ubicomp at Reboot 11
Homemade Ubicomp at Reboot 11Homemade Ubicomp at Reboot 11
Homemade Ubicomp at Reboot 11
 
Building Installations in Five Days (and a bit) at Ignite London 4
Building Installations in Five Days (and a bit) at Ignite London 4Building Installations in Five Days (and a bit) at Ignite London 4
Building Installations in Five Days (and a bit) at Ignite London 4
 
Manual barmesa
Manual barmesaManual barmesa
Manual barmesa
 
Economia popular y solidaria
Economia popular y solidariaEconomia popular y solidaria
Economia popular y solidaria
 
Презентація Руху сприяння територіальній обороні України
Презентація Руху сприяння територіальній обороні УкраїниПрезентація Руху сприяння територіальній обороні України
Презентація Руху сприяння територіальній обороні України
 
ÐDaM and Equally presentation at the EDCon conference
ÐDaM and Equally presentation at the EDCon conferenceÐDaM and Equally presentation at the EDCon conference
ÐDaM and Equally presentation at the EDCon conference
 
Tipos de memoria
Tipos de memoriaTipos de memoria
Tipos de memoria
 
Concrete, Concrete Bowling Ball SIS5
Concrete, Concrete Bowling Ball SIS5Concrete, Concrete Bowling Ball SIS5
Concrete, Concrete Bowling Ball SIS5
 
Llilum 161108 at MVP Global Summit 2016
Llilum 161108 at MVP Global Summit 2016Llilum 161108 at MVP Global Summit 2016
Llilum 161108 at MVP Global Summit 2016
 
Memoria virtual
Memoria virtualMemoria virtual
Memoria virtual
 
Rehabilitations
Rehabilitations Rehabilitations
Rehabilitations
 
S'pore 2004
S'pore 2004S'pore 2004
S'pore 2004
 
RIsk Management for Events Lesson 4 alcohol, crowd control & emergencies
RIsk Management for Events Lesson 4   alcohol, crowd control & emergenciesRIsk Management for Events Lesson 4   alcohol, crowd control & emergencies
RIsk Management for Events Lesson 4 alcohol, crowd control & emergencies
 
Sports Materials , Baseball SIS5
Sports Materials , Baseball SIS5Sports Materials , Baseball SIS5
Sports Materials , Baseball SIS5
 
Sports Materials , Baseball SIS5
Sports Materials , Baseball SIS5Sports Materials , Baseball SIS5
Sports Materials , Baseball SIS5
 

Similar to Browser Extensions for Web Hackers

Chrome Extensions for Web Hackers
Chrome Extensions for Web HackersChrome Extensions for Web Hackers
Chrome Extensions for Web HackersMark Wubben
 
Creating chrome-extension
Creating chrome-extensionCreating chrome-extension
Creating chrome-extensionAkshay Khale
 
Orange is the new blue: How to port Chrome Extension to Firefox Extension
Orange is the new blue: How to port Chrome Extension to Firefox ExtensionOrange is the new blue: How to port Chrome Extension to Firefox Extension
Orange is the new blue: How to port Chrome Extension to Firefox Extensionchaykaborya
 
Chrome Extension Develop Starts
Chrome Extension Develop StartsChrome Extension Develop Starts
Chrome Extension Develop Startstaobao.com
 
Google chrome extension
Google chrome extensionGoogle chrome extension
Google chrome extensionJohnny Kingdom
 
Cliw - extension development
Cliw -  extension developmentCliw -  extension development
Cliw - extension developmentvicccuu
 
Developing chrome extensions 2021 04-30
Developing chrome extensions 2021 04-30Developing chrome extensions 2021 04-30
Developing chrome extensions 2021 04-30sy250
 
JavaScript Presentation Frameworks and Libraries
JavaScript Presentation Frameworks and LibrariesJavaScript Presentation Frameworks and Libraries
JavaScript Presentation Frameworks and LibrariesOleksii Prohonnyi
 
Chrome Extensions - Basic concepts powerpoint
Chrome Extensions - Basic concepts powerpointChrome Extensions - Basic concepts powerpoint
Chrome Extensions - Basic concepts powerpointf20190876
 
How and Why to extend Firefox
How and Why to extend FirefoxHow and Why to extend Firefox
How and Why to extend FirefoxGraham King
 
Dependency management in Magento with Composer
Dependency management in Magento with ComposerDependency management in Magento with Composer
Dependency management in Magento with ComposerManuele Menozzi
 
CMS & Chrome Extension Development
CMS & Chrome Extension DevelopmentCMS & Chrome Extension Development
CMS & Chrome Extension DevelopmentSarang Ananda Rao
 
Make an language translator with voice extension
Make an language translator with voice extensionMake an language translator with voice extension
Make an language translator with voice extensionRebecca Peltz
 
Web browser extensions development
Web browser extensions developmentWeb browser extensions development
Web browser extensions developmentdragoslargu
 
1 Web Page Foundations Overview This lab walk.docx
1  Web Page Foundations Overview This lab walk.docx1  Web Page Foundations Overview This lab walk.docx
1 Web Page Foundations Overview This lab walk.docxhoney725342
 
从小书签到浏览器扩展的应用
从小书签到浏览器扩展的应用从小书签到浏览器扩展的应用
从小书签到浏览器扩展的应用Alipay
 
Google Chrome Extensions - DevFest09
Google Chrome Extensions - DevFest09Google Chrome Extensions - DevFest09
Google Chrome Extensions - DevFest09mihaiionescu
 
Build your own Chrome Extension with AngularJS
Build your own Chrome Extension with AngularJSBuild your own Chrome Extension with AngularJS
Build your own Chrome Extension with AngularJSflrent
 
Chrome Extension Step by step Guide .pptx
Chrome Extension Step by step Guide .pptxChrome Extension Step by step Guide .pptx
Chrome Extension Step by step Guide .pptxgeekhouse.io
 

Similar to Browser Extensions for Web Hackers (20)

Chrome Extensions for Web Hackers
Chrome Extensions for Web HackersChrome Extensions for Web Hackers
Chrome Extensions for Web Hackers
 
Creating chrome-extension
Creating chrome-extensionCreating chrome-extension
Creating chrome-extension
 
Orange is the new blue: How to port Chrome Extension to Firefox Extension
Orange is the new blue: How to port Chrome Extension to Firefox ExtensionOrange is the new blue: How to port Chrome Extension to Firefox Extension
Orange is the new blue: How to port Chrome Extension to Firefox Extension
 
Chrome Extension Develop Starts
Chrome Extension Develop StartsChrome Extension Develop Starts
Chrome Extension Develop Starts
 
Google chrome extension
Google chrome extensionGoogle chrome extension
Google chrome extension
 
Cliw - extension development
Cliw -  extension developmentCliw -  extension development
Cliw - extension development
 
Developing chrome extensions 2021 04-30
Developing chrome extensions 2021 04-30Developing chrome extensions 2021 04-30
Developing chrome extensions 2021 04-30
 
JavaScript Presentation Frameworks and Libraries
JavaScript Presentation Frameworks and LibrariesJavaScript Presentation Frameworks and Libraries
JavaScript Presentation Frameworks and Libraries
 
Chrome Extensions - Basic concepts powerpoint
Chrome Extensions - Basic concepts powerpointChrome Extensions - Basic concepts powerpoint
Chrome Extensions - Basic concepts powerpoint
 
How and Why to extend Firefox
How and Why to extend FirefoxHow and Why to extend Firefox
How and Why to extend Firefox
 
Dependency management in Magento with Composer
Dependency management in Magento with ComposerDependency management in Magento with Composer
Dependency management in Magento with Composer
 
CMS & Chrome Extension Development
CMS & Chrome Extension DevelopmentCMS & Chrome Extension Development
CMS & Chrome Extension Development
 
Make an language translator with voice extension
Make an language translator with voice extensionMake an language translator with voice extension
Make an language translator with voice extension
 
Web browser extensions development
Web browser extensions developmentWeb browser extensions development
Web browser extensions development
 
1 Web Page Foundations Overview This lab walk.docx
1  Web Page Foundations Overview This lab walk.docx1  Web Page Foundations Overview This lab walk.docx
1 Web Page Foundations Overview This lab walk.docx
 
从小书签到浏览器扩展的应用
从小书签到浏览器扩展的应用从小书签到浏览器扩展的应用
从小书签到浏览器扩展的应用
 
Google Chrome Extensions - DevFest09
Google Chrome Extensions - DevFest09Google Chrome Extensions - DevFest09
Google Chrome Extensions - DevFest09
 
Build your own Chrome Extension with AngularJS
Build your own Chrome Extension with AngularJSBuild your own Chrome Extension with AngularJS
Build your own Chrome Extension with AngularJS
 
Chrome Extension Step by step Guide .pptx
Chrome Extension Step by step Guide .pptxChrome Extension Step by step Guide .pptx
Chrome Extension Step by step Guide .pptx
 
Browser extension
Browser extensionBrowser extension
Browser extension
 

Recently uploaded

Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 

Recently uploaded (20)

Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 

Browser Extensions for Web Hackers

  • 1. Browser Extensions for Web Hackers JSConf EU 2010, Berlin, Germany Mark Wubben A talk about browser extensions in Chrome & Safari, why they’re so great for web hackers and how to build them. Given at JSConf EU on September 26th, 2010 in Berlin, Germany. Licensed under Creative Commons Attribution-Share Alike 2.5 http://creativecommons.org/licenses/by-sa/2.5/dk/ Photo by Steve Peck, http://www.flickr.com/photos/stevepeck/4615314670/. CC-BY-2.0.
  • 2. I’m a Web Hacker I’m a Web Hacker. That might scare some people, but I don’t hack highway signs or break into computers. I (try to) build cool shit. I think you’re web hackers as well, otherwise, why would you be here? But if you’re a web hacker, why limit yourself to building websites? Why not hack other systems? Build, say, browser extensions? Photo by James Kim, http://www.flickr.com/photos/underbiteman/2638246638/. CC- BY-2.0.
  • 3. Add-ons, Plugins, Extensions My question to you is, have you ever build a browser extension? Did you try? Did you look into it and got scared? That’s been my story—Firefox extension have always looked too bewildering. You’d have to know about RDF, XUL, XPCOM. You had to restart your browser every time you wanted to try something. Bah! But luckily, times have changed. Chrome and Safari are showing a new way of building extensions. Easy, based on Open Web Technology. Photo by Jon Fife, http://www.flickr.com/photos/good-karma/652486713/. CC-BY-SA-2.0.
  • 4. Open Web Technology See, these new extension platforms are based on JavaScript, mostly. But also HTML and CSS. And the cool new HTML5ish APIs like localStorage or geo-location. Let’s dive in. Photo by Kevin Dooley, http://www.flickr.com/photos/pagedooley/4126491780/. CC- BY-2.0.
  • 5. Diving in to… what? We’ll have a look soon at the underlying concepts of these new extension platforms. But you might wonder, I’ve mentioned Chrome & Safari, what about Mozilla? The good news is that Mozilla is renewing its platform — through Jetpack. However the current stage of development is focused on building out the fundamentals of its platform. They also take a different approach that I can’t fit into the 30 minute time slot. We therefore won’t cover Jetpack much in this talk. Photo by Justin De La Ornellas, http://www.flickr.com/photos/ornellas/4380104720/. CC-BY 2.0.
  • 6. We’re going to start off with Chrome. Later during this talk we’ll look at how the concepts used in Chrome translate to Safari. Photo by Matt Biddulph, http://www.flickr.com/photos/mbiddulph/2924278682/. CC-BY-SA 2.0.
  • 7. This is the Extensions page in Chrome. It shows you which extensions you have installed. You can disable or uninstall them, see their web page, allow them to run in Incognito mode. At the bottom is a link to the Google Chrome Extensions Gallery. At the top is an option to enable Developer mode.
  • 8. Developer mode lets you load a new extension from your local file system. You can also package extensions for release or force the installed extensions to be updated. Normally this is done automatically when the browser is restarted.
  • 9. So, what is an extension?! Now the obvious question is, what *is* an extension?!
  • 10. An extension is a folder with a manifest.json file In essence, an extension isn’t much more than a folder that contains a manifest.json file. Let’s try loading a few folders as an extension.
  • 12. { "name": "Chrome Extensions Rock!", "version": "1.0" } manifest.json And there’s the manifest for our very simple extension. These are the only two required properties, a name for your extension and a version number. Admittedly, this extension doesn’t do much. For more about the manifest file, see http://code.google.com/chrome/extensions/ manifest.html.
  • 13. Extensions can have content scripts. One of the things a Chrome Extension can do is to run scripts on the pages you visit. These are content scripts and should be familiar, because its a concept from the Firefox-based Greasemonkey add-on.
  • 14. Photo by Brian Sawyer, http://www.flickr.com/photos/olivepress/61618336/. CC-BY-SA 2.0.
  • 15. Aaron Boodman / Greasemonkey In fact, the guy who invented Greasemonkey, Aaron Boodman, has been at Google for a few years now and is one of the guys behind the new Extensions platform. To put it differently, Chrome Extensions is Greasemonkey on steroids. Photo by Mark Wubben, http://www.flickr.com/photos/novemberborn/230693761/. CC-BY- SA-2.0.
  • 16. Fixing Twitter opening links in new windows You might have noticed how external links on Twitter always open in a new window. I find this annoying, so I figured I’d write an extension to fix it.
  • 17. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json The manifest for our new extension, dubbed Twitter Fixer.
  • 18. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json Note how I’ve added a description.
  • 19. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json You can specify multiple content scripts per extension.
  • 20. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json A content scripts needs to match a page. This is done through match patterns. Here we specify our extension will run on any page or subdomain on twitter.com, over HTTP as well as HTTPS. Keep in mind that the user is warned about the sites you might match. The more restrictive your match pattern, the better. To learn more, see http://code.google.com/chrome/extensions/match_patterns.html.
  • 21. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json A content script itself can exist of any number of JavaScript files. They’re loaded in the same order as you specify in the manifest. You can also specify CSS files that need to be added to the page your content script runs on. To learn more, see http://code.google.com/chrome/extensions/content_scripts.html.
  • 22. var links = document.querySelectorAll( "a[target=_blank]"); Array.prototype.slice.call(links).forEach( function(a){ a.removeAttribute("target"); }); fixer.js And the actual content script.
  • 24. { "name": "Twitter Fixer", "version": "1.0.0", "description": "Fix the external links…", "content_scripts": [{ "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["vapor.js", "fixer.js"] }] } manifest.json
  • 25. Isolated Worlds Chrome has learned from the security problems that existed with Greasemonkey, and even with Firefox add-ons as a whole. Each extension lives in a so-called “isolated world”, meaning it’s isolated from other extensions save for a few tightly controlled communication bridges. Photo by F.H. Mira, http://www.flickr.com/photos/fhmira/3204656258/sizes/o/. CC-BY- SA-2.0.
  • 26. Content scripts run in separate contexts For example, the JavaScript inside your content scripts is evaluated in a separate context from the page JavaScript. This means your code won’t affect the page code, and vice versa. You can’t directly call page code, and it can’t directly call your code.
  • 27. Shared DOM Luckily the page document is shared between the various content scripts that might be running on it. That way, you can change it!
  • 28. Communicating with page JavaScript But with these isolated worlds, how can your content scripts talk to the page JavaScript? Well, you’ve got access to the DOM, so you can insert your own JavaScript into the page! And, you can use DOM events so the inserted JavaScript can talk back to you.
  • 29. document.documentElement.addEventListener( "ExtensionNotify", function(){ alert("Notified!"); }, false ); var s = document.createElement("script"); s.textContent = 'function notifyContentScript(){ var evt = document.createEvent("Event"); evt.initEvent("ExtensionNotify", false, false); document.documentElement.dispatchEvent(evt); }'; document.body.appendChild(s); communication.js This sets up a content script that insert the `notifyContentScript` method into the page. When this method is called, a custom DOM event is dispatched on the document element, which is used to notify the content script. While you can’t send data along with the event, you can store it in the DOM. The content script can then look it up.
  • 30. document.documentElement.addEventListener( "ExtensionNotify", function(){ alert("Notified!"); }, false ); var s = document.createElement("script"); s.textContent = 'function notifyContentScript(){ var evt = document.createEvent("Event"); evt.initEvent("ExtensionNotify", false, false); document.documentElement.dispatchEvent(evt); }'; document.body.appendChild(s); communication.js This sets up a content script that insert the `notifyContentScript` method into the page. When this method is called, a custom DOM event is dispatched on the document element, which is used to notify the content script. While you can’t send data along with the event, you can store it in the DOM. The content script can then look it up.
  • 31. document.documentElement.addEventListener( "ExtensionNotify", function(){ alert("Notified!"); }, false ); var s = document.createElement("script"); s.textContent = 'function notifyContentScript(){ var evt = document.createEvent("Event"); evt.initEvent("ExtensionNotify", false, false); document.documentElement.dispatchEvent(evt); }'; document.body.appendChild(s); communication.js This sets up a content script that insert the `notifyContentScript` method into the page. When this method is called, a custom DOM event is dispatched on the document element, which is used to notify the content script. While you can’t send data along with the event, you can store it in the DOM. The content script can then look it up.
  • 32. document.documentElement.addEventListener( "ExtensionNotify", function(){ alert("Notified!"); }, false ); var s = document.createElement("script"); s.textContent = 'function notifyContentScript(){ var evt = document.createEvent("Event"); evt.initEvent("ExtensionNotify", false, false); document.documentElement.dispatchEvent(evt); }'; document.body.appendChild(s); communication.js This sets up a content script that insert the `notifyContentScript` method into the page. When this method is called, a custom DOM event is dispatched on the document element, which is used to notify the content script. While you can’t send data along with the event, you can store it in the DOM. The content script can then look it up.
  • 34. Content scripts are limited. Background pages! Content scripts are fairly limited though. They exist only as long as the page they run on exists. They don’t have access to any permanent storage, so you can’t configure them. Nor can they talk to other websites, so you can’t look up anything through an API. Luckily, Chrome Extensions let you build background pages. These are normal HTML pages, except that they’re not rendered. They’re loaded when the browser starts, and won’t be unloaded until it’s closed. Let’s build a more complicated extension.
  • 35. Expanding bit.ly URLs on Twitter Due to character constraints URLs in Tweets are often shortened. But, I’d like to see where I’m going! Let’s write Chrome Extension that can expand bit.ly URLs.
  • 36. { "name": "Twitter Fixer", "version": "1.1.0", "description": "Expands shortened URLs…", "permissions": ["http://api.bit.ly/*"], "background_page": "background.html", "content_scripts": [{ "run_at": "document_end", "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json
  • 37. { "name": "Twitter Fixer", "version": "1.1.0", "description": "Expands shortened URLs…", "permissions": ["http://api.bit.ly/*"], "background_page": "background.html", "content_scripts": [{ "run_at": "document_end", "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json I’ve made two major modifications to the manifest.json we used previously. First is loading the background page, this is done using the background_page property whose value is the relative path (from the manifest.json file) to the background page. By convention this is named background.html, but you can name it whatever you like. The other change is that I’m now requesting permission to talk to the Bit.ly API. Chrome forces the extension developer to request permission for almost anything. When the user installs your extension he’s made aware of what you’re extension will have permission to, therefore making it harder for nefarious Extension developers to sneak bad stuff into their extensions without the users knowing about it.
  • 38. { "name": "Twitter Fixer", "version": "1.1.0", "description": "Expands shortened URLs…", "permissions": ["http://api.bit.ly/*"], "background_page": "background.html", "content_scripts": [{ "run_at": "document_end", "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json Another change I made is to specify the `run_at` property for the content script. This way I can make sure it runs right after the page document has finished parsing, so we don’t have to wait too long before we can expand the bit.ly URLs.
  • 39. var parsed = parseUrls(); chrome.extension.sendRequest( parsed.hashes, function(mapping){ for(hash in mapping){ parsed.links[hash].forEach(function(link){ link.textContent = mapping[hash]; }); } } ); fixer.js The code to find the URLs in the page isn’t terribly important so I’ve not put it in this slide. Suffice to say, `parsed` contains a list of bit.ly hashes, and a mapping from a hash to one or more link elements.
  • 40. var parsed = parseUrls(); chrome.extension.sendRequest( parsed.hashes, function(mapping){ for(hash in mapping){ parsed.links[hash].forEach(function(link){ link.textContent = mapping[hash]; }); } } ); fixer.js The content script needs to talk to the background page to expand the hashes. This is done through the `chrome.extension.sendRequest` API.
  • 41. var parsed = parseUrls(); chrome.extension.sendRequest( parsed.hashes, function(mapping){ for(hash in mapping){ parsed.links[hash].forEach(function(link){ link.textContent = mapping[hash]; }); } } ); fixer.js Also note how I can use forEach on an array. Chrome has a fully up-to-date JavaScript engine so native forEach is available. Same for using textContent to set the link text value.
  • 42. <!doctype html> <script src="dojo.js"></script> <script> chrome.extension.onRequest.addListener( function(hashes, sender, sendResponse){ // … sendResponse(mapping); } ); </script> background.html I won’t go into the specifics of how to talk to bit.ly and get the expanded URLs.
  • 43. <!doctype html> <script src="dojo.js"></script> <script> chrome.extension.onRequest.addListener( function(hashes, sender, sendResponse){ // … sendResponse(mapping); } ); </script> background.html Note how I can pull other code into the background page. It’s a web page, after all.
  • 44. <!doctype html> <script src="dojo.js"></script> <script> chrome.extension.onRequest.addListener( function(hashes, sender, sendResponse){ // … sendResponse(mapping); } ); </script> background.html The important bit is how you register a handler for requests from the content script. You get the request object, a reference to the tab from which the request was sent, and a callback function to call once you have a response. This communication mechanism is completely asynchronous.
  • 46. Debugging Web Inspector You can easily debug your extension using the Web Inspector you would normally use to debug your web pages. You can set break points or use the debugger keyword. An inspector for the background page can be opened by clicking on the “background.html” link in the Extensions page (if you have Developer mode enabled). You may also notice how the background page is actually at “chrome-extension://some- unique-identifier/background.html”. This is the domain it runs in, so the extension can piggyback on the normal same-origin restrictions!
  • 47. Quick Recap To recap: * Chrome extensions are folders containing a manifest.json file * You can have Greasemonkey-like content scripts that act on the page * You can have HTML-driven background pages for generic/shared logic, talking to other sites * You can easily communicate between content scripts and background pages
  • 48. So, how do these principles translate to Safari? Photo by William Warby, http://www.flickr.com/photos/wwarby/2405490902/. CC-BY 2.0.
  • 49. Here’s Safari’s Extensions preference pane. It lists the extensions you have installed.
  • 50. In order to start developing extensions, make sure to enable the Develop menu. Then, under the Develop menu, you’ll find an “Extension Builder” item.
  • 51. This then is the Extension Builder, which initially is really quite barren. Let’s make a very simple extension by clicking the “+” icon in the bottom left.
  • 52. Safari lets you manage your extension configuration through a nice enough UI, whereas Chrome makes you edit a JSON file. Another difference is that Apple requires you to register with their Safari developer program (for free). This then gives you access to your own Safari Developer Certificate. Without the certificate, you can’t even *test* your extension locally.
  • 53. developer.apple.com Just go to developer.apple.com/safari and follow the steps to obtain your certificate.
  • 54. And once you’ve done so, the Install button becomes active. Click Install and your extension is installed!
  • 55. See?
  • 56. Content scripts? Yes, Safari also has content scripts, although it’s called “Injected Extension Content”. You configure them through the Extension Builder. Let’s see about implementing the Twitter Fixer extension in Safari.
  • 57. First we set the permissions for our extension. It can access some domains, namely twitter.com and subdomains, including secure pages.
  • 58. First we set the permissions for our extension. It can access some domains, namely twitter.com and subdomains, including secure pages.
  • 59. Then we add the content script, setting it to run when the document has loaded.
  • 60. Info.plist is Safari’s manifest.json. It’s a typical Apple file, that isn’t nice to edit manually. Yay for Extension Builder! fixer.js is of course our content script. In fact, it’s the exact same script we used for Chrome!
  • 61. Background pages? Safari has background pages too, except that they’re called “global pages”.
  • 62. You specify the global page file in the Extension Builder as well.
  • 63. You specify the global page file in the Extension Builder as well. Note that we also need to request permission to connect to the bit.ly API.
  • 64. Demo time Let’s have a look at the Twitter Fixer in Safari.
  • 65. Differences… Of course the Chrome extension didn’t *just* work in Safari. The extension APIs are different! For this extension, the only platform specific APIs we used were for communicating between the content script and the background page. Let’s compare Chrome to Safari.
  • 66. var parsed = parseUrls(); chrome.extension.sendRequest( parsed.hashes, function(mapping){ for(hash in mapping){ parsed.links[hash].forEach(function(link){ link.textContent = mapping[hash]; }); } } ); fixer.js This is the original content script as used in Chrome.
  • 67. var parsed = parseUrls(); chrome.extension.sendRequest( parsed.hashes, function(mapping){ for(hash in mapping){ parsed.links[hash].forEach(function(link){ link.textContent = mapping[hash]; }); } } ); fixer.js Let’s focus on the communication to the background page. We send a payload to the background page and provide a callback that can be called by the background page when it has a response.
  • 68. var parsed = parseUrls(); safari.self.tab.dispatchMessage("expandHashes", parsed.hashes); safari.self.addEventListener("message", function(evt){ if(evt.name == "mappingComplete"){ var mapping = evt.message; // … } }, false); fixer.js Safari takes a different approach. It has a special message event that content scripts and global pages (etc) can listen to. The event has a name and a message property which contains the original message object. `dispatchMessage()` is used to send these events. This example is for content scripts. The messages arrive at different objects for global pages.
  • 69. var parsed = parseUrls(); safari.self.tab.dispatchMessage("expandHashes", parsed.hashes); safari.self.addEventListener("message", function(evt){ if(evt.name == "mappingComplete"){ var mapping = evt.message; // … } }, false); fixer.js Safari takes a different approach. It has a special message event that content scripts and global pages (etc) can listen to. The event has a name and a message property which contains the original message object. `dispatchMessage()` is used to send these events.
  • 70. var parsed = parseUrls(); safari.self.tab.dispatchMessage("expandHashes", parsed.hashes); safari.self.addEventListener("message", function(evt){ if(evt.name == "mappingComplete"){ var mapping = evt.message; // … } }, false); fixer.js Safari takes a different approach. It has a special message event that content scripts and global pages (etc) can listen to. The event has a name and a message property which contains the original message object. `dispatchMessage()` is used to send these events.
  • 71. var parsed = parseUrls(); safari.self.tab.dispatchMessage("expandHashes", parsed.hashes); safari.self.addEventListener("message", function(evt){ if(evt.name == "mappingComplete"){ var mapping = evt.message; // … } }, false); fixer.js Safari takes a different approach. It has a special message event that content scripts and global pages (etc) can listen to. The event has a name and a message property which contains the original message object. `dispatchMessage()` is used to send these events.
  • 72. <!doctype html> <script src="dojo.js"></script> <script> chrome.extension.onRequest.addListener( function(hashes, sender, sendResponse){ // … sendResponse(mapping); } ); </script> background.html Here then the original background page script as used in Chrome.
  • 73. <!doctype html> <script src="dojo.js"></script> <script> chrome.extension.onRequest.addListener( function(hashes, sender, sendResponse){ // … sendResponse(mapping); } ); </script> background.html Here then the original background page script as used in Chrome.
  • 74. <!doctype html> <script src="dojo.js"></script> <script> safari.application.addEventListener("message", function(evt){ if(evt.name != "expandHashes"){ return; } // … evt.target.page.dispatchMessage( "mappingComplete", mapping); }, false); </script> background.html For the background page, we listen to the message event on `safari.application`. We can send a response by dispatching a message to the content script the message event originates from.
  • 75. <!doctype html> <script src="dojo.js"></script> <script> safari.application.addEventListener("message", function(evt){ if(evt.name != "expandHashes"){ return; } // … evt.target.page.dispatchMessage( "mappingComplete", mapping); }, false); </script> background.html For the background page, we listen to the message event on `safari.application`. We can send a response by dispatching a message to the content script the message event originates from.
  • 76. <!doctype html> <script src="dojo.js"></script> <script> safari.application.addEventListener("message", function(evt){ if(evt.name != "expandHashes"){ return; } // … evt.target.page.dispatchMessage( "mappingComplete", mapping); }, false); </script> background.html For the background page, we listen to the message event on `safari.application`. We can send a response by dispatching a message to the content script the message event originates from.
  • 77. Extension Settings Now for something more advanced: can we provide settings for our extension?
  • 78. Yes we can! There’s a nice UI for it in the Extension Builder, even.
  • 79. And here’s the end result.
  • 80. safari.extension.settings You can read the settings from `safari.extension.settings`, though not in content scripts. You’ll have to use the communication APIs to retrieve the settings from the background page.
  • 81. Can we do this in Chrome, too? Well, yes, except that Chrome doesn’t have a nice UI and API for managing settings. You’ll have to do it yourself, with the help of a special background page and localStorage. Photo by Matt Biddulph, http://www.flickr.com/photos/mbiddulph/2924278682/. CC-BY-SA 2.0.
  • 82. { "name": "Twitter Fixer", "version": "1.2.0", "description": "Expands shortened URLs…", "permissions": ["http://api.bit.ly/*"], "options_page": "options.html", "background_page": "background.html", "content_scripts": [{ "run_at": "document_end", "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json Chrome supports Options pages, which are just like background pages except that they can be opened directly.
  • 83. { "name": "Twitter Fixer", "version": "1.2.0", "description": "Expands shortened URLs…", "permissions": ["http://api.bit.ly/*"], "options_page": "options.html", "background_page": "background.html", "content_scripts": [{ "run_at": "document_end", "matches": ["http://*.twitter.com/*", "https://*.twitter.com/*"], "js": ["fixer.js"] }] } manifest.json Chrome supports Options pages, which are just like background pages except that they can be opened directly.
  • 85. So, about Jetpack… The approach taken by both Chrome and Safari is one of content scripts and background pages, in essence normal HTML. Jetpack is taking a CommonJS approach where your extension consists of a main script that adds the UI elsewhere. I do encourage you to check it out.
  • 86. Bonus! Safari/Chrome hybrid extension Given that both Chrome and Safari use folders for their extensions, it’s quite possible to build a hybrid extension that can be loaded into both Chrome *and* Safari. (Of course, once compiled for release, this would no longer be the case). I’ve combined the latest version of Twitter Fixer so it works in both browsers. This is done by abstracting the communication and settings logic. For Chrome, the Options page is built dynamically based on the Settings configured for Safari.
  • 87. Thank you Mark Wubben supercollider.dk & novemberborn.net twitter.com/novemberborn Slides: 11born.net/jsconfeu/slides Code: 11born.net/jsconfeu/code Licensed under Creative Commons Attribution-Share Alike 2.5 http://creativecommons.org/licenses/by-sa/2.5/dk/ And that concludes this talk. Thank you for your attention.
  • 88. Steve Peck James Kim Jon Fife Kevin Dooley F H Mira Justin De La Ornellas William Warby Matt Biddulph Jeff Kubina Brian Sawyer Matt Jones Many, many thanks to the wonderful people on Flickr who licensed their photos under Creative Commons. Photo by Jeff Kubina, http://flickr.com/photos/kubina/903033693/. CC-BY-SA 2.0.
  • 89. Now, as Matt Jones would put it, GET EXCITED and MAKE THINGS! Illustration by Matt Jones, CC-BY-SA-NC, http://www.flickr.com/photos/blackbeltjones/ 3365682994/.