Using Google Analytics in Extensions

As an add-on developer, you may want to have usage reporting integrated into your add-on. This allows you to understand how your users are using the add-on in real life, which can often lead to important insights and code updates that improve user experience.

The most popular way to do this is to inject the Google Analytics script into your codebase as if it were a web page. However, this is incompatible with our review policies. Injecting remote scripts into privileged code – or even content – is very dangerous and it’s not allowed. Fortunately, there are ways to send reports to Google Analytics without resorting to remote script injection, and they are very easy to implement.

Update: add-ons that use GA are required to have a privacy policy on AMO, and the data they send should be only what’s strictly necessary for usage reporting. This blog post is meant to show the safer ways of using GA, not advocate its unrestricted use.

I created a branch of one of my add-ons to serve as a demo. The add-on is a WebExtension that injects a content script into some AMO pages to add links that are useful to admins and reviewers. The diff for the branch shouldn’t take much time to read and understand. It mostly comes down to this XHR:

let request = new XMLHttpRequest();
let message =
  "v=1&tid=" + GA_TRACKING_ID + "&cid= " + GA_CLIENT_ID + "&aip=1" +
  "&ds=add-on&t=event&ec=AAA&ea=" + aType;"POST", "", true);

In my demo I do everything from the content script code. For add-ons with a more complex structure, you should set up a background script that does the reporting and have content scripts send messages to it if needed.

I set up my reporting so the hits are sent as events. You can read about reporting types and all the different parameters in the Google Analytics developer docs.

Thanks to the real-time tracking feature I could see that my implementation was working right away:


That’s it! With just a few lines of code you can set up Google Analytics for your add-on safely, in a way that meets our review guidelines.

26 responses

  1. ael wrote on :

    As a user, I may wish to block this kind of thing. Is that possible?

    To put it another way: Do Mozilla review policies insist that in its description, such an addon mentions that it automatically submits usage data? Do they ensure such addons have a privacy policy?

    1. Jorge Villalobos wrote on :

      Add-ons that submit user info to third parties need to have a privacy policy that states it (and we are restrictive about it being anonymized). As for blocking this kind of thing, it is possible for extensions to do that, but most content-blocking extensions only block what happens in content, and don’t block what other extensions do.

  2. Stephan Sokolow wrote on :

    And what if we don’t trust Google Analytics? This sort of thing is why F-Droid puts up big red warnings for Firefox.

    Most of our machines have GA blocked using both a local HOSTS file and at the router, but there are Android devices here which aren’t rooted.

    1. Jorge Villalobos wrote on :

      It would be possible for an extension to block access to GA for all extensions, but to be completely sure it’s best to block it in HOSTS like you mentioned. Otherwise you should avoid add-ons that do this, either by looking at the privacy policy or inspecting the sources and/or web traffic.

  3. Pochy wrote on :

    Hi Jorge
    That is interesting solution, but I do not like much GA, is possible to do this for another platform like piwik or other system for tracking visits.

    1. Jorge Villalobos wrote on :

      I don’t know if other tracking systems have APIs, I only tried with GA because it’s already very popular among add-on devs. Feel free to try out other systems and comment here!

    2. Daniel wrote on :

      I haven’t tried it inside an add-on, but this Piwik code works for me in my web page:

      _paq.push([‘trackEvent’, ‘SomeCategory’, ‘SomeDetail’]);

      _paq is just the global variable name, it might not be the same for other users of Piwik but you should be able to see it at the place where you integrate Piwik.

      1. Jorge Villalobos wrote on :

        Yes, but the point of this blog post is to avoid injecting remote scripts into your code. To do this with Piwik we need an API that lets you use XHR to send the data directly. That’s what would require some investigation.

  4. Peter wrote on :

    If an extensions uses GA or other trackers, there should be a warning on the AMO page. Hiding this information in the privacy policy is not a sufficient solution.

    Instead of creating blog posts telling develops how to track users in extensions, Mozilla should focus on creating an environment that respects users’ privacy. After reading “Using Google Analytics in Extensions” in my feed reader I expected a post saying “Don’t.” – not a tutorial.

    1. Electronium wrote on :

      Oh good, yet another person who doesn’t want developers to have any insight into how their software is used, even via anonymized analytics. Go write yourself an addon that hides the ones you have a ridiculous stance against. The rest of us don’t mind letting developers have some basic statistics to work with if it helps them improve their software. Not everyone is gathering stats just to hunt you down and make your life miserable.

      1. Peter wrote on :

        I’m fine with (optional) telemetry (as implemented for example in Firefox), but against being tracked by ad companies in whatever I’m doing.

        By the way, I’m a developer of a Firefox/Chrome extension with a six digit user base. And somehow I manged that without using 3rd party trackers.

      2. Stephan Sokolow wrote on :

        1. Do you know why NetFlix doesn’t do The NetFlix Challenge anymore? (The NetFlix Challenge was a contest where the research team which produced a good enough improvement over their previous suggestion algorithm won a million dollars.)

        They don’t do it because a law was passed requiring a *guarantee* that the anonymized test data can’t be de-anonymized by some clever person. (The law was passed after a judge got burned by someone who dug his video rental records out of a dumpster.)

        2. Be outraged all you want, but our admin has GA blocked at the gateway router as well as via HOSTS files on each individual machine, updated monthly. If the developers were using their own telemetry (or if Mozilla provided a telemetry service), then their pings would get through but, as-is, it’s blocked by a mechanism that was put in place without any consideration that GA might be used by things other than websites.

        3. Firefox doesn’t have Chrome’s nice little “Allow to run in incognito windows” checkbox. You’re damn right I’m going to make sure that my extensions never send analytics pings.

  5. Ulf3000 wrote on :

    welp , this is disconcerning , google is one of the companys with a “666” logo.

    if it didn´t ocur to why they collect everything, i tell you why now ->

    every analysis since the beginning of mankind was collected to predict the future outcomes in similar cases .
    thats common knowledge , all sience and everything else is build upon that promise .

    so now think about if someone just collects all social data in the world , why would someone do that ? becasue they want to build a society-future-prediction-and-manipulation system.

    Im not content with that , now we already got an ON-SLEW of crappy startpage webextensions ported to firefox. next round they´ll all come with illuminati tracking system, you need to put a BIG RED BUTOON on the addons dowload page which says “Beware! this addon tracks your life”

  6. Philip wrote on :

    First, why content script is needed? Background.js can handle AJAX request. If it is security, AJAX call in content script may danger web site like background.js does for browser. Content script has to be banned for AJAX call if it is security concern.

    What about XUL addons?

    1. Jorge Villalobos wrote on :

      The add-on in the example is just a content script, so that’s why I added the code there. You’re right that, generally, it’s more appropriate to have the code in the background script. Since it’s just an XHR POST, there’s no danger of anything remote being run in the client.

      The example should work the same with XUL add-ons. The reporting could be done from a JS Module, a component, a bootstrap script, etc.

      1. Philip wrote on :

        Yes background script is better. Google Chrome also use background scripts for Google Analytics.

        OK didn’t test but content script may collect domain specific GA cookies if “permissions:cookies” allowed.

        This might break domain GA statistics the content script attached to. Or extension may collect domain’s GA cookies at its Google Analytics account while calling GA AJAX.

        I don’t suppose this is possible for background.js because it is attached to local background.html and there is no domain cookies for background.html

        Test content script.execution thoroughly, not to break domain GA statistics. Ban it if it manipulates domain GA.

        Ask Google Analytics team or Google Chrome devs why background.html is preferred by them, but not content script.

        1. Jorge Villalobos wrote on :

          I did some testing and didn’t see any cookies being stored using the method implemented in this post. It’s likely that the more conventional method of injecting the script does do that. Note that the method recommended here is also used for mobile apps and other applications that wouldn’t be using XHR or have cookie storage, so it wouldn’t make much sense to try to store cookies in those cases.

          Thanks for bringing this up, I hadn’t considered that case.

  7. Alex wrote on :

    Using this functionality I send data to Google Analytics with the help of Google Chrome and Mozilla Firefox. I receive the same answer response (GIF89a) but data sent via Google Chrome appear in Google Analytics and those sent via Mozilla do not. Please advise what are the necessary steps to be done to avoid this mistake. Thanks!

  8. Alex wrote on :

    Thanks! This option doesn’t work for me for some reason in Mozilla Firefox though it works in Chrome and the response is the same in both cases. Can you please advise what causes this issue?

    1. Jorge Villalobos wrote on :

      What isn’t working for you? Is the data just not being recorded by GA?

  9. Alex wrote on :

    Yes, the data just not being recorded by GA!

    1. Jorge Villalobos wrote on :

      You’re possibly not sending all the required parameters. I suggest you read the documentation carefully, since different types of hit have different required fields.

  10. Alex wrote on :

    “POST” to “”

    chrome – > “v=1&tid=” + my_GA_TRACKING_ID + “&cid= 555&t=event&ec=AAA1&ea=test”;
    ff -> “v=1&tid=” + my_GA_TRACKING_ID + “&cid= 555&t=event&ec=AAA2&ea=test”

    But, unfortunately, the data from FF just not being recorded by GA

  11. Alex wrote on :

    the problem is solved, thanks.
    I send data to another js file and it worked

  12. laike9m wrote on :

    What should I fill in under the “Website URL” when creating new property?

    1. Jorge Villalobos wrote on :

      I set it up as a mobile app, which I think didn’t require a domain. If you want to set it up as a webpage, you could use some domain you own.