Home | Schedule & Readings | Links & Resources

Radical Software

LCST 2234, Fall 2021 (CRN 9430)
Rory Solomon

Project 2, Tutorial 3: Options

Plan for today:

  1. Providing an extension icon
  2. Adding extension options
Today we'll be seeing how you can give the user some options in how your browser extension operates, a kind of control panel. Depicted here is probably one of the most iconic control panels of all time, Lieutenant Uhura's (Nichelle Nichols) workstation from the original Star Trek series.

I. Providing an extension icon

Let's first see how to add a custom icon for your extension. This is pretty easy:

{
  "manifest_version": 2,
  "name": "Rory's First Extension",
  "description": "This is my first attempt at a browser extension!",
  "version": "0.1",
  "content_scripts": [
    {
      "matches": [
        "*://*/*",
        "file://*"
      ],
      "js": [
        "content.js"
      ],
      "run_at": "document_idle"
    }
  ],
  "icons": {
    "128": "images/icon.png"
  }
}

Note the comma that I have to add after the square bracket that ends the content_scripts list. Probably on line 17 for you. Let's see what happens if you forget to add that — what is the error message?

Now you would need to put a file named icon.jpg inside a folder (directory) called images in your project-2 folder. You can name the file whatever you'd like, but just make sure that the filename is the same as what you specify in manifest.json.

Here I have specified to use that is meant to be 128 pixels wide, and 128 pixels tall. So the .png file I'm using should have those dimensions.

The Google Chrome documentation on extension icons states that "You should always provide a 128x128 icon; it's used during installation and by the Chrome Web Store. Extensions should also provide a 48x48 icon, which is used in the extensions management page (chrome://extensions)." That documentation states that "you can also specify a 16x16 icon to be used as the favicon for an extension's pages."

You don't really need to provide all of these. If you only provide one, the browser will try to automatically re-size, which may or may not look up to your standards. If you want to be thorough, provide these multiple sizes.

II. Adding extension options

Options give some control & configurability to whoever is using your extension — whoever has installed it into their browser and is using it. The way you add these options is my creating a simple HTML page with a web form that has some input fields, with this the user can specify some values and press a submit button, when that happens your code will store those values, and then elsewhere in your extension you will access those values from the extension storage and use them in your code in some way.

As an example, let's build on what we've done so far. Last week we ended with an example that replaces all instances of one work or phrase with another. My example replaced the with WHAT. This week, let's see how we could let the user specify a word or phrase to replace, and a word or phrase to replace it with.

(a) The options user interface

To get started with options, we first have to specify the HTML file that will be displayed when the user clicks "Options" for your extension in the extensions menu. You specify this by adding to manifest.json:

	
  ],
  "options_page": "options.html",
  "icons": {

Note that this snippet is in between content_scripts and icons, probably on line 18 for you. The order of fields in manifest.json is actually not super important, but you'll probably want to keep yours the same or similar to my examples to make sure you have the syntax correct.

Now let's create that file! Make a new file in Atom, save it, and make sure you name it options.html and save it into your project-2 folder. Let's add this code to the file:

<!DOCTYPE html>
<html>
  <body>
    <div>
      From text: <input id="fromField" type="text" />
      To text: <input id="toField" type="text" />
      <button type="button" id="button">Set text</button>
    </div>
  </body>
</html>

The input tags are inputs into which the user can specify values. If you were really using this in your project, you should probably specify some additional helper text to the explain to the user what is going on. You could do that with some plain text included in a <p> <p> tag above or below the <input> fields.

From text: is what will be displayed to the user, and fromField is the name we will use for this field elsewhere in our code.

(b) Saving option values

Now we need to create the Javascript code that will handle saving these values when the user clicks Set text.

<html>
  <script src="options.js"></script>
  <body>

Now we have to implement that. Create a new file in Atom, save it as options.js and add in this code:

let fromTextElement = document.getElementById('fromField');
let toTextElement = document.getElementById('toField');
let setButtonElement = document.getElementById('button');

setButtonElement.addEventListener('click', function() {
  chrome.storage.sync.set({
    fromStored: fromTextElement.value,
    toStored: toTextElement.value});
  });

The three ...Element variables are the various HTML elements in the DOM (the document) that we want to access from our Javascript code.

The next chunk of code listens for when the setButtonElement receives a click event. When that happens, we get the value of each field (e.g., fromTextElement.value), put that into a dictionary. A dictionary is a group of key-value pairs specified in Javascript and many programming languages by curly braces { }

So what the above code does is save (or store) the values that the user specifies into this thing called chrome.storage, when the user clicks the Set text button.

(c) Using option values

Now we have to use those values somewhere in our code. We will do this in our content script. Let's make a new one for this tutorial.

Create another new file, let's call it tutorial3-content.js, and save it to your project-2 folder. (Remember to make sure to be careful and precise about capitalization, punctuation, and spaces!)

Modify manifest.json to use this new content script:

  "content_scripts": [
    {
      "matches": [
        "*://*/*",
        "file://*"
      ],
      "js": [
        "tutorial3-content.js"
      ],
      "run_at": "document_end"
    }
  ],

Now modify tutorial3-content.js to look like this:

// Technique 3
chrome.storage.sync.get({fromStored: "", toStored: ""}, function(result) {
  var html = document.querySelector('html');
  var walker = document.createTreeWalker(html, NodeFilter.SHOW_TEXT);
  var node;
  while (node = walker.nextNode()) {
    node.nodeValue = node.nodeValue.replace(/the /gi, 'WHAT')
  }
}

That is using the same code from last week, but places inside this new block that get()s these values from the chrome.storage so that we can use them. But we're not using them yet. Make this change:

  while (node = walker.nextNode()) {
    var re = new RegExp(result.fromStored,"gi")
    node.nodeValue = node.nodeValue.replace(re, result.toStored);
  }

Other things ...

We should show the user what the previously saved values are. In options.js:

chrome.storage.sync.get({fromStored: "", toStored: ""}, function(result) {
  fromText.value = result.fromStored;
  toText.value = result.toStored;
});
    

And we can add a clear button to clear the values. In options.html:

      <button type="button" id="clear">Clear text</button>
    

And handle that:

let clearButton = document.getElementById('clear');

// ...
      
clearButton.addEventListener('click', function() {
  chrome.storage.sync.clear();
  fromText.value = "";
  toText.value = "";
});
    
Last Updated: Thu Oct 28 2021 16:05:47 GMT+0000 (Coordinated Universal Time)