Skip to content

A talk that teaches people to make useless Chrome plugins, with some examples.

Notifications You must be signed in to change notification settings

mattpiccolella/useless-chrome-extensions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Building (Useless) Chrome Extensions

Build (silly) things that can make your browser experience much more enjoyable.

Written and developed by Matt Piccolella and ADI. The GitHub repository for this talk is available here.

Credit to [Chrome Developer] chrome-developer.

Download the sample code here.

Getting Started: FAQs

What is a Chrome Extension?

From Google Chrome's website, Chrome extensions "allow you to add functionality to Chrome without diving deeply into native code." This could include, for example, changing the text that appears on the page, blocking ads that would appear on a page, or add some much-needed doge to your webpages.

Why should I build Chrome extensions?

Chrome extensions are fun, fast, and easy! Using just a few lines of beginner JavaScript code, you can make something that can make people's lives easier, or something that will make them laugh.

What will this tutorial teach me?

This tutorial will teach you the basics of Chrome extensions: what files you'll need, what goes in each file, and how to use it in your Chrome browser. After the tutorial, you will have several Chrome extensions that you can use.

Using this Document

The start of this document walks you through building a Chrome extension and launching it on your browser. After you've gotten your 'Hello World' extension done, we'll give you some examples of things you can do with your Chrome extensions. From there, it's all up to your own creativity!

Table of Contents


1.0 Your First Chrome Extension

Building a Chrome extension is surprisingly easy, and takes just a few pieces of understanding. You'll need at least two files, all of which are written in the familiar HTML, CSS, and JavaScript languages that the rest of the web is written in. This is wonderful because we won't need to worry about any of the internals of Chrome, which can be quite complicated, in order to get our apps working.

1.1 manifest.json

One of our files must be manifest.json. This file is a small file which provides some details about what the Chrome extension is: things like name, version number, a description of what your extension does, what images/scripts you'll be using, etc. This is generally just a few lines long, and contains just a few details. Here is a simple manifest file:

{
  "manifest_version": 2,
  "name": "My First Chrome Extension",
  "version": "1.0",
  "description": "The very first chrome extension I've ever written!",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ]
}

Let's go through this piece by piece.

  1. manifest_version: This is the version of the manifest specification we're using, basically just describing what format of the file we're currently using. This will always be 2, for our purposes.
  2. name: The name of our Chrome extension. Be creative!
  3. version: A string to represent the version of our application. Generally a good idea to start with 1.0 or 1.0.0, but this is purely descriptive.
  4. description: The description of your extension. This let's your users know what they're in for, so it's best to be descriptive.
  5. content_scripts: This field is the first of the ones we've seen that is optional. Content scripts allow us to specify some scripts that we want to run in the browser context, which basically means we have access to the DOM, or the HTML elements of the page.
    1. We use matches to provide a regular expression for websites we'd match: *://*/* matches all websites! However, say we only wanted this to happen on Google websites, we could provide http://www.google.com/*. We can also provide more than one of these.
    2. We use js to provide the scripts we want to run. For example, here we provide the file name script.js, which tells the browser that we will be providing a file script.js that the browser will run. **We will create this file in the next section.
    3. We use run_at to tell the browser when the script should execute. In this case, we want to run it at document_end, which means once the entire page has loaded.

This is just one simple manifest.json file. There are lots of other attributes you can set, such as background, which allows us to run a single script in the background, permissions, which allow us to ask the user for certain things we may not be able to do withour permission, and browser_action, which creates small icons next to the search bar that you can click and have something happen. However, content_scripts is what we'll mainly be looking in this tutorial.

1.2 Writing your JavaScript

In the same directory that you have your manifest.json file, now create a new file and call it script.js; remember, this is the name that we used in our manifest.json file, so we're now going to create that script. Now our directory, which I've called first-chrome-extension, has our two files:

manifest.json script.js

Inside of script.js, put the following line:

console.log("My first chrome extension!");

That's it! With this simple line of JavaScript, we can prove that we have a working Chrome extension, which will print My first chrome extension! in the JavaScript console on every page that you load.

1.3 Using your Extension

Let's run first-chrome-extension now. To do this, you're going to need to open Chrome (install here if you haven't already).

Next, visit chrome://extensions by typing it into the toolbar and pressing enter (or selecting More Tools > Extensions from the Chrome menu).Once you've done this, make sure the option for Developer mode in the top-right corner is selected, as follows:

Developer Mode

Once this is checked, select the option for Load unpacked extension.... In the dialogue that appears, select the directory in which you have been keeping your files, for me first-chrome-extension. Once you've done this, your Chrome extension is installed!

To try it out, try visiting a website, like https://google.com. Once you've done this, open up your JavaScript console (More Tools > JavaScript Console or Alt-Command-J). You should see the following:

First Extension

Congrats! You've created your first Chrome extension!

2.0 Examples

Now that we know how to create the simplest Chrome extension, we can start thinking about other things we're going to be doing. We'll provide three examples of (funny) applications that all deal with DOM manipulation, which is manipulating elements that already exist on the page. **There are other types of Chrome extensions then the content_scripts examples we provide in these examples; we'll discuss those in section 2.4.

2.1 Text Replacement

You've heard of Cloud-to-Butt. Let's make our own! Maybe I want to change computer to robot, or person to cantaloupe. The possibilities are endless!

manifest.json

{
  "manifest_version": 2,
  "name": "Text Replacement",
  "version": "1.0",
  "description": "Sample application that will replace text on webpages.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ]
}

script.js

var ELEMENT = 1;
var DOCUMENT = 9;
var DOCUMENT_FRAGMENT = 11;
var TEXT = 3;

// Enter things that you'd like to replace
var MATCH = ['computer','person'];
var REPLACE = ['robot','cantaloupe'];

walk(document.body);

function walk(node) {
    // Function from here for replacing text: http://is.gd/mwZp7E
    
    var child, next;

    switch (node.nodeType) {
        case ELEMENT:  // Element
        case DOCUMENT:  // Document
        case DOCUMENT_FRAGMENT: // Document fragment
            child = node.firstChild;
            while (child) {
                next = child.nextSibling;
                walk(child);
                child = next;
            }
            break;

        case TEXT: // Text node
            replaceText(node);
            break;
    }
}

function replaceText(textNode) {
    var v = textNode.nodeValue;

    // Go through and match/replace all the strings we've given it, using RegExp.
    for (var i = 0; i < MATCH.length; i++) {
        v = v.replace(new RegExp('\\b' + MATCH[i] + '\\b', 'g'), REPLACE[i]);
    }

    textNode.nodeValue = v;
}

All you need to do to have all the text-replace goodness you want is change the arrays at the top of the script.

You may notice that our text only replaces the lowercase version of 'computer.' If you try going here, you'll see that mentions within sentences are changed, but not the title. To change this, you have a few options. The first, which is likely the expected behavior, would be to simply add more pairs to MATCH and REPLACE i.e. add 'Computer' to MATCH and Robot to REPLACE. This way we can match the capitalized versions. Alternatively, you could swap the 'g' flag in v.replace to be an i flag, which stands for case insensitive. Thus, your new line would look as follows:

v = v.replace(new RegExp('\\b' + MATCH[i] + '\\b', 'g'), REPLACE[i]);

However, this would replace capitalized words with lowercase words, which is not always what we want.

2.2 Image Replacement

There's not enough Nicholas Cage on the Internet. Let's do that by changing all the images on the Internet to a random picture of Nicholas Cage!

manifest.json

{
  "manifest_version": 2,
  "name": "Cage Match",
  "version": "1.0",
  "description": "Replace all the images on a webpage with pictures of Nicholas Cage.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ]
}

script.js

// Links to pictures of Nicholas Cage
var CAGE_URLS = ['http://upload.wikimedia.org/wikipedia/commons/3/33/Nicolas_Cage_2011_CC.jpg',
                 'http://cdn.financebuzz.io/images/2016/02/03/nicholascage1.jpg',
                 'http://pmcdeadline2.files.wordpress.com/2010/08/nicolas_cage.jpg',
                 'http://upload.wikimedia.org/wikipedia/commons/f/f3/Nicolas_Cage_-_66%C3%A8me_Festival_de_Venise_(Mostra).jpg',
                 'http://cdn-static.denofgeek.com/sites/denofgeek/files/nicolas-cage-nicolas-cage-26969958-1974-1300.jpg'
                ];

// Pick out a random image from our collection.
function getRandomImage() {
    return CAGE_URLS[Math.floor(Math.random() * CAGE_URLS.length)];
}

// Get all the images on a page.
var images = document.getElementsByTagName("img");

// Replace each image with a random one.
for (var i = 0; i < images.length; i++) {
    var image = images[i];
  image.src = getRandomImage();
  console.log(image);
}

Replace the links in our CAGE_URLS array with links to any pictures you'd like, and watch your browsing experience transform!

2.3 Link Replacement

Rick-Rolling is hilarious. Let's make a certain percentage of the links on every page redirect to 'Never Gonna Give You Up'.

manifest.json

{
  "manifest_version": 2,
  "name": "Rick Roll",
  "version": "1.0",
  "description": "Application that replaces all links with a link to RickRoll.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ]
}

script.js

// Link and a percentage likelihood to replace your link.
var LINK = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ';
var RATIO = 0.5;

// Get all the links on the page.
var links = document.getElementsByTagName("a");

// Replace RATIO of them with Rick Astley.
for (var i = 0; i < links.length; i++) {
    if (Math.random() < RATIO) {
    links[i].href = LINK;
    }
}

Click with care people!

2.4 Other Things

This is just one simple manifest.json file. There are lots of other attributes you can set, such as background, which allows us to run a single script in the background, permissions, which allow us to ask the user for certain things we may not be able to do withour permission, and browser_action, which creates small icons next to the search bar that you can click and have something happen. However, content_scripts is what we'll mainly be looking in this tutorial.

3.0 More Advanced Topics

Chrome extensions provide all sorts of functionality beyond the functionality described above. You can explore more advanced topics on the Chrome Developer Website. However, several of these features will be important to us as we move forward, so we will go through them here.

3.1 Background Pages

So far, we've been working with content scripts, which are linked to individual pages. They can be loaded and run at different times in the page's life; for example, when the page first loads, or when it's about to be presented. However, it will useful for certain applications to have a single script that runs across the entire lifetime of an extension; rather than having the script run once for every page, we can just have one script that runs when the extension is first loaded.

As a result, Chrome extensions provide us with something called background pages. A background page is essentially a single dummy page that runs in the extension process that exists for the entire lifetime of the process. In order to utilize the background page, we must first make a change to our manifest.json file:

{
  "name" : "My extension",
  ...
  "background": {
    "scripts" : ["background.js"]
  },
  ...
}

By adding this, we'll have a new background page created by the extension that is associated with each of our scripts. To try this out, create a new file called background.js and add a single line:

console.log("I am in the background.");

Reload your page, and check your console for the print message. You won't see the printed statement. So what gives? Well, as we saw before, the background page is not linked to any specific page, which explains why we're not seeing it in the individual tab's console. Instead, it'll be linked to the extension's background page.

To view the background page, go to chrome://extensions. Once you reload your extension, there should be a new part of your chrome extension that says "Inspect views: background page." Click 'background page', and a new console should show up that shows our "I am in the background" message! Thus, we see that the background page is linked to the application and not the tab.

3.2 Browser Actions

You may be used to using Chrome extensions that have icons next to your search bar. We can customize these images, add tooltips, and respond to clicks to the button by user browser actions.

Just as before, to add a browser action, we simply add a new JSON object to our manifest.json:

{
  "name" : "My extension",
  ...
  "browser_action": {
    "default_icon": "icon.png",
    "default_title": "My extension",
    "default_popup": "popup.html"
  },
  ...
}

By adding this, we can customize the icon that gets added to the Chrome toolbar for our extension. default_icon allows us to set a custom icon; to do this, simple add an icon to your extension directory and refer to it by name here. default_title allows us to change the tooltip that we see when we hover over the icon. default_popup allows us to define an HTML page that will show when we click the icon; this is for applications where you might want to allow the user to fill out some form when they click on your icon.

We can also, somewhat more interestingly, add listeners for clicks to our icon. If we want to set a listener for a click to our icon, we can use the following code segment:

// Called when the user clicks on the browser action icon.
chrome.browserAction.onClicked.addListener(function(tab) {
  ...
});

As we will see in a moment, this function will be called from our background script. The parameter, tab, refers to the tab that was open when the button was pressed; thus, this is a way for us to be able to interact with the contents of a page from the background of our application.

3.3 Message Passing

Imagine you want to do something to the page you're currently viewing when you click on the extension's icon. This is a very common task; later, we'll change all the images on the page when we click the icon. However, before when we were discussing background pages, we learned that we have only one page running for the entire extension. Similarly, we only have one icon for the entire application; we don't get a new icon for each new tab. Thus, this means that our icon is handled by our background.js script, while our current page is handled by our script.js script, our content script. So, if we want to be able to change the current page in response to a click of the icon, we're going to need some way for these two scripts to interact.

For this, we will use a technology called message passing. Message passing essentially allows us to send messages from the background of our script to the content (each browser page) of our browser. To send a message from the content to the background, we simply call this:

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response);
})

Thus, from any one of our pages, we can send data from the client to the background. We then get a callback function that runs when the message has been successfully returned. Similarly, we can send a message from the background to any one of our pages by using a different API:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  console.log(response);
});

However, as we see here, we have to provide a tab ID. This is because, as we saw before, there is one background page but many content pages; so, in order for the background to send to a content page, it needs to know which of the many content pages it needs to send the message to. As we'll see later, there are ways of finding the tab ID for the tab that you're interested in.

4.0 Scottify

My favorite television show ever is "The Office." In it, Steve Carell stars as Michael Scott, my favorite television character of all time. So, whenever I see him in other movies or shows, I always think of him as Michael Scott, no matter what role he's playing. So, I want to build an extension to change his real name (Steve Carell) to his character's name (Michael Scott). Plus, we'll add one or two new features as well.

4.1 Text Replacement

First, let's start with the most basic application, one that replaces the words on the page with "Michael Scott" instead of "Steve Carell." To do this, let's first create our basic manifest.json file in a new directory called scottify:

{
  "manifest_version": 2,
  "name": "Scottify",
  "version": "1.0",
  "description": "An extension that gives Michael Scott a more prominent place on the Internet.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ]
}

Now that we have this, copy script.js from our sample text-replacement code we saw earlier. Inside of it, please update MATCH and REPLACE to be 'Steve Carell' and 'Michael Scott'. When you're finished, your code should look as follows:

var ELEMENT = 1;
var DOCUMENT = 9;
var DOCUMENT_FRAGMENT = 11;
var TEXT = 3;

// Enter things that you'd like to replace
var MATCH = ['Steve Carell'];
var REPLACE = ['Michael Scott'];

walk(document.body);

function walk(node) {
    // Function from here for replacing text: http://is.gd/mwZp7E
    
    var child, next;

    switch (node.nodeType) {
        case ELEMENT:  // Element
        case DOCUMENT:  // Document
        case DOCUMENT_FRAGMENT: // Document fragment
            child = node.firstChild;
            while (child) {
                next = child.nextSibling;
                walk(child);
                child = next;
            }
            break;

        case TEXT: // Text node
            replaceText(node);
            break;
    }
}

function replaceText(textNode) {
    var v = textNode.nodeValue;

    // Go through and match/replace all the strings we've given it, using RegExp.
    for (var i = 0; i < MATCH.length; i++) {
        v = v.replace(new RegExp('\\b' + MATCH[i] + '\\b', 'g'), REPLACE[i]);
    }

    textNode.nodeValue = v;
}

Load your new extension by pressing "Load unpacked extension" at chrome://extensions and selecting your scottify directory. Go to the Wikipedia page for Steve Carell located here, and you should see the names have changed. Woo!

4.2 Image Replacement

I've decided that my internet doesn't have enough Michael Scott on it. So, I want my extension to be able to replace all the images on a page with images of Michael Scott. However, I don't want this for all pages, only for the ones that I want. I want to be able to press my icon and have all the images change to Michael Scott. To do this, let's use our more advanced techniques: background pages, browser actions, and message passing.

First, let's set the icon of our extension. Download this image and add it to your scottify directory. Make sure it is named scott.png. Add this to your manifest.json:

"browser_action": {
  "default_icon": "scott.png"
}

Now, if you reload your extension, you should the picture of The Donald as your extension icon.

Now, let's add our background page. First, add this to your manifest.json file:

"permissions" : ["tabs", "*://*/*"],
"background" : {
  "scripts" : ["background.js"],
  "persistent" : false
}

First, we request permissions, which we'll use for our background page. First, we'll need access to the tabs in your browser, which we'll use to pass messages between the background and the content. Next, we specify the script, which will be our background script; we choose to name it background.js. Also, importantly, we set persistent equal to false, to specify that our background page is an event page that only needs to be loaded in response to certain events; this is a little confusing, but more information is available here.

From here, our manifest.json file is complete, and should look as follows:

{
  "manifest_version": 2,
  "name": "Scottify",
  "version": "1.0",
  "description": "An extension that gives Michael Scott a more prominent place on the Internet.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ],
  "permissions" : ["tabs", "*://*/*"],
  "background" : {
    "scripts" : ["background.js"],
    "persistent" : false
  },
  "browser_action": {
    "default_icon": "scott.png"
  }
}

(Note: order is not important.)

Now, let's create our new background.js file; add the following code there:

// Called when the user clicks on the browser action icon.
chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.sendMessage(tab.id, {}, function(response) {
    console.log("Your page has been scott-ified!");
  });
});

We see the only thing we do is add a listener to the click of our icon. Once we receive this click, we send a message to our tab ID. Once we hear back from our tab, we can confidently print that the page has been scott-ified.

In script.js, our content script, we need to add the functionality to both change the images to images of Michael Scott and the ability to actually receive the messages our background script is sending. Add this code to the bottom of your script.js file:

// Replace all images with images of Michael Scott. 
SCOTT_PICS = [
  'https://s-media-cache-ak0.pinimg.com/originals/4c/ac/d0/4cacd03e19d8b8ab7b939462176f8355.png',
  'http://www.businessnewsdaily.com/images/i/000/008/678/original/michael-scott-the-office.PNG?1432126986',
  'http://cdn1.theodysseyonline.com/files/2015/10/30/6358177813696988401291296824_michael-scott-the-office-9.jpg',
  'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSwrkfrrT2PCfRAheKr0BICorUaxfZHNQc6NGwhK4bO_8qf9g6p',
  'https://susanecolasanti.files.wordpress.com/2007/09/michaelscott2.jpg'
]

function scottify() {
  // Get all the images on a page.
  var images = document.getElementsByTagName("img");

  // Replace each image with a random one.
  for (var i = 0; i < images.length; i++) {
    var image = images[i];
    image.src = SCOTT_PICS[Math.floor(Math.random() * SCOTT_PICS.length)];
  }
}

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  scottify();
});

The code at the top is very similar to the code from our cage-match extension provided in the sample code and shown above. We create a list of links, each one of which is a picture of Michael Scott. Then, we create a function called scottify, which takes each image on the current page, and makes it an image of Michael Scott from one of the five that we have provided.

The interesting bit is the part at the bottom. Essentially, we add a listener to the Chrome runtime that listens for any messages that are sent to the tab. As soon as we get a message, we know it is being sent from our background page to tell us that our icon has been pressed, so at that point, we can change the images on our page.

Reload your extension. Then, go to Google Images and search for something, like "pizza" or "puppies." Then, click our extension logo and watch all your pictures change!

4.3 Advanced JavaScript: DOM Mutations

Our extension is looking pretty good, but there's one problem. Try doing a Google search for "Steve Carell" and look at the search results for the first page. You should see that all of our "Steve Carell"s have been changed to "Michael Scott"s. However, try clicking to the next page; you should still see "Steve Carell."

This is because of a problem in the way that content scripts work. Remember in our manifest.json file where we specified run_at: document_end? This basically says that as soon as our page is loaded for the first time, run our script, which does the text replacement. However, many pages load things dynamically, which update the current document without having to load a new page. Facebook does this so you don't have to reload the page to scroll down on your newsfeed, and Google does this with its search results. So, how can we change the text in this dynamically loaded content?

We can do this with an advanced JavaScript technique called mutation observing. Essentially, we can create an observer that listens for certain events that occur within our DOM; this could be an event like the addition of a new link, the deletion of an image, or anything like that. In this case, we want to listen for the insertion of new elements; these new elements that are inserted may have "Steve Carell"s in them, which we definitely need to change.

To do this, add the following code to your script.js:

// Create a MutationObserver to handle events
// (e.g. filtering TextNode elements)
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        if (mutation.addedNodes) {
            [].slice.call(mutation.addedNodes).forEach(function(node) {
              walk(node);
            });
        }
    });
});

// Start observing "childList" events in document and its descendants
observer.observe(document, {
    childList: true,
    subtree:   true
});

First, we create a MutationObserver object. We go through each of our mutations, looking at any nodes that may have been added in that mutation. Then, we step through each of those added nodes, and call walk on them, the function that we use to do our text replacement. This way, we make sure to do text replacement on every new node that is added. Then, we call the observer to observe, passing in our entire document and instructing it to look at both the childList of our document as well as the subtree (essentially any sub-elements of the document). This way, whenever any new elements are added to our page, we can Scottify them without having to reload the page.

Reload your extension and try the Google search results again. No matter how many pages you go through, you should see your Scott results.

Our final code should look as follows:

manifest.json

{
  "manifest_version": 2,
  "name": "Scottify",
  "version": "1.0",
  "description": "An extension that replaces Steve Carell with Michael Scott around the Internet.",
  "content_scripts": 
  [
    {
      "matches": ["*://*/*"],
      "js": ["script.js"],
      "run_at": "document_end"
    }
  ],
  "permissions" : ["tabs", "*://*/*"],
  "background" : {
    "scripts" : ["background.js"],
    "persistent" : false
  },
  "browser_action": {
    "default_icon": "scott.png"
  }
}

script.js

var ELEMENT = 1;
var DOCUMENT = 9;
var DOCUMENT_FRAGMENT = 11;
var TEXT = 3;

// Enter things that you'd like to replace
var MATCH = ['Steve Carell'];
var REPLACE = ['Michael Scott'];

walk(document.body);

function walk(node) {
    // Function from here for replacing text: http://is.gd/mwZp7E

    var child, next;

    switch (node.nodeType) {
        case ELEMENT:  // Element
        case DOCUMENT:  // Document
        case DOCUMENT_FRAGMENT: // Document fragment
            child = node.firstChild;
            while (child) {
                next = child.nextSibling;
                walk(child);
                child = next;
            }
            break;

        case TEXT: // Text node
            replaceText(node);
            break;
    }
}

function replaceText(textNode) {
    var v = textNode.nodeValue;

    // Go through and match/replace all the strings we've given it, using RegExp.
    for (var i = 0; i < MATCH.length; i++) {
        v = v.replace(new RegExp('\\b' + MATCH[i] + '\\b', 'g'), REPLACE[i]);
    }

    textNode.nodeValue = v;
}

// Create a MutationObserver to handle events
// (e.g. filtering TextNode elements)
var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        if (mutation.addedNodes) {
            [].slice.call(mutation.addedNodes).forEach(function(node) {
              walk(node);
            });
        }
    });
});

// Start observing "childList" events in document and its descendants
observer.observe(document, {
    childList: true,
    subtree:   true
});

// Replace all images with images of Michael Scott. 
SCOTT_PICS = [
  'https://s-media-cache-ak0.pinimg.com/originals/4c/ac/d0/4cacd03e19d8b8ab7b939462176f8355.png',
  'http://www.businessnewsdaily.com/images/i/000/008/678/original/michael-scott-the-office.PNG?1432126986',
  'http://cdn1.theodysseyonline.com/files/2015/10/30/6358177813696988401291296824_michael-scott-the-office-9.jpg',
  'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSwrkfrrT2PCfRAheKr0BICorUaxfZHNQc6NGwhK4bO_8qf9g6p',
  'https://susanecolasanti.files.wordpress.com/2007/09/michaelscott2.jpg'
]

function scottify() {

  // Get all the images on a page.
  var images = document.getElementsByTagName("img");

  // Replace each image with a random one.
  for (var i = 0; i < images.length; i++) {
    var image = images[i];
    image.src = SCOTT_PICS[Math.floor(Math.random() * SCOTT_PICS.length)];
  }
}

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  scottify();
  walk(document.body);
});

background.js

// Called when the user clicks on the browser action icon.
chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.tabs.sendMessage(tab.id, {}, function(response) {
    console.log("Your page has been scottified!");
  });
});

Additional Resources

If you want to learn more about how to build awesome Chrome extensions, check out these resources:

About

A talk that teaches people to make useless Chrome plugins, with some examples.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published