I built a clone of a popular Gmail extension for Firefox. Here is how!
Tags: webdev, javascript, firefox
Published: 2019-08-22 22:55:06

---
title: I built a clone of a popular Gmail extension for Firefox. Here is how!
published: true
description: A step-by-step guide on how I built my first browser extension
tags: #webdev #javascript #opensource #firefox
---

One of the most frustrating things when switching browsers must be the unavailability of some of your favorite add-ons. I recently tried to fully switch from Chrome to Firefox but had to stop mid-way due to the absence of an extension I use everyday: *Rapportive* (now actually called Linkedin Sales Navigator).

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/fbi9wsnv8899x509nijh.png)

Rapportive is a Chrome-only tool for gmail displaying in a sidebar the Linkedin profile of your email correspondents. You gain access to their profile picture, name, title, company and connection level without having to leave your inbox. When you exchange with dozens of new people every week, this add-on quickly becomes vital.

Reason why I decided a few months ago to develop **RAPPORTEUR**, the Firefox version of Rapportive and simultaneously develop my first *real* browser extension.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/fdm9ti9sdy4anlkw7et9.png)



## 1 - Get the information from Linkedin
The first step was to make sure I could retrieve the needed data from Linkedin solely using an email address. A quick glance at the Linkedin documentation indicated that the API was fairly limited and wasn’t allowing access to profiles based on addresses.

After looking closer at the requests made by Rapportive in Chrome, I noticed that calling the following URL was giving me back the exact Linkedin object I desired, as an HTML string.

https://www.linkedin.com/sales/gmail/profile/viewByEmail/your_email_address

 Try it in your browser! (You’ll need to be connected to Linkedin)



## 2 - Learn about coding add-ons 
The second step was to actually figure out how to code and structure a browser add-on. The process is fairly straight forward, mainly requiring to declare two things:
*  `manifest.json`  containing all the information needed for the add-on to work and be displayed in Firefox
* `your_code.js` containing the actual logic of your application

You can then test your extension locally by loading it from Firefox’s preferences: `about:debugging#addons` -> `Load Temporary Add-On`. 

For more details and get started with a very basic example, check [Your first extension | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension)



## 3 - Inject the data into Gmail

### Timing
Now that I had a shell for our add-on and access to nicely formatted data (thanks Linkedin), I simply needed to inject it into our gmail layout. Well … this wasn’t that simple. 

Gmail’s DOM is like an onion coming from hell, proudly exhibiting an infinite number of layers. Nested divs and spans with crazy class names, all loading with different timings. 

After finding the div able to host our sidebar, the main challenge was to find the good timing to inject our content. Again, every element of the gmail interface loads at different time and my script was first running too early, before the div being actually created. Listening to `DOMContentLoaded` and other page lifecycle events couldn’t do the trick.

You can learn more about these events [here](https://javascript.info/onload-ondomcontentloaded).

As a backup plan, I chose to periodically listen to the presence of the desired div, via a `setInterval`. It worked!

```js
const windowLoaded = setInterval(function() {
    if (document.querySelector(".y3") !== null) {
      clearInterval(windowLoaded);
      // RUN YOUR CODE
  }, 100);
```



### Gmail APIs

After extracting the correspondents’ email addresses from the email object and running the fetch request, the Linkedin window was successfully showing in the sidebar. Yeah!

Next step was to make sure the fetch request would trigger again when opening a new email in a new page. For this purpose, I needed a way to know if I was in the inbox view, the thread view or the email view. That’s at this stage I discovered two third-party APIs helping to easily manipulate and listen to the gmail interface.

The first one was [**InboxSDK**](https://www.inboxsdk.com/)
Setup was easy and I quickly got running through a few tests, until realizing the API was only working in Chrome and Safari. Next!

The second one was [**Gmail.js**](https://github.com/KartikTalwar/gmail.js)
I found the setup slightly more complex (node packages and build tool needed) and the documentation not as clear as InboxSDK. The library works on Firefox though and was offering methods I needed. But only a few of them actually worked in my case (I even had to change the source code in the package file at some point).   Too unstable for my taste, using the API also created CORS issues. Back to square one!

Going vanilla javascript and inspired by a [similar experience](https://medium.com/square-corner-blog/sqrome-a-chrome-extension-e2e90c1c5d2), I decided to simply listen to url changes and react accordingly. Boom!

```js
let currentUrl = document.location.href;
setInterval(function() {
  if (document.location.href != currentUrl) {
    currentUrl = document.location.href;
    // RUN YOUR CODE
  }
}, 500);
```


## 4 - Publish to Mozilla’s add-ons library
A few hours of coding, tweaking and experimenting later, Rapporteur was finally ready. To make an add-on easily available to users, you simply need to publish and list it on [Mozilla’s add-ons library](https://addons.mozilla.org).

This page goes through all the steps required: [Submitting an add-on | MDN](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Distribution/Submitting_an_add-on).

You can now install *Rapporteur* for Firefox here ([Rapporteur – Get this Extension for 🦊](https://addons.mozilla.org/en-US/firefox/addon/rapporteur/)) or see the source code here: [GitHub - paulgaumer/rapporteur](https://github.com/paulgaumer/rapporteur).

Thanks a lot for reading and feel free to let me know your thoughts!