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 500749 588d1e2f8c9d9f34ac3d3ac62960e1660fcc9afb
parent 500716 fab6a95048aaf7ca24a337e7a79963955b8cd34f
child 500750 011cfa1666cbede441dcb7bc5f9e4b00c41d5878
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrhelmer, gbrown
bugs1451484
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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();