iheartjesselondon – Walking through a very basic Google Chrome extension (Part 1 of ? (at least 2))

Posted on December 19th, 2010

Quick background – this guy Jesse and I send each other Web links 5-20 times a day. I decided I wanted to streamline this process as much as possible (one click, no typing). Since we both use Chrome the majority of the time, writing an extension was a no-brainer. Much more importantly, it gave me an excuse to work on a short project in a language I am familiar with, but on a platform new to me. The best thing about all of this, it’s very easy*! Part one of this post is a walk-through of code basics.

BASIC FILES THAT MAKE IT WORK:

Below are the files that are included in my extension directory, along with my icon, which I define in manifest.json (icon.png).

manifest.json: This file uses json to define files used, permissions, title, and more (detailed list of what can be included found here). Here is my manifest.json:

code:
{
  "name": "I Heart Jesse London",
  "version": "1.0",
  "options_page": "options.html",
  "description": "This extension emails jesse london, or whoever, a link to the current website",
   "browser_action": {
     "default_icon": "icon.png",
     "default_title": "send some internet lovin",
     "default_popup": "popup.html"
  },
  "permissions": [
    "http://domainnameforemailwebservice.com/",
    "tabs"
  ]
}

As you can see, it’s very self-explanatory. I give it a name, version, define my icon, popup and options files and set some permissions that allow me to access information about tabs and also to use the web service, hosted elsewhere, for sending my email**.

options.html: It’s easy to collect and store information in the Chrome extension. In my manifest.json file, you can see that I named options.html as my options_page. The options page is what it sounds like, it’s the page that opens up in a new tab when the user goes to set extension options. My file is just a very basic html form with some javascript to check and set some local variables. This is the lovely thing about Chrome extensions; they are basically web pages so you really just need to know html and javascript.

Here’s the html used:

<body>

Enter in your favorite persons email address, your own, and the magic key ;)
TO: <input id="email_address" value="jesselondon@gmail.com" name="email_address" /><br />
From: <input id="from_address" value="jennifer.stander@gmail.com" name="from_address" /><br />
Key: <input id="key_string" value="input key here" name="key_string" />

<br>
<button onclick="save_options()">Save</button>
<br>
<div id="status"></div>
</body>

Easy cheesy, right? On page load it runs a function to restore the options with values previously stored, there’s a form with 3 fields and some defaults, a <div> that displays confirmation on save, and on form submit the function save_options() is called.

Here’s are the two functions, with comments!:

<script type="text/javascript">

// Saves options to localStorage.
function save_options() {
  var email_address = document.getElementById("email_address").value;
  localStorage["email_address"] = document.getElementById("email_address").value;

  var from_address = document.getElementById("from_address").value;
  localStorage["from_address"] = from_address;

  var key_string = document.getElementById("key_string").value;
  localStorage["key_string"] = key_string;

  // Update status to let user know options were saved - briefly :)

  var status = document.getElementById("status");
  status.innerHTML = "Options Saved: <br>To Email: " + email_address + "<br>From Email: " + from_address;
  setTimeout(function() {
    status.innerHTML = "";
  }, 750);
}

// autofills form fields with previous entered information if it exists
window.onload = function() {
    //first check to see if there are already email addresses stored, exit if not (will display defaults then)
  if(!localStorage["email_address"] && !localStorage["from_address"]){
    return;
  }
  document.getElementById("email_address").value = localStorage["email_address"];
  document.getElementById("from_address").value = localStorage["from_address"];
  document.getElementById("key_string").value = localStorage["key_string"];
}

</script>

popup.html: the default_popup in manifest.js is exactly that, a popup window that appears when the icon is clicked. I had added the popup at Jesse’s suggestion so that he knows that he for sure clicked the icon and will be adding the service response in the future to verify that it indeed went through. I originally had all of my functionality in a background page, but for some reason I couldn’t get it to work with my popup*, so I just threw all of the code in popup.html. I’ll revisit this when I’m ready for some frustration.

On the popup page load I call the function that does all of the work and pass it information from the selected tab:

window.onload = function()
{
	// grab the selected tab title and url and send to my email function
	chrome.tabs.getSelected(null, function(tab){
		send_email(tab.title, tab.url);
	});
}

The main functionality of my little extension is all in send_email. Here is all the information that you need for using cross-origin xmlhttprequest within a chrome extension. I pretty much copied and pasted from it, even included the malicious script warning in the comments :) My function takes the title and url and sends it to a webservice – the main thing to note here is that you will need to include the domain in your permissions in manifest.json. Here it is:

// uses my own dumb webservice, returns true or false
 function send_email(title, url){
    var jens_url = "http://domainnameforemailwebservice.com";
    var xhr = new XMLHttpRequest();
    var fullstring = "";

    // need to encode the following still on this end
    // would like to alter this to use gmail if it's open
    // until then it's just for jesse and I, I guess

    var emailaddress = localStorage["email_address"];
    var fromaddress = localStorage["from_address"];
    var key_string = localStorage["key_string"];

    var fullstring= jens_url + "?id=" + key_string + "&email_address=" + mailaddress + "&page_title=" + title + "&page_link=" + url + "&from_address=" + fromaddress;
    console.log(fullstring);

    //send and receive
    xhr.open("GET", fullstring, true);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        // WARNING! Might be injecting a malicious script! This should not be complete yet
        console.log(xhr.responseText);
      }
    }
    xhr.send();
    return xhr.responseText;
  }

And there you go, that’s what I have so far, and it’s working! All code that I’ve discussed is available publicly on github. Next up in Part 2 I’ll cover installation, testing and distribution.

That’s it for now!
Jen

* The one thing that wasn’t easy and I haven’t really figured out is how to use a background.js file and popup together. Originally, I wasn’t using a popup so had a listener programmed into a background script, along with all of my other functions. I preferred this separation of logic and display, but once I added a popup into manifest.json, the listener no longer worked. I hope to revisit this in the future. If anyone has suggestions, please let me know! (I will admit that not a lot of time was spent trying to resolve this though.)

** It would be nice to use the user’s gmail account instead of a hosted web service. I need to do more research to see how easy and safe this is though. Any discussion on integrating gmail functionality into a custom extension is welcome – please comment. I’ve seen examples that open up the gmail tab, but I really want everything to happen in the background.

*** I am aware of a few bugs and will fix them before Part 2.

**** At some point I should really have this usable for someone besides myself and jesse

Click here to view and leave comments!