Bug 1451484 - Import WebExtension sources for the WebCompat GoFaster Addon to Fennec. r=rhelmer,gbrown
authorDennis Schubert <dschubert@mozilla.com>
Fri, 19 Oct 2018 20:36:12 +0000
changeset 490457 588d1e2f8c9d9f34ac3d3ac62960e1660fcc9afb
parent 490456 fab6a95048aaf7ca24a337e7a79963955b8cd34f
child 490459 011cfa1666cbede441dcb7bc5f9e4b00c41d5878
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersrhelmer, gbrown
bugs1451484
milestone64.0a1
Bug 1451484 - Import WebExtension sources for the WebCompat GoFaster Addon to Fennec. r=rhelmer,gbrown Differential Revision: https://phabricator.services.mozilla.com/D9298
mobile/android/extensions/webcompat/aboutConfigPrefs.js
mobile/android/extensions/webcompat/aboutConfigPrefs.json
mobile/android/extensions/webcompat/bootstrap.js
mobile/android/extensions/webcompat/content/data/ua_overrides.jsm
mobile/android/extensions/webcompat/content/lib/ua_overrider.jsm
mobile/android/extensions/webcompat/injections.js
mobile/android/extensions/webcompat/injections/css/bug0000000-dummy-css-injection.css
mobile/android/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
mobile/android/extensions/webcompat/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
mobile/android/extensions/webcompat/injections/js/bug1457335-histography.io-ua-change.js
mobile/android/extensions/webcompat/injections/js/bug1472075-bankofamerica.com-ua-change.js
mobile/android/extensions/webcompat/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
mobile/android/extensions/webcompat/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
mobile/android/extensions/webcompat/install.rdf.in
mobile/android/extensions/webcompat/jar.mn
mobile/android/extensions/webcompat/manifest.json
mobile/android/extensions/webcompat/moz.build
mobile/android/extensions/webcompat/test/.eslintrc.js
mobile/android/extensions/webcompat/test/browser.ini
mobile/android/extensions/webcompat/test/browser_check_installed.js
mobile/android/extensions/webcompat/test/browser_overrider.js
mobile/android/extensions/webcompat/ua_overrides.js
mobile/android/extensions/webcompat/webextension/background.js
mobile/android/extensions/webcompat/webextension/injections/css/bug0000000-dummy-css-injection.css
mobile/android/extensions/webcompat/webextension/injections/js/bug0000000-dummy-js-injection.js
mobile/android/extensions/webcompat/webextension/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
mobile/android/extensions/webcompat/webextension/injections/js/bug1457335-histography.io-ua-change.js
mobile/android/extensions/webcompat/webextension/injections/js/bug1472075-bankofamerica.com-ua-change.js
mobile/android/extensions/webcompat/webextension/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
mobile/android/extensions/webcompat/webextension/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
mobile/android/extensions/webcompat/webextension/manifest.json
mobile/android/tests/browser/robocop/robocop_testharness.js
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/aboutConfigPrefs.js
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/* global ExtensionAPI, ExtensionCommon */
+
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+this.aboutConfigPrefs = class extends ExtensionAPI {
+  getAPI(context) {
+    const EventManager = ExtensionCommon.EventManager;
+    const extensionIDBase = context.extension.id.split("@")[0];
+    const extensionPrefNameBase = `extensions.${extensionIDBase}.`;
+
+    return {
+      aboutConfigPrefs: {
+        onPrefChange: new EventManager({
+          context,
+          name: "aboutConfigPrefs.onUAOverridesPrefChange",
+          register: (fire, name) => {
+            const prefName = `${extensionPrefNameBase}${name}`;
+            const callback = () => {
+              fire.async(name).catch(() => {}); // ignore Message Manager disconnects
+            };
+            Services.prefs.addObserver(prefName, callback);
+            return () => {
+              Services.prefs.removeObserver(prefName, callback);
+            };
+          },
+        }).api(),
+        async getPref(name) {
+          try {
+            return Services.prefs.getBoolPref(`${extensionPrefNameBase}${name}`);
+          } catch (_) {
+            return undefined;
+          }
+        },
+        async setPref(name, value) {
+          Services.prefs.setBoolPref(`${extensionPrefNameBase}${name}`, value);
+        },
+      },
+    };
+  }
+};
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/aboutConfigPrefs.json
@@ -0,0 +1,53 @@
+[
+  {
+    "namespace": "aboutConfigPrefs",
+    "description": "experimental API extension to allow access to about:config preferences",
+    "events": [
+      {
+        "name": "onPrefChange",
+        "type": "function",
+        "parameters": [{
+          "name": "name",
+          "type": "string",
+          "description": "The preference which changed"
+        }],
+        "extraParameters": [{
+          "name": "name",
+          "type": "string",
+          "description": "The preference to monitor"
+        }]
+      }
+    ],
+    "functions": [
+      {
+        "name": "getPref",
+        "type": "function",
+        "description": "Get a preference's value",
+        "parameters": [{
+          "name": "name",
+          "type": "string",
+          "description": "The preference name"
+        }],
+        "async": true
+      },
+      {
+        "name": "setPref",
+        "type": "function",
+        "description": "Set a preference's value",
+        "parameters": [
+          {
+            "name": "name",
+            "type": "string",
+            "description": "The preference name"
+          },
+          {
+            "name": "value",
+            "type": "boolean",
+            "description": "The new value"
+          }
+        ],
+        "async": true
+      }
+    ]
+  }
+]
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/bootstrap.js
+++ /dev/null
@@ -1,116 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-const PREF_BRANCH = "extensions.webcompat.";
-const PREF_DEFAULTS = {
-  perform_injections: true,
-  perform_ua_overrides: true
-};
-
-const INJECTIONS_ENABLE_PREF_NAME = "extensions.webcompat.perform_injections";
-
-const BROWSER_STARTUP_FINISHED_TOPIC = "browser-delayed-startup-finished";
-
-const UA_OVERRIDES_INIT_TOPIC = "useragentoverrides-initialized";
-const UA_ENABLE_PREF_NAME = "extensions.webcompat.perform_ua_overrides";
-
-ChromeUtils.defineModuleGetter(this, "UAOverrider", "chrome://webcompat/content/lib/ua_overrider.jsm");
-ChromeUtils.defineModuleGetter(this, "UAOverrides", "chrome://webcompat/content/data/ua_overrides.jsm");
-
-let overrider;
-let webextensionPort;
-
-function InjectionsEnablePrefObserver() {
-  let isEnabled = Services.prefs.getBoolPref(INJECTIONS_ENABLE_PREF_NAME);
-  webextensionPort.postMessage({
-    type: "injection-pref-changed",
-    prefState: isEnabled
-  });
-}
-
-function UAEnablePrefObserver() {
-  let isEnabled = Services.prefs.getBoolPref(UA_ENABLE_PREF_NAME);
-  overrider.setShouldOverride(isEnabled);
-}
-
-function setDefaultPrefs() {
-  const branch = Services.prefs.getDefaultBranch(PREF_BRANCH);
-  for (const [key, val] of Object.entries(PREF_DEFAULTS)) {
-    // If someone beat us to setting a default, don't overwrite it.
-    if (branch.getPrefType(key) !== branch.PREF_INVALID) {
-      continue;
-    }
-
-    switch (typeof val) {
-      case "boolean":
-        branch.setBoolPref(key, val);
-        break;
-      case "number":
-        branch.setIntPref(key, val);
-        break;
-      case "string":
-        branch.setCharPref(key, val);
-        break;
-    }
-  }
-}
-
-this.install = function() {};
-this.uninstall = function() {};
-
-this.startup = function({webExtension}) {
-  setDefaultPrefs();
-
-  // Intentionally reset the preference on every browser restart to avoid site
-  // breakage by accidentally toggled preferences or by leaving it off after
-  // debugging a site.
-  Services.prefs.clearUserPref(INJECTIONS_ENABLE_PREF_NAME);
-  Services.prefs.addObserver(INJECTIONS_ENABLE_PREF_NAME, InjectionsEnablePrefObserver);
-
-  Services.prefs.clearUserPref(UA_ENABLE_PREF_NAME);
-  Services.prefs.addObserver(UA_ENABLE_PREF_NAME, UAEnablePrefObserver);
-
-  // Listen to the useragentoverrides-initialized notification we get and
-  // initialize our overrider there. This is done to avoid slowing down the
-  // apparent startup process, since we avoid loading anything before the first
-  // window is visible to the user. See bug 1371442 for details.
-  let uaStartupObserver = {
-    observe(aSubject, aTopic, aData) {
-      if (aTopic !== UA_OVERRIDES_INIT_TOPIC) {
-        return;
-      }
-
-      Services.obs.removeObserver(this, UA_OVERRIDES_INIT_TOPIC);
-      overrider = new UAOverrider(UAOverrides);
-      overrider.init();
-    }
-  };
-  Services.obs.addObserver(uaStartupObserver, UA_OVERRIDES_INIT_TOPIC);
-
-  // Observe browser-delayed-startup-finished and only initialize our embedded
-  // WebExtension after that. Otherwise, we'd try to initialize as soon as the
-  // browser starts up, which adds a heavy startup penalty.
-  let appStartupObserver = {
-    observe(aSubject, aTopic, aData) {
-      webExtension.startup().then((api) => {
-        api.browser.runtime.onConnect.addListener((port) => {
-          webextensionPort = port;
-        });
-
-        return Promise.resolve();
-      }).catch((ex) => {
-        console.error(ex);
-      });
-      Services.obs.removeObserver(this, BROWSER_STARTUP_FINISHED_TOPIC);
-    }
-  };
-  Services.obs.addObserver(appStartupObserver, BROWSER_STARTUP_FINISHED_TOPIC);
-};
-
-this.shutdown = function() {
-  Services.prefs.removeObserver(INJECTIONS_ENABLE_PREF_NAME, InjectionsEnablePrefObserver);
-  Services.prefs.removeObserver(UA_ENABLE_PREF_NAME, UAEnablePrefObserver);
-};
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/content/data/ua_overrides.jsm
+++ /dev/null
@@ -1,65 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * For detailed information on our policies, and a documention on this format
- * and its possibilites, please check the Mozilla-Wiki at
- *
- * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
- */
-const UAOverrides = [
-
-  /*
-   * This is a dummy override that applies a Chrome UA to a dummy site that
-   * blocks all browsers but Chrome.
-   *
-   * This was only put in place to allow QA to test this system addon on an
-   * actual site, since we were not able to find a proper override in time.
-   */
-  {
-    baseDomain: "schub.io",
-    applications: ["firefox", "fennec"],
-    uriMatcher: (uri) => uri.includes("webcompat-addon-testcases.schub.io"),
-    uaTransformer: (originalUA) => {
-      let prefix = originalUA.substr(0, originalUA.indexOf(")") + 1);
-      return `${prefix} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36`;
-    }
-  },
-
-  /*
-   * Bug 1480710 - m.imgur.com - Build UA override
-   * WebCompat issue #13154 - https://webcompat.com/issues/13154
-   *
-   * imgur returns a 404 for requests to CSS and JS file if requested with a Fennec
-   * User Agent. By removing the Fennec identifies and adding Chrome Mobile's, we
-   * receive the correct CSS and JS files.
-   */
-  {
-    baseDomain: "imgur.com",
-    applications: ["fennec"],
-    uriMatcher: (uri) => uri.includes("m.imgur.com"),
-    uaTransformer: (originalUA) => {
-      let prefix = originalUA.substr(0, originalUA.indexOf(")") + 1);
-      return prefix + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36";
-    }
-  },
-
-  /*
-   * Bug 755590 - sites.google.com - top bar doesn't show up in Firefox for Android
-   *
-   * Google Sites does show a different top bar template based on the User Agent.
-   * For Fennec, this results in a broken top bar. Appending Chrome and Mobile Safari
-   * identifiers to the UA results in a correct rendering.
-   */
-  {
-    baseDomain: "google.com",
-    applications: ["fennec"],
-    uriMatcher: (uri) => uri.includes("sites.google.com"),
-    uaTransformer: (originalUA) => {
-      return originalUA + " Chrome/68.0.3440.85 Mobile Safari/537.366";
-    }
-  }
-];
-
-var EXPORTED_SYMBOLS = ["UAOverrides"]; /* exported UAOverrides */
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/content/lib/ua_overrider.jsm
+++ /dev/null
@@ -1,126 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm");
-
-class UAOverrider {
-  constructor(overrides) {
-    this._overrides = {};
-    this._shouldOverride = true;
-
-    this.initOverrides(overrides);
-  }
-
-  initOverrides(overrides) {
-    // on xpcshell tests, there is no implementation for nsIXULAppInfo, so this
-    // might fail there. To have all of our test cases running at all times,
-    // assume they are on Desktop for now.
-    let currentApplication = "firefox";
-    try {
-      currentApplication = Services.appinfo.name.toLowerCase();
-    } catch (ex) {
-      console.warn("Getting appinfo.name failed, assuming we run on Desktop.", ex);
-    }
-
-    for (let override of overrides) {
-      // Firefox for Desktop is the default application for all overrides.
-      if (!override.applications) {
-        override.applications = ["firefox"];
-      }
-
-      // If the current application is not targeted by the override in question,
-      // we can skip adding the override to our checks entirely.
-      if (!override.applications.includes(currentApplication)) {
-        continue;
-      }
-
-      if (!this._overrides[override.baseDomain]) {
-        this._overrides[override.baseDomain] = [];
-      }
-
-      if (!override.uriMatcher) {
-        override.uriMatcher = () => true;
-      }
-
-      this._overrides[override.baseDomain].push(override);
-    }
-  }
-
-  /**
-   * Used for disabling overrides when the pref has been flipped to false.
-   *
-   * Since we no longer use our own event handlers, we check this bool in our
-   * override callback and simply return early if we are not supposed to do
-   * anything.
-   */
-  setShouldOverride(newState) {
-    this._shouldOverride = newState;
-  }
-
-  init() {
-    UserAgentOverrides.addComplexOverride(this.overrideCallback.bind(this));
-  }
-
-  overrideCallback(channel, defaultUA) {
-    if (!this._shouldOverride) {
-      return false;
-    }
-
-    let uaOverride = this.lookupUAOverride(channel.URI, defaultUA);
-    if (uaOverride) {
-      console.log("The user agent has been overridden for compatibility reasons.");
-      return uaOverride;
-    }
-
-    return false;
-  }
-
-  /**
-   * Try to use the eTLDService to get the base domain (will return example.com
-   * for http://foo.bar.example.com/foo/bar).
-   *
-   * However, the eTLDService is a bit picky and throws whenever we pass a
-   * blank host name or an IP into it, see bug 1337785. Since we do not plan on
-   * override UAs for such cases, we simply catch everything and return false.
-   */
-  getBaseDomainFromURI(uri) {
-    try {
-      return Services.eTLD.getBaseDomain(uri);
-    } catch (_) {
-      return false;
-    }
-  }
-
-  /**
-   * This function returns a User Agent based on the URI passed into. All
-   * override rules are defined in data/ua_overrides.jsm and the required format
-   * is explained there.
-   *
-   * Since it is expected and designed to have more than one override per base
-   * domain, we have to loop over this._overrides[baseDomain], which contains
-   * all available overrides.
-   *
-   * If the uriMatcher function returns true, the uaTransformer function gets
-   * called and its result will be used as the Use Agent for the current
-   * request.
-   *
-   * If there are more than one possible overrides, that is if two or more
-   * uriMatchers would return true, the first one gets applied.
-   */
-  lookupUAOverride(uri, defaultUA) {
-    let baseDomain = this.getBaseDomainFromURI(uri);
-    if (baseDomain && this._overrides[baseDomain]) {
-      for (let uaOverride of this._overrides[baseDomain]) {
-        if (uaOverride.uriMatcher(uri.specIgnoringRef)) {
-          return uaOverride.uaTransformer(defaultUA);
-        }
-      }
-    }
-
-    return false;
-  }
-}
-
-var EXPORTED_SYMBOLS = ["UAOverrider"]; /* exported UAOverrider */
rename from mobile/android/extensions/webcompat/webextension/background.js
rename to mobile/android/extensions/webcompat/injections.js
--- a/mobile/android/extensions/webcompat/webextension/background.js
+++ b/mobile/android/extensions/webcompat/injections.js
@@ -5,49 +5,49 @@
  * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
  */
 const contentScripts = {
   universal: [
     {
       matches: ["*://webcompat-addon-testcases.schub.io/*"],
       css: [{file: "injections/css/bug0000000-dummy-css-injection.css"}],
       js: [{file: "injections/js/bug0000000-dummy-js-injection.js"}],
-      runAt: "document_start"
-    }
+      runAt: "document_start",
+    },
   ],
   desktop: [
     {
       matches: ["https://ib.absa.co.za/*"],
       js: [{file: "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js"}],
-      runAt: "document_start"
+      runAt: "document_start",
     },
     {
       matches: ["http://histography.io/*"],
       js: [{file: "injections/js/bug1457335-histography.io-ua-change.js"}],
-      runAt: "document_start"
+      runAt: "document_start",
     },
     {
       matches: ["*://*.bankofamerica.com/*"],
       js: [{file: "injections/js/bug1472075-bankofamerica.com-ua-change.js"}],
-      runAt: "document_start"
+      runAt: "document_start",
     },
     {
       matches: ["http://202.166.205.141/bbvrs/*"],
       js: [{file: "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js"}],
       runAt: "document_start",
-      allFrames: true
+      allFrames: true,
     },
     {
       matches: ["*://portalminasnet.com/*"],
       js: [{file: "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js"}],
       runAt: "document_start",
-      allFrames: true
-    }
+      allFrames: true,
+    },
   ],
-  android: []
+  android: [],
 };
 
 /* globals browser */
 
 let port = browser.runtime.connect();
 let registeredContentScripts = [];
 
 async function registerContentScripts() {
@@ -81,14 +81,22 @@ port.onMessage.addListener((message) => 
         registerContentScripts();
       } else {
         unregisterContentScripts();
       }
       break;
   }
 });
 
-/**
- * Note that we reset all preferences on extension startup, so the injections will
- * never be disabled when this loads up. Because of that, we can simply register
- * right away.
- */
-registerContentScripts();
+const INJECTION_PREF = "perform_injections";
+function checkInjectionPref() {
+  browser.aboutConfigPrefs.getPref(INJECTION_PREF).then(value => {
+    if (value === undefined) {
+      browser.aboutConfigPrefs.setPref(INJECTION_PREF, true);
+    } else if (value === false) {
+      unregisterContentScripts();
+    } else {
+      registerContentScripts();
+    }
+  });
+}
+browser.aboutConfigPrefs.onPrefChange.addListener(checkInjectionPref, INJECTION_PREF);
+checkInjectionPref();
rename from mobile/android/extensions/webcompat/webextension/injections/css/bug0000000-dummy-css-injection.css
rename to mobile/android/extensions/webcompat/injections/css/bug0000000-dummy-css-injection.css
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug0000000-dummy-js-injection.js
rename to mobile/android/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug0000000-dummy-js-injection.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
@@ -2,10 +2,10 @@
 
 /* globals exportFunction */
 
 Object.defineProperty(window.wrappedJSObject, "isTestFeatureSupported", {
   get: exportFunction(function() {
     return true;
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
rename to mobile/android/extensions/webcompat/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js
@@ -18,10 +18,10 @@
 
 console.info("window.controllers has been shimmed for compatibility reasons. See https://webcompat.com/issues/16401 for details.");
 
 Object.defineProperty(window.wrappedJSObject, "controllers", {
   get: exportFunction(function() {
     return true;
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug1457335-histography.io-ua-change.js
rename to mobile/android/extensions/webcompat/injections/js/bug1457335-histography.io-ua-change.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug1457335-histography.io-ua-change.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug1457335-histography.io-ua-change.js
@@ -15,18 +15,18 @@ console.info("The user agent has been ov
 
 const CHROME_UA = navigator.userAgent + " Chrome for WebCompat";
 
 Object.defineProperty(window.navigator.wrappedJSObject, "userAgent", {
   get: exportFunction(function() {
     return CHROME_UA;
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
 
 Object.defineProperty(window.navigator.wrappedJSObject, "vendor", {
   get: exportFunction(function() {
     return "Google Inc.";
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug1472075-bankofamerica.com-ua-change.js
rename to mobile/android/extensions/webcompat/injections/js/bug1472075-bankofamerica.com-ua-change.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug1472075-bankofamerica.com-ua-change.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug1472075-bankofamerica.com-ua-change.js
@@ -17,27 +17,27 @@ if (!navigator.platform.includes("Win"))
 
   const WINDOWS_UA = navigator.userAgent.replace(/\(.*; rv:/i, "(Windows NT 10.0; Win64; x64; rv:");
 
   Object.defineProperty(window.navigator.wrappedJSObject, "userAgent", {
     get: exportFunction(function() {
       return WINDOWS_UA;
     }, window),
 
-    set: exportFunction(function() {}, window)
+    set: exportFunction(function() {}, window),
   });
 
   Object.defineProperty(window.navigator.wrappedJSObject, "appVersion", {
     get: exportFunction(function() {
       return "appVersion";
     }, window),
 
-    set: exportFunction(function() {}, window)
+    set: exportFunction(function() {}, window),
   });
 
   Object.defineProperty(window.navigator.wrappedJSObject, "platform", {
     get: exportFunction(function() {
       return "Win64";
     }, window),
 
-    set: exportFunction(function() {}, window)
+    set: exportFunction(function() {}, window),
   });
 }
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
rename to mobile/android/extensions/webcompat/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js
@@ -14,10 +14,10 @@
 
 console.info("window.sidebar has been shimmed for compatibility reasons. See https://webcompat.com/issues/11622 for details.");
 
 Object.defineProperty(window.wrappedJSObject, "sidebar", {
   get: exportFunction(function() {
     return false;
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
rename from mobile/android/extensions/webcompat/webextension/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
rename to mobile/android/extensions/webcompat/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
--- a/mobile/android/extensions/webcompat/webextension/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
+++ b/mobile/android/extensions/webcompat/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js
@@ -14,10 +14,10 @@
 
 console.info("window.sidebar has been shimmed for compatibility reasons. See https://webcompat.com/issues/18143 for details.");
 
 Object.defineProperty(window.wrappedJSObject, "sidebar", {
   get: exportFunction(function() {
     return false;
   }, window),
 
-  set: exportFunction(function() {}, window)
+  set: exportFunction(function() {}, window),
 });
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/install.rdf.in
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>webcompat@mozilla.org</em:id>
-    <em:version>2.0.1</em:version>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-    <em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
-
-    <!-- Firefox Desktop -->
-    <em:targetApplication>
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
-        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Firefox for Android -->
-    <em:targetApplication>
-      <Description>
-        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
-        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
-        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>Web Compat</em:name>
-    <em:description>Urgent post-release fixes for web compatibility.</em:description>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/jar.mn
+++ /dev/null
@@ -1,3 +0,0 @@
-[features/webcompat@mozilla.org] chrome.jar:
-% content webcompat %content/
-  content/ (content/*)
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/manifest.json
@@ -0,0 +1,37 @@
+{
+  "manifest_version": 2,
+  "name": "Web Compat",
+  "description": "Urgent post-release fixes for web compatibility.",
+  "version": "3.0.0",
+
+  "applications": {
+    "gecko": {
+      "id": "webcompat@mozilla.org",
+      "strict_min_version": "59.0b5"
+    }
+  },
+
+  "experiment_apis": {
+    "aboutConfigPrefs": {
+      "schema": "aboutConfigPrefs.json",
+      "parent": {
+        "scopes": ["addon_parent"],
+        "script": "aboutConfigPrefs.js",
+        "paths": [["aboutConfigPrefs"]]
+      }
+    }
+  },
+
+  "permissions": [
+    "webRequest",
+    "webRequestBlocking",
+    "<all_urls>"
+  ],
+
+  "background": {
+    "scripts": [
+      "injections.js",
+      "ua_overrides.js"
+    ]
+  }
+}
--- a/mobile/android/extensions/webcompat/moz.build
+++ b/mobile/android/extensions/webcompat/moz.build
@@ -3,38 +3,30 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org'] += [
-  'bootstrap.js'
-]
-
-FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension'] += [
-  'webextension/background.js',
-  'webextension/manifest.json'
-]
-
-FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension']['injections']['css'] += [
-  'webextension/injections/css/bug0000000-dummy-css-injection.css'
+  'aboutConfigPrefs.js',
+  'aboutConfigPrefs.json',
+  'injections.js',
+  'manifest.json',
+  'ua_overrides.js'
 ]
 
-FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension']['injections']['js'] += [
-  'webextension/injections/js/bug0000000-dummy-js-injection.js',
-  'webextension/injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js',
-  'webextension/injections/js/bug1457335-histography.io-ua-change.js',
-  'webextension/injections/js/bug1472075-bankofamerica.com-ua-change.js',
-  'webextension/injections/js/bug1472081-election.gov.np-window.sidebar-shim.js',
-  'webextension/injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js'
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['css'] += [
+  'injections/css/bug0000000-dummy-css-injection.css'
 ]
 
-FINAL_TARGET_PP_FILES.features['webcompat@mozilla.org'] += [
-  'install.rdf.in'
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['js'] += [
+  'injections/js/bug0000000-dummy-js-injection.js',
+  'injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js',
+  'injections/js/bug1457335-histography.io-ua-change.js',
+  'injections/js/bug1472075-bankofamerica.com-ua-change.js',
+  'injections/js/bug1472081-election.gov.np-window.sidebar-shim.js',
+  'injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js'
 ]
 
-BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
-JAR_MANIFESTS += ['jar.mn']
-
 with Files('**'):
   BUG_COMPONENT = ('Web Compatibility Tools', 'Go Faster')
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/test/.eslintrc.js
+++ /dev/null
@@ -1,7 +0,0 @@
-"use strict";
-
-module.exports = {
-  "extends": [
-    "plugin:mozilla/browser-test"
-  ]
-};
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/test/browser.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[DEFAULT]
-
-[browser_check_installed.js]
-[browser_overrider.js]
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/test/browser_check_installed.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-add_task(async function installed() {
-  let addon = await AddonManager.getAddonByID("webcompat@mozilla.org");
-  isnot(addon, null, "Webcompat addon should exist");
-  is(addon.name, "Web Compat");
-  ok(addon.isCompatible, "Webcompat addon is compatible with Firefox");
-  ok(!addon.appDisabled, "Webcompat addon is not app disabled");
-  ok(addon.isActive, "Webcompat addon is active");
-  is(addon.type, "extension", "Webcompat addon is type extension");
-});
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/test/browser_overrider.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "UAOverrider", "chrome://webcompat/content/lib/ua_overrider.jsm");
-XPCOMUtils.defineLazyServiceGetter(this, "IOService", "@mozilla.org/network/io-service;1", "nsIIOService");
-
-function getnsIURI(uri) {
-  return IOService.newURI(uri, "utf-8");
-}
-
-add_task(function test() {
-  let overrider = new UAOverrider([
-    {
-      baseDomain: "example.org",
-      uaTransformer: () => "Test UA"
-    }
-  ]);
-
-  let finalUA = overrider.lookupUAOverride(getnsIURI("http://www.example.org/foobar/"));
-  is(finalUA, "Test UA", "Overrides the UA without a matcher function");
-});
-
-add_task(function test() {
-  let overrider = new UAOverrider([
-    {
-      baseDomain: "example.org",
-      uriMatcher: () => false,
-      uaTransformer: () => "Test UA"
-    }
-  ]);
-
-  let finalUA = overrider.lookupUAOverride(getnsIURI("http://www.example.org/foobar/"));
-  isnot(finalUA, "Test UA", "Does not override the UA with the matcher returning false");
-});
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/ua_overrides.js
@@ -0,0 +1,129 @@
+/**
+ * For detailed information on our policies, and a documention on this format
+ * and its possibilites, please check the Mozilla-Wiki at
+ *
+ * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
+ */
+const UAOverrides = {
+  universal: [
+    /*
+     * This is a dummy override that applies a Chrome UA to a dummy site that
+     * blocks all browsers but Chrome.
+     *
+     * This was only put in place to allow QA to test this system addon on an
+     * actual site, since we were not able to find a proper override in time.
+     */
+    {
+      matches: ["*://webcompat-addon-testcases.schub.io/*"],
+      uaTransformer: (originalUA) => {
+        let prefix = originalUA.substr(0, originalUA.indexOf(")") + 1);
+        return `${prefix} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36`;
+      },
+    },
+  ],
+  desktop: [],
+  android: [
+    /*
+     * Bug 1480710 - m.imgur.com - Build UA override
+     * WebCompat issue #13154 - https://webcompat.com/issues/13154
+     *
+     * imgur returns a 404 for requests to CSS and JS file if requested with a Fennec
+     * User Agent. By removing the Fennec identifies and adding Chrome Mobile's, we
+     * receive the correct CSS and JS files.
+     */
+    {
+      matches: ["*://m.imgur.com/*"],
+      uaTransformer: (originalUA) => {
+        let prefix = originalUA.substr(0, originalUA.indexOf(")") + 1);
+        return prefix + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36";
+      },
+    },
+
+    /*
+     * Bug 755590 - sites.google.com - top bar doesn't show up in Firefox for Android
+     *
+     * Google Sites does show a different top bar template based on the User Agent.
+     * For Fennec, this results in a broken top bar. Appending Chrome and Mobile Safari
+     * identifiers to the UA results in a correct rendering.
+     */
+    {
+      matches: ["*://sites.google.com/*"],
+      uaTransformer: (originalUA) => {
+        return originalUA + " Chrome/68.0.3440.85 Mobile Safari/537.366";
+      },
+    },
+
+    /*
+     * Bug 945963 - tieba.baidu.com serves simplified mobile content to Firefox Android
+     * WebCompat issue #18455 - https://webcompat.com/issues/18455
+     *
+     * tieba.baidu.com and tiebac.baidu.com serve a heavily simplified and less functional
+     * mobile experience to Firefox for Android users. Adding the AppleWebKit indicator
+     * to the User Agent gets us the same experience.
+     */
+    {
+      matches: ["*://tieba.baidu.com/*", "*://tiebac.baidu.com/*"],
+      uaTransformer: (originalUA) => {
+        return originalUA + " AppleWebKit/537.36 (KHTML, like Gecko)";
+      },
+    },
+  ],
+};
+
+/* globals browser */
+
+let activeListeners = [];
+function buildAndRegisterListener(matches, transformer) {
+  let listener = (details) => {
+    for (var header of details.requestHeaders) {
+      if (header.name.toLowerCase() === "user-agent") {
+        header.value = transformer(header.value);
+      }
+    }
+    return {requestHeaders: details.requestHeaders};
+  };
+
+  browser.webRequest.onBeforeSendHeaders.addListener(
+    listener,
+    {urls: matches},
+    ["blocking", "requestHeaders"]
+  );
+
+  activeListeners.push(listener);
+}
+
+async function registerUAOverrides() {
+  let platform = "desktop";
+  let platformInfo = await browser.runtime.getPlatformInfo();
+  if (platformInfo.os == "android") {
+    platform = "android";
+  }
+
+  let targetOverrides = UAOverrides.universal.concat(UAOverrides[platform]);
+  targetOverrides.forEach((override) => {
+    buildAndRegisterListener(override.matches, override.uaTransformer);
+  });
+}
+
+function unregisterUAOverrides() {
+  activeListeners.forEach((listener) => {
+    browser.webRequest.onBeforeSendHeaders.removeListener(listener);
+  });
+
+  activeListeners = [];
+}
+
+const OVERRIDE_PREF = "perform_ua_overrides";
+function checkOverridePref() {
+  browser.aboutConfigPrefs.getPref(OVERRIDE_PREF).then(value => {
+    if (value === undefined) {
+      browser.aboutConfigPrefs.setPref(OVERRIDE_PREF, true);
+    } else if (value === false) {
+      unregisterUAOverrides();
+    } else {
+      registerUAOverrides();
+    }
+  });
+}
+browser.aboutConfigPrefs.onPrefChange.addListener(checkOverridePref, OVERRIDE_PREF);
+checkOverridePref();
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/webextension/manifest.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "Web Compat",
-  "description": "Urgent post-release fixes for web compatibility.",
-  "version": "2.0",
-
-  "applications": {
-    "gecko": {
-      "id": "webcompat@mozilla.org",
-      "strict_min_version": "59.0b5"
-    }
-  },
-
-  "permissions": [
-    "<all_urls>"
-  ],
-
-  "background": {
-    "scripts": [
-      "background.js"
-    ]
-  }
-}
--- a/mobile/android/tests/browser/robocop/robocop_testharness.js
+++ b/mobile/android/tests/browser/robocop/robocop_testharness.js
@@ -1,13 +1,20 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+if (!("SpecialPowers" in window)) {
+  dump("Robocop robocop_testharness.js found SpecialPowers unavailable: reloading...\n");
+  setTimeout(() => {
+    window.location.reload();
+  }, 1000);
+}
+
 function sendMessageToJava(message) {
   SpecialPowers.Services.androidBridge.dispatch(message.type, message);
 }
 
 function _evalURI(uri, sandbox) {
   // We explicitly allow Cross-Origin requests, since it is useful for
   // testing, but we allow relative URLs by maintaining our baseURI.
   let req = new XMLHttpRequest();