Bug 1568636 - Release - Import WebCompat GoFaster 5.0.2 sources r=denschub,rhelmer a=RyanVM
authorksenia <kberezina@mozilla.com>
Fri, 02 Aug 2019 17:16:36 +0200
changeset 537276 e84a036414a02b733cffe58fcdb0ab23cad6def0
parent 537275 b8459608aee5d3fa28f54a0fb6246ca87f1e5a7d
child 537277 fdf90a977a136dd0c37ef8b3b4d8402f0862da04
push id2121
push userarchaeopteryx@coole-files.de
push dateFri, 02 Aug 2019 16:03:14 +0000
treeherdermozilla-release@e84a036414a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdenschub, rhelmer, RyanVM
bugs1568636
milestone68.0.2
Bug 1568636 - Release - Import WebCompat GoFaster 5.0.2 sources r=denschub,rhelmer a=RyanVM Differential Revision: https://phabricator.services.mozilla.com/D40435
browser/extensions/webcompat/AboutCompat.jsm
browser/extensions/webcompat/about-compat/AboutCompat.jsm
browser/extensions/webcompat/about-compat/aboutCompat.css
browser/extensions/webcompat/about-compat/aboutCompat.html
browser/extensions/webcompat/about-compat/aboutCompat.js
browser/extensions/webcompat/about-compat/aboutPage.js
browser/extensions/webcompat/about-compat/aboutPage.json
browser/extensions/webcompat/about-compat/aboutPageProcessScript.js
browser/extensions/webcompat/aboutCompat.css
browser/extensions/webcompat/aboutCompat.html
browser/extensions/webcompat/aboutCompat.js
browser/extensions/webcompat/aboutConfigPrefs.js
browser/extensions/webcompat/aboutConfigPrefs.json
browser/extensions/webcompat/aboutPage.js
browser/extensions/webcompat/aboutPage.json
browser/extensions/webcompat/aboutPageProcessScript.js
browser/extensions/webcompat/background.js
browser/extensions/webcompat/data/injections.js
browser/extensions/webcompat/data/ua_overrides.js
browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js
browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json
browser/extensions/webcompat/injections.js
browser/extensions/webcompat/injections/css/bug0000000-dummy-css-injection.css
browser/extensions/webcompat/injections/css/bug0000000-testbed-css-injection.css
browser/extensions/webcompat/injections/css/bug1567610-dns.google.com-moz-fit-content.css
browser/extensions/webcompat/injections/css/bug1568256-zertifikate.commerzbank.de-flex.css
browser/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
browser/extensions/webcompat/injections/js/bug0000000-testbed-js-injection.js
browser/extensions/webcompat/jar.mn
browser/extensions/webcompat/lib/about_compat_broker.js
browser/extensions/webcompat/lib/injections.js
browser/extensions/webcompat/lib/module_shim.js
browser/extensions/webcompat/lib/ua_overrides.js
browser/extensions/webcompat/manifest.json
browser/extensions/webcompat/moz.build
browser/extensions/webcompat/run.js
browser/extensions/webcompat/ua_overrides.js
mobile/android/extensions/webcompat/AboutCompat.jsm
mobile/android/extensions/webcompat/about-compat/AboutCompat.jsm
mobile/android/extensions/webcompat/about-compat/aboutCompat.css
mobile/android/extensions/webcompat/about-compat/aboutCompat.html
mobile/android/extensions/webcompat/about-compat/aboutCompat.js
mobile/android/extensions/webcompat/about-compat/aboutPage.js
mobile/android/extensions/webcompat/about-compat/aboutPage.json
mobile/android/extensions/webcompat/about-compat/aboutPageProcessScript.js
mobile/android/extensions/webcompat/aboutCompat.css
mobile/android/extensions/webcompat/aboutCompat.html
mobile/android/extensions/webcompat/aboutCompat.js
mobile/android/extensions/webcompat/aboutConfigPrefs.js
mobile/android/extensions/webcompat/aboutConfigPrefs.json
mobile/android/extensions/webcompat/aboutPage.js
mobile/android/extensions/webcompat/aboutPage.json
mobile/android/extensions/webcompat/aboutPageProcessScript.js
mobile/android/extensions/webcompat/background.js
mobile/android/extensions/webcompat/data/injections.js
mobile/android/extensions/webcompat/data/ua_overrides.js
mobile/android/extensions/webcompat/experiment-apis/aboutConfigPrefs.js
mobile/android/extensions/webcompat/experiment-apis/aboutConfigPrefs.json
mobile/android/extensions/webcompat/injections.js
mobile/android/extensions/webcompat/injections/css/bug0000000-dummy-css-injection.css
mobile/android/extensions/webcompat/injections/css/bug0000000-testbed-css-injection.css
mobile/android/extensions/webcompat/injections/css/bug1567610-dns.google.com-moz-fit-content.css
mobile/android/extensions/webcompat/injections/css/bug1568256-zertifikate.commerzbank.de-flex.css
mobile/android/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
mobile/android/extensions/webcompat/injections/js/bug0000000-testbed-js-injection.js
mobile/android/extensions/webcompat/jar.mn
mobile/android/extensions/webcompat/lib/about_compat_broker.js
mobile/android/extensions/webcompat/lib/injections.js
mobile/android/extensions/webcompat/lib/module_shim.js
mobile/android/extensions/webcompat/lib/ua_overrides.js
mobile/android/extensions/webcompat/manifest.json
mobile/android/extensions/webcompat/moz.build
mobile/android/extensions/webcompat/run.js
mobile/android/extensions/webcompat/ua_overrides.js
rename from mobile/android/extensions/webcompat/AboutCompat.jsm
rename to browser/extensions/webcompat/about-compat/AboutCompat.jsm
--- a/mobile/android/extensions/webcompat/AboutCompat.jsm
+++ b/browser/extensions/webcompat/about-compat/AboutCompat.jsm
@@ -1,31 +1,37 @@
 /* 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";
 
 var EXPORTED_SYMBOLS = ["AboutCompat"];
 
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const addonID = "webcompat@mozilla.org";
-const addonPageRelativeURL = "/aboutCompat.html";
+const addonPageRelativeURL = "/about-compat/aboutCompat.html";
 
 function AboutCompat() {
-  this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(addonPageRelativeURL);
+  this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(
+    addonPageRelativeURL
+  );
 }
 AboutCompat.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
   getURIFlags() {
     return Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS;
   },
 
   newChannel(aURI, aLoadInfo) {
     const uri = Services.io.newURI(this.chromeURL);
     const channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
     channel.originalURI = aURI;
 
-    channel.owner = Services.scriptSecurityManager.createCodebasePrincipal(uri, aLoadInfo.originAttributes);
+    channel.owner = (Services.scriptSecurityManager.createContentPrincipal ||
+      Services.scriptSecurityManager.createCodebasePrincipal)(
+      uri,
+      aLoadInfo.originAttributes
+    );
     return channel;
   },
 };
rename from mobile/android/extensions/webcompat/aboutCompat.css
rename to browser/extensions/webcompat/about-compat/aboutCompat.css
--- a/mobile/android/extensions/webcompat/aboutCompat.css
+++ b/browser/extensions/webcompat/about-compat/aboutCompat.css
@@ -64,21 +64,21 @@
     background-color: var(--in-content-box-background-hover) !important;
     text-decoration: none;
   }
 
   h2.lighter-font-weight {
     font-weight: lighter;
   }
 
-  html[dir=ltr] th {
+  html[dir="ltr"] th {
     text-align: left;
   }
 
-  html[dir=rtl] th {
+  html[dir="rtl"] th {
     text-align: right;
   }
 }
 
 @media (any-pointer: coarse), (any-pointer: none) {
   * {
     margin: 0;
     padding: 0;
@@ -86,17 +86,19 @@
 
   html {
     font-family: sans-serif;
     font-size: 14px;
     -moz-text-size-adjust: none;
     background-color: #f5f5f5;
   }
 
-  table, tr, p {
+  table,
+  tr,
+  p {
     display: block;
     background: #fff;
   }
 
   table {
     border-top: 2px solid #0a84ff;
     margin-top: -2px;
     position: absolute;
rename from browser/extensions/webcompat/aboutCompat.html
rename to browser/extensions/webcompat/about-compat/aboutCompat.html
--- a/browser/extensions/webcompat/aboutCompat.html
+++ b/browser/extensions/webcompat/about-compat/aboutCompat.html
@@ -1,23 +1,25 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <base/>
+
   <!-- If you change this script tag you must update the hash in the extension's
-         `content_security_policy` 'sha256-HbSjs39Y0thRGfO3RHrNzLPKyC/tq6FdIuP3jEBAcJQ=' -->
-  <script>document.head.firstElementChild.href = browser.runtime.getURL("");</script>
+         `content_security_policy` 'sha256-MmZkN2QaIHhfRWPZ8TVRjijTn5Ci1iEabtTEWrt9CCo=' -->
+  <script>/* globals browser */ document.head.firstElementChild.href = browser.runtime.getURL("");</script>
+
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <link rel="stylesheet" href="aboutCompat.css" />
+  <link rel="stylesheet" href="about-compat/aboutCompat.css" />
   <link rel="stylesheet" media="screen and (pointer:fine), projection" type="text/css"
           href="chrome://global/skin/in-content/common.css"/>
   <link rel="localization" href="toolkit/about/aboutCompat.ftl"/>
   <title data-l10n-id="text-title"></title>
-  <script src="aboutCompat.js"></script>
+  <script src="about-compat/aboutCompat.js"></script>
   </head>
 <body>
   <h2 class="tab active" data-l10n-id="label-overrides"></h2>
   <table id="overrides">
     <col/>
     <col/>
     <col/>
   </table>
rename from mobile/android/extensions/webcompat/aboutCompat.js
rename to browser/extensions/webcompat/about-compat/aboutCompat.js
--- a/mobile/android/extensions/webcompat/aboutCompat.js
+++ b/browser/extensions/webcompat/about-compat/aboutCompat.js
@@ -1,74 +1,83 @@
 /* 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";
 
 /* globals browser */
 
+let availablePatches;
+
 const portToAddon = (function() {
   let port;
 
   function connect() {
-    port = browser.runtime.connect({name: "AboutCompatTab"});
+    port = browser.runtime.connect({ name: "AboutCompatTab" });
     port.onMessage.addListener(onMessageFromAddon);
     port.onDisconnect.addListener(e => {
       port = undefined;
     });
   }
 
   connect();
 
   async function send(message) {
     if (port) {
       return port.postMessage(message);
     }
     return Promise.reject("background script port disconnected");
   }
 
-  return {send};
-}());
+  return { send };
+})();
 
-const $ = function(sel) { return document.querySelector(sel); };
+const $ = function(sel) {
+  return document.querySelector(sel);
+};
 
 const DOMContentLoadedPromise = new Promise(resolve => {
-  document.addEventListener("DOMContentLoaded", () => {
-    resolve();
-  }, {once: true});
+  document.addEventListener(
+    "DOMContentLoaded",
+    () => {
+      resolve();
+    },
+    { once: true }
+  );
 });
 
 Promise.all([
   browser.runtime.sendMessage("getOverridesAndInterventions"),
   DOMContentLoadedPromise,
 ]).then(([info]) => {
   document.body.addEventListener("click", async evt => {
     const ele = evt.target;
     if (ele.nodeName === "BUTTON") {
       const row = ele.closest("[data-id]");
       if (row) {
         evt.preventDefault();
         ele.disabled = true;
         const id = row.getAttribute("data-id");
         try {
-          await browser.runtime.sendMessage({command: "toggle", id});
+          await browser.runtime.sendMessage({ command: "toggle", id });
         } catch (_) {
           ele.disabled = false;
         }
       }
     } else if (ele.classList.contains("tab")) {
       document.querySelectorAll(".tab").forEach(tab => {
         tab.classList.remove("active");
       });
       ele.classList.add("active");
     }
   });
 
-  redraw(info);
+  availablePatches = info;
+  redraw();
 });
 
 function onMessageFromAddon(msg) {
   if ("interventionsChanged" in msg) {
     redrawTable($("#interventions"), msg.interventionsChanged);
   }
 
   if ("overridesChanged" in msg) {
@@ -76,71 +85,87 @@ function onMessageFromAddon(msg) {
   }
 
   const id = msg.toggling || msg.toggled;
   const button = $(`[data-id="${id}"] button`);
   if (!button) {
     return;
   }
   const active = msg.active;
-  document.l10n.setAttributes(button,
-                      active ? "label-disable" : "label-enable");
+  document.l10n.setAttributes(
+    button,
+    active ? "label-disable" : "label-enable"
+  );
   button.disabled = !!msg.toggling;
 }
 
-function redraw(info) {
-  const {overrides, interventions} = info;
-  redrawTable($("#overrides"), overrides);
-  redrawTable($("#interventions"), interventions);
+function redraw() {
+  if (!availablePatches) {
+    return;
+  }
+  const { overrides, interventions } = availablePatches;
+  const showHidden = location.hash === "#all";
+  redrawTable($("#overrides"), overrides, showHidden);
+  redrawTable($("#interventions"), interventions, showHidden);
 }
 
-function redrawTable(table, data) {
+function redrawTable(table, data, showHidden = false) {
   const df = document.createDocumentFragment();
-  table.querySelectorAll("tr").forEach(tr => { tr.remove(); });
+  table.querySelectorAll("tr").forEach(tr => {
+    tr.remove();
+  });
 
   let noEntriesMessage;
   if (data === false) {
     noEntriesMessage = "text-disabled-in-about-config";
   } else if (data.length === 0) {
-    noEntriesMessage = table.id === "overrides" ? "text-no-overrides"
-                                                : "text-no-interventions";
+    noEntriesMessage =
+      table.id === "overrides" ? "text-no-overrides" : "text-no-interventions";
   }
 
   if (noEntriesMessage) {
     const tr = document.createElement("tr");
     df.appendChild(tr);
 
     const td = document.createElement("td");
     td.setAttribute("colspan", "3");
     document.l10n.setAttributes(td, noEntriesMessage);
     tr.appendChild(td);
 
     table.appendChild(df);
     return;
   }
 
   for (const row of data) {
+    if (row.hidden && !showHidden) {
+      continue;
+    }
+
     const tr = document.createElement("tr");
     tr.setAttribute("data-id", row.id);
     df.appendChild(tr);
 
     let td = document.createElement("td");
     td.innerText = row.domain;
     tr.appendChild(td);
 
     td = document.createElement("td");
     const a = document.createElement("a");
     const bug = row.bug;
     a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`;
-    document.l10n.setAttributes(a, "label-more-information", {bug});
+    document.l10n.setAttributes(a, "label-more-information", { bug });
     a.target = "_blank";
     td.appendChild(a);
     tr.appendChild(td);
 
     td = document.createElement("td");
     tr.appendChild(td);
     const button = document.createElement("button");
-    document.l10n.setAttributes(button,
-                        row.active ? "label-disable" : "label-enable");
+    document.l10n.setAttributes(
+      button,
+      row.active ? "label-disable" : "label-enable"
+    );
     td.appendChild(button);
   }
   table.appendChild(df);
 }
+
+window.onhashchange = redraw;
rename from browser/extensions/webcompat/aboutPage.js
rename to browser/extensions/webcompat/about-compat/aboutPage.js
--- a/browser/extensions/webcompat/aboutPage.js
+++ b/browser/extensions/webcompat/about-compat/aboutPage.js
@@ -1,32 +1,40 @@
 /* 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, Services, XPCOMUtils */
 
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(
+  this,
+  "Services",
+  "resource://gre/modules/Services.jsm"
+);
 
-XPCOMUtils.defineLazyServiceGetter(this, "resProto",
-                                   "@mozilla.org/network/protocol;1?name=resource",
-                                   "nsISubstitutingProtocolHandler");
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "resProto",
+  "@mozilla.org/network/protocol;1?name=resource",
+  "nsISubstitutingProtocolHandler"
+);
 
 const ResourceSubstitution = "webcompat";
 const ProcessScriptURL = "resource://webcompat/aboutPageProcessScript.js";
 
 this.aboutPage = class extends ExtensionAPI {
   onStartup() {
-    const {rootURI} = this.extension;
+    const { rootURI } = this.extension;
 
-    resProto.setSubstitution(ResourceSubstitution,
-                             Services.io.newURI("chrome/res/", null, rootURI));
+    resProto.setSubstitution(
+      ResourceSubstitution,
+      Services.io.newURI("about-compat/", null, rootURI)
+    );
 
     Services.ppmm.loadProcessScript(ProcessScriptURL, true);
   }
 
   onShutdown() {
     resProto.setSubstitution(ResourceSubstitution, null);
 
     Services.ppmm.removeDelayedProcessScript(ProcessScriptURL);
rename from browser/extensions/webcompat/aboutPage.json
rename to browser/extensions/webcompat/about-compat/aboutPage.json
rename from browser/extensions/webcompat/aboutPageProcessScript.js
rename to browser/extensions/webcompat/about-compat/aboutPageProcessScript.js
deleted file mode 100644
--- a/browser/extensions/webcompat/background.js
+++ /dev/null
@@ -1,92 +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";
-
-/* global browser, disableInjection, disableOverride, enableInjection,
-          enableOverride, InjectionsEnabled, UAOverridesEnabled */
-
-const UAOverrides = [];
-const Injections = [];
-
-function getOverrideOrInterventionById(id) {
-  for (const [type, things] of Object.entries({
-    overrides: UAOverrides,
-    interventions: Injections,
-  })) {
-    for (const what of things) {
-      if (what.id === id) {
-        return {type, what};
-      }
-    }
-  }
-  return {};
-}
-
-const portsToAboutCompatTabs = (function() {
-  const ports = new Set();
-
-  browser.runtime.onConnect.addListener(port => {
-    ports.add(port);
-    port.onDisconnect.addListener(function() {
-      ports.delete(port);
-    });
-  });
-
-  async function broadcast(message) {
-    for (const port of ports) {
-      port.postMessage(message);
-    }
-  }
-
-  return {broadcast};
-}());
-
-function filterOverrides(overrides) {
-  return overrides.filter(override => override.availableOnPlatform).map(override => {
-    const {id, active, bug, domain} = override;
-    return {id, active, bug, domain};
-  });
-}
-
-browser.runtime.onMessage.addListener(msg => {
-  switch (msg.command || msg) {
-    case "toggle": {
-      const id = msg.id;
-      const {type, what} = getOverrideOrInterventionById(id);
-      if (!what) {
-        return Promise.reject(`No such override or intervention to toggle: ${id}`);
-      }
-      portsToAboutCompatTabs.broadcast({toggling: id, active: what.active}).then(async () => {
-        switch (type) {
-          case "interventions": {
-            if (what.active) {
-              await disableInjection(what);
-            } else {
-              await enableInjection(what);
-            }
-            break;
-          }
-          case "overrides": {
-            if (what.active) {
-              await disableOverride(what);
-            } else {
-              await enableOverride(what);
-            }
-            break;
-          }
-        }
-        portsToAboutCompatTabs.broadcast({toggled: id, active: what.active});
-      });
-      break;
-    }
-    case "getOverridesAndInterventions": {
-      return Promise.resolve({
-        overrides: UAOverridesEnabled && filterOverrides(UAOverrides) || false,
-        interventions: InjectionsEnabled && filterOverrides(Injections) || false,
-      });
-    }
-  }
-  return undefined;
-});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/data/injections.js
@@ -0,0 +1,244 @@
+/* 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";
+
+/* globals module */
+
+/**
+ * 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 AVAILABLE_INJECTIONS = [
+  {
+    id: "testbed-injection",
+    platform: "all",
+    domain: "webcompat-addon-testbed.herokuapp.com",
+    bug: "0000000",
+    hidden: true,
+    contentScripts: {
+      matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
+      css: [
+        {
+          file: "injections/css/bug0000000-testbed-css-injection.css",
+        },
+      ],
+      js: [
+        {
+          file: "injections/js/bug0000000-testbed-js-injection.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1452707",
+    platform: "desktop",
+    domain: "ib.absa.co.za",
+    bug: "1452707",
+    contentScripts: {
+      matches: ["https://ib.absa.co.za/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1457335",
+    platform: "desktop",
+    domain: "histography.io",
+    bug: "1457335",
+    contentScripts: {
+      matches: ["*://histography.io/*"],
+      js: [
+        {
+          file: "injections/js/bug1457335-histography.io-ua-change.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1472075",
+    platform: "desktop",
+    domain: "bankofamerica.com",
+    bug: "1472075",
+    contentScripts: {
+      matches: ["*://*.bankofamerica.com/*"],
+      js: [
+        {
+          file: "injections/js/bug1472075-bankofamerica.com-ua-change.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1472081",
+    platform: "desktop",
+    domain: "election.gov.np",
+    bug: "1472081",
+    contentScripts: {
+      matches: ["http://202.166.205.141/bbvrs/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js",
+        },
+      ],
+      runAt: "document_start",
+      allFrames: true,
+    },
+  },
+  {
+    id: "bug1482066",
+    platform: "desktop",
+    domain: "portalminasnet.com",
+    bug: "1482066",
+    contentScripts: {
+      matches: ["*://portalminasnet.com/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js",
+        },
+      ],
+      runAt: "document_start",
+      allFrames: true,
+    },
+  },
+  {
+    id: "bug1526977",
+    platform: "desktop",
+    domain: "sreedharscce.in",
+    bug: "1526977",
+    contentScripts: {
+      matches: ["*://*.sreedharscce.in/authenticate"],
+      css: [
+        {
+          file: "injections/css/bug1526977-sreedharscce.in-login-fix.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1518781",
+    platform: "desktop",
+    domain: "twitch.tv",
+    bug: "1518781",
+    contentScripts: {
+      matches: ["*://*.twitch.tv/*"],
+      css: [
+        {
+          file: "injections/css/bug1518781-twitch.tv-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1551672",
+    platform: "android",
+    domain: "Sites using PDK 5 video",
+    bug: "1551672",
+    pdk5fix: {
+      urls: ["https://*/*/tpPdk.js", "https://*/*/pdk/js/*/*.js"],
+      types: ["script"],
+    },
+  },
+  {
+    id: "bug1305028",
+    platform: "desktop",
+    domain: "gaming.youtube.com",
+    bug: "1305028",
+    contentScripts: {
+      matches: ["*://gaming.youtube.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1432935-discord",
+    platform: "desktop",
+    domain: "discordapp.com",
+    bug: "1432935",
+    contentScripts: {
+      matches: ["*://discordapp.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1432935-breitbart",
+    platform: "desktop",
+    domain: "breitbart.com",
+    bug: "1432935",
+    contentScripts: {
+      matches: ["*://*.breitbart.com/*"],
+      css: [
+        {
+          file: "injections/css/bug1432935-breitbart.com-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1561371",
+    platform: "android",
+    domain: "mail.google.com",
+    bug: "1561371",
+    contentScripts: {
+      matches: ["*://mail.google.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1567610",
+    platform: "all",
+    domain: "dns.google.com",
+    bug: "1567610",
+    contentScripts: {
+      matches: ["*://dns.google.com/*"],
+      css: [
+        {
+          file: "injections/css/bug1567610-dns.google.com-moz-fit-content.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1568256",
+    platform: "android",
+    domain: "zertifikate.commerzbank.de",
+    bug: "1568256",
+    contentScripts: {
+      matches: ["*://*.zertifikate.commerzbank.de/webforms/mobile/*"],
+      css: [
+        {
+          file: "injections/css/bug1568256-zertifikate.commerzbank.de-flex.css",
+        },
+      ],
+    },
+  },
+];
+
+module.exports = AVAILABLE_INJECTIONS;
rename from mobile/android/extensions/webcompat/ua_overrides.js
rename to browser/extensions/webcompat/data/ua_overrides.js
--- a/mobile/android/extensions/webcompat/ua_overrides.js
+++ b/browser/extensions/webcompat/data/ua_overrides.js
@@ -1,48 +1,58 @@
+/* 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";
+
+/* globals module */
+
 /**
  * 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
  */
-
-"use strict";
-
-/* globals filterOverrides, portsToAboutCompatTabs, UAOverrides */
-
-let UAOverridesEnabled = true;
-
-for (const override of [
+const AVAILABLE_UA_OVERRIDES = [
   {
-    id: "testoverride",
+    id: "testbed-override",
     platform: "all",
-    domain: "webcompat-addon-testcases.schub.io",
-    bug: "1287966",
+    domain: "webcompat-addon-testbed.herokuapp.com",
+    bug: "0000000",
+    hidden: true,
     config: {
-      matches: ["*://webcompat-addon-testcases.schub.io/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36";
+      matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36 for WebCompat"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1563839 - rolb.santanderbank.com - Build UA override
      * WebCompat issue #33462 - https://webcompat.com/issues/33462
      *
      * santanderbank expects UA to have 'like Gecko', otherwise it runs
      * xmlDoc.onload whose support has been dropped. It results in missing labels in forms
      * and some other issues.  Adding 'like Gecko' fixes those issues.
      */
     id: "bug1563839",
     platform: "all",
     domain: "rolb.santanderbank.com",
     bug: "1563839",
     config: {
-      matches: ["*://*.santander.co.uk/*", "*://rolb.santanderbank.com/*"],
+      matches: [
+        "*://*.santander.co.uk/*",
+        "*://bob.santanderbank.com/*",
+        "*://rolb.santanderbank.com/*",
+      ],
       uaTransformer: originalUA => {
         return originalUA.replace("Gecko", "like Gecko");
       },
     },
   },
   {
     /*
      * Bug 1480710 - m.imgur.com - Build UA override
@@ -53,282 +63,265 @@ for (const override of [
      * receive the correct CSS and JS files.
      */
     id: "bug1480710",
     platform: "android",
     domain: "m.imgur.com",
     bug: "1480710",
     config: {
       matches: ["*://m.imgur.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * 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.
      */
     id: "bug945963",
     platform: "android",
     domain: "tieba.baidu.com",
     bug: "945963",
     config: {
       matches: ["*://tieba.baidu.com/*", "*://tiebac.baidu.com/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA + " AppleWebKit/537.36 (KHTML, like Gecko)";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
     id: "bug1177298-2",
     platform: "android",
     domain: "lohaco.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.lohaco.jp/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
     id: "bug1177298-3",
     platform: "android",
     domain: "nhk.or.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.nhk.or.jp/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA + " AppleWebKit";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1338260 - Add UA override for directTV
      * (Imported from ua-update.json.in)
      *
      * DirectTV has issues with scrolling and cut-off images. Pretending to be
      * Chrome for Android fixes those issues.
      */
     id: "bug1338260",
     platform: "android",
     domain: "directv.com",
     bug: "1338260",
     config: {
       matches: ["*://*.directv.com/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1385206 - Create UA override for rakuten.co.jp on Firefox Android
      * (Imported from ua-update.json.in)
      *
      * rakuten.co.jp serves a Desktop version if Firefox is included in the UA.
      */
     id: "bug1385206",
     platform: "android",
     domain: "rakuten.co.jp",
     bug: "1385206",
     config: {
       matches: ["*://*.rakuten.co.jp/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA.replace(/Firefox.+$/, "");
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 969844 - mobile.de sends desktop site to Firefox on Android
      *
      * mobile.de sends the desktop site to Fennec. Spooing as Chrome works fine.
      */
     id: "bug969844",
     platform: "android",
     domain: "mobile.de",
     bug: "969844",
     config: {
       matches: ["*://*.mobile.de/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509831 - cc.com - Add UA override for CC.com
      * WebCompat issue #329 - https://webcompat.com/issues/329
      *
      * ComedyCentral blocks Firefox for not being able to play HLS, which was
      * true in previous versions, but no longer is. With a spoofed Chrome UA,
      * the site works just fine.
      */
     id: "bug1509831",
     platform: "android",
     domain: "cc.com",
     bug: "1509831",
     config: {
       matches: ["*://*.cc.com/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1508516 - cineflix.com.br - Add UA override for cineflix.com.br/m/
      * WebCompat issue #21553 - https://webcompat.com/issues/21553
      *
      * The site renders a blank page with any Firefox snipped in the UA as it
      * is running into an exception. Spoofing as Chrome makes the site work
      * fine.
      */
     id: "bug1508516",
     platform: "android",
     domain: "cineflix.com.br",
     bug: "1508516",
     config: {
       matches: ["*://*.cineflix.com.br/m/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509852 - redbull.com - Add UA override for redbull.com
      * WebCompat issue #21439 - https://webcompat.com/issues/21439
      *
      * Redbull.com blocks some features, for example the live video player, for
      * Fennec. Spoofing as Chrome results in us rendering the video just fine,
      * and everything else works as well.
      */
     id: "bug1509852",
     platform: "android",
     domain: "redbull.com",
     bug: "1509852",
     config: {
       matches: ["*://*.redbull.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509873 - zmags.com - Add UA override for secure.viewer.zmags.com
      * WebCompat issue #21576 - https://webcompat.com/issues/21576
      *
      * The zmags viewer locks out Fennec with a "Browser unsupported" message,
      * but tests showed that it works just fine with a Chrome UA. Outreach
      * attempts were unsuccessful, and as the site has a relatively high rank,
      * we alter the UA.
      */
     id: "bug1509873",
     platform: "android",
     domain: "zmags.com",
     bug: "1509873",
     config: {
       matches: ["*://*.viewer.zmags.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
   },
-]) {
-  UAOverrides.push(override);
-}
-
-/* globals browser */
+  {
+    /*
+     * Bug 1566253 - posts.google.com - Add UA override for posts.google.com
+     * WebCompat issue #17870 - https://webcompat.com/issues/17870
+     *
+     * posts.google.com displaying "Your browser doesn't support this page".
+     * Spoofing as Chrome works fine.
+     */
+    id: "bug1566253",
+    platform: "android",
+    domain: "posts.google.com",
+    bug: "1566253",
+    config: {
+      matches: ["*://posts.google.com/*"],
+      uaTransformer: _ => {
+        return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G900M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36";
+      },
+    },
+  },
+  {
+    /*
+     * Bug 1567945 - Create UA override for beeg.com on Firefox Android
+     * WebCompat issue #16648 - https://webcompat.com/issues/16648
+     *
+     * beeg.com is hiding content of a page with video if Firefox exists in UA,
+     * replacing "Firefox" with an empty string makes the page load
+     */
+    id: "bug1567945",
+    platform: "android",
+    domain: "beeg.com",
+    bug: "1567945",
+    config: {
+      matches: ["*://beeg.com/*"],
+      uaTransformer: originalUA => {
+        return originalUA.replace(/Firefox.+$/, "");
+      },
+    },
+  },
+];
 
 const UAHelpers = {
   getPrefix(originalUA) {
     return originalUA.substr(0, originalUA.indexOf(")") + 1);
   },
 };
 
-const ActiveListeners = new Map();
-
-function enableOverride(override) {
-  if (override.active) {
-    return;
-  }
-
-  const {matches, uaTransformer} = override.config;
-  const listener = (details) => {
-    for (var header of details.requestHeaders) {
-      if (header.name.toLowerCase() === "user-agent") {
-        header.value = uaTransformer(header.value);
-      }
-    }
-    return {requestHeaders: details.requestHeaders};
-  };
-
-  browser.webRequest.onBeforeSendHeaders.addListener(
-    listener,
-    {urls: matches},
-    ["blocking", "requestHeaders"]
-  );
-
-  ActiveListeners.set(override, listener);
-  override.active = true;
-}
-
-async function registerUAOverrides() {
-  const platformMatches = ["all"];
-  let platformInfo = await browser.runtime.getPlatformInfo();
-  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
-
-  for (const override of UAOverrides) {
-    if (platformMatches.includes(override.platform)) {
-      override.availableOnPlatform = true;
-      enableOverride(override);
-    }
-  }
-  UAOverridesEnabled = true;
-  portsToAboutCompatTabs.broadcast({overridesChanged: filterOverrides(UAOverrides)});
-}
-
-function unregisterUAOverrides() {
-  for (const override of UAOverrides) {
-    disableOverride(override);
-  }
-  UAOverridesEnabled = false;
-  portsToAboutCompatTabs.broadcast({overridesChanged: false});
-}
-
-function disableOverride(override) {
-  if (!override.active) {
-    return;
-  }
-
-  browser.webRequest.onBeforeSendHeaders.removeListener(ActiveListeners.get(override));
-  override.active = false;
-  ActiveListeners.delete(override);
-}
-
-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();
+module.exports = AVAILABLE_UA_OVERRIDES;
rename from browser/extensions/webcompat/aboutConfigPrefs.js
rename to browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.js
rename from browser/extensions/webcompat/aboutConfigPrefs.json
rename to browser/extensions/webcompat/experiment-apis/aboutConfigPrefs.json
deleted file mode 100644
--- a/browser/extensions/webcompat/injections.js
+++ /dev/null
@@ -1,262 +0,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
- */
-
-"use strict";
-
-/* globals browser, filterOverrides, Injections, portsToAboutCompatTabs */
-
-let InjectionsEnabled = true;
-
-for (const injection of [
-  {
-    id: "testinjection",
-    platform: "all",
-    domain: "webcompat-addon-testcases.schub.io",
-    bug: "1287966",
-    contentScripts: {
-      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",
-    },
-  }, {
-    id: "bug1452707",
-    platform: "desktop",
-    domain: "ib.absa.co.za",
-    bug: "1452707",
-    contentScripts: {
-      matches: ["https://ib.absa.co.za/*"],
-      js: [{file: "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1457335",
-    platform: "desktop",
-    domain: "histography.io",
-    bug: "1457335",
-    contentScripts: {
-      matches: ["*://histography.io/*"],
-      js: [{file: "injections/js/bug1457335-histography.io-ua-change.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1472075",
-    platform: "desktop",
-    domain: "bankofamerica.com",
-    bug: "1472075",
-    contentScripts: {
-      matches: ["*://*.bankofamerica.com/*"],
-      js: [{file: "injections/js/bug1472075-bankofamerica.com-ua-change.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1472081",
-    platform: "desktop",
-    domain: "election.gov.np",
-    bug: "1472081",
-    contentScripts: {
-      matches: ["http://202.166.205.141/bbvrs/*"],
-      js: [{file: "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js"}],
-      runAt: "document_start",
-      allFrames: true,
-    },
-  }, {
-    id: "bug1482066",
-    platform: "desktop",
-    domain: "portalminasnet.com",
-    bug: "1482066",
-    contentScripts: {
-      matches: ["*://portalminasnet.com/*"],
-      js: [{file: "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js"}],
-      runAt: "document_start",
-      allFrames: true,
-    },
-  }, {
-    id: "bug1526977",
-    platform: "desktop",
-    domain: "sreedharscce.in",
-    bug: "1526977",
-    contentScripts: {
-      matches: ["*://*.sreedharscce.in/authenticate"],
-      css: [{file: "injections/css/bug1526977-sreedharscce.in-login-fix.css"}],
-    },
-  }, {
-    id: "bug1518781",
-    platform: "desktop",
-    domain: "twitch.tv",
-    bug: "1518781",
-    contentScripts: {
-      matches: ["*://*.twitch.tv/*"],
-      css: [{file: "injections/css/bug1518781-twitch.tv-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1551672",
-    platform: "android",
-    domain: "Sites using PDK 5 video",
-    bug: "1551672",
-    pdk5fix: {
-      urls: ["https://*/*/tpPdk.js", "https://*/*/pdk/js/*/*.js"],
-      types: ["script"],
-    },
-  }, {
-    id: "bug1305028",
-    platform: "desktop",
-    domain: "gaming.youtube.com",
-    bug: "1305028",
-    contentScripts: {
-      matches: ["*://gaming.youtube.com/*"],
-      css: [{file: "injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1432935-discord",
-    platform: "desktop",
-    domain: "discordapp.com",
-    bug: "1432935",
-    contentScripts: {
-      matches: ["*://discordapp.com/*"],
-      css: [{file: "injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css"}],
-    },
-  }, {
-    id: "bug1432935-breitbart",
-    platform: "desktop",
-    domain: "breitbart.com",
-    bug: "1432935",
-    contentScripts: {
-      matches: ["*://*.breitbart.com/*"],
-      css: [{file: "injections/css/bug1432935-breitbart.com-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1561371",
-    platform: "android",
-    domain: "mail.google.com",
-    bug: "1561371",
-    contentScripts: {
-      matches: ["*://mail.google.com/*"],
-      css: [{file: "injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css"}],
-    },
-  },
-]) {
-  Injections.push(injection);
-}
-
-let port = browser.runtime.connect();
-const ActiveInjections = new Map();
-
-async function registerContentScripts() {
-  const platformMatches = ["all"];
-  let platformInfo = await browser.runtime.getPlatformInfo();
-  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
-
-  for (const injection of Injections) {
-    if (platformMatches.includes(injection.platform)) {
-      injection.availableOnPlatform = true;
-      await enableInjection(injection);
-    }
-  }
-
-  InjectionsEnabled = true;
-  portsToAboutCompatTabs.broadcast({interventionsChanged: filterOverrides(Injections)});
-}
-
-function replaceStringInRequest(requestId, inString, outString, inEncoding = "utf-8") {
-  const filter = browser.webRequest.filterResponseData(requestId);
-  const decoder = new TextDecoder(inEncoding);
-  const encoder = new TextEncoder();
-  const RE = new RegExp(inString, "g");
-  const carryoverLength = inString.length;
-  let carryover = "";
-  filter.ondata = event => {
-    const replaced = (carryover + decoder.decode(event.data, {stream: true})).replace(RE, outString);
-    filter.write(encoder.encode(replaced.slice(0, -carryoverLength)));
-    carryover = replaced.slice(-carryoverLength);
-  };
-  filter.onstop = event => {
-    if (carryover.length) {
-      filter.write(encoder.encode(carryover));
-    }
-    filter.close();
-  };
-}
-
-async function enableInjection(injection) {
-  if (injection.active) {
-    return;
-  }
-
-  if ("pdk5fix" in injection) {
-    const {urls, types} = injection.pdk5fix;
-    const listener = injection.pdk5fix.listener = ({requestId}) => {
-      replaceStringInRequest(requestId, "VideoContextChromeAndroid", "VideoContextAndroid");
-      return {};
-    };
-    browser.webRequest.onBeforeRequest.addListener(listener, {urls, types}, ["blocking"]);
-    injection.active = true;
-    return;
-  }
-
-  try {
-    const handle = await browser.contentScripts.register(injection.contentScripts);
-    ActiveInjections.set(injection, handle);
-    injection.active = true;
-  } catch (ex) {
-    console.error("Registering WebCompat GoFaster content scripts failed: ", ex);
-  }
-}
-
-function unregisterContentScripts() {
-  for (const injection of Injections) {
-    disableInjection(injection);
-  }
-  InjectionsEnabled = false;
-  portsToAboutCompatTabs.broadcast({interventionsChanged: false});
-}
-
-async function disableInjection(injection) {
-  if (!injection.active) {
-    return;
-  }
-
-  if (injection.pdk5fix) {
-    const {listener} = injection.pdk5fix;
-    browser.webRequest.onBeforeRequest.removeListener(listener);
-    injection.active = false;
-    delete injection.pdk5fix.listener;
-    return;
-  }
-
-  const contentScript = ActiveInjections.get(injection);
-  await contentScript.unregister();
-  ActiveInjections.delete(injection);
-  injection.active = false;
-}
-
-port.onMessage.addListener((message) => {
-  switch (message.type) {
-    case "injection-pref-changed":
-      if (message.prefState) {
-        registerContentScripts();
-      } else {
-        unregisterContentScripts();
-      }
-      break;
-  }
-});
-
-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 browser/extensions/webcompat/injections/css/bug0000000-dummy-css-injection.css
rename to browser/extensions/webcompat/injections/css/bug0000000-testbed-css-injection.css
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/injections/css/bug1567610-dns.google.com-moz-fit-content.css
@@ -0,0 +1,12 @@
+/**
+ * dns.google.com - Page content is shifted to the left side of the page
+ * Bug #1567610 - https://bugzilla.mozilla.org/show_bug.cgi?id=1567610
+ * WebCompat issue #22494 - https://webcompat.com/issues/22494
+ *
+ * Affected element is styled with width:fit-content; which is not
+ * supported by Firefox yet, see https://bugzilla.mozilla.org/show_bug.cgi?id=1495868
+ * Adding -moz-fit-content fixes the issue
+ */
+main > .ng-star-inserted > .centered {
+  width: -moz-fit-content;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/injections/css/bug1568256-zertifikate.commerzbank.de-flex.css
@@ -0,0 +1,12 @@
+/**
+ * zertifikate.commerzbank.de - clickable elements on the page are collapsed
+ * Bug #1568256 - https://bugzilla.mozilla.org/show_bug.cgi?id=1568256
+ * WebCompat issue #9102 - https://webcompat.com/issues/9102
+ *
+ * Affected elements have display:-webkit-box and display:flex applied, however,
+ * listed in wrong order, so display:-webkit-box is becoming the final say.
+ * Adding display: flex for those elements fixes the issue
+ */
+.x-layout-box {
+  display: flex !important;
+}
rename from browser/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
rename to browser/extensions/webcompat/injections/js/bug0000000-testbed-js-injection.js
deleted file mode 100644
--- a/browser/extensions/webcompat/jar.mn
+++ /dev/null
@@ -1,7 +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/.
-
-[features/webcompat@mozilla.org] chrome.jar:
-  res/AboutCompat.jsm (AboutCompat.jsm)
-  res/aboutPageProcessScript.js (aboutPageProcessScript.js)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/lib/about_compat_broker.js
@@ -0,0 +1,123 @@
+/* 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 browser, module */
+
+class AboutCompatBroker {
+  constructor(bindings) {
+    this.portsToAboutCompatTabs = this.buildPorts();
+
+    this._injections = bindings.injections;
+    this._injections.bindAboutCompatBroker(this);
+
+    this._uaOverrides = bindings.uaOverrides;
+    this._uaOverrides.bindAboutCompatBroker(this);
+  }
+
+  buildPorts() {
+    const ports = new Set();
+
+    browser.runtime.onConnect.addListener(port => {
+      ports.add(port);
+      port.onDisconnect.addListener(function() {
+        ports.delete(port);
+      });
+    });
+
+    async function broadcast(message) {
+      for (const port of ports) {
+        port.postMessage(message);
+      }
+    }
+
+    return { broadcast };
+  }
+
+  filterOverrides(overrides) {
+    return overrides
+      .filter(override => override.availableOnPlatform)
+      .map(override => {
+        const { id, active, bug, domain, hidden } = override;
+        return { id, active, bug, domain, hidden };
+      });
+  }
+
+  getOverrideOrInterventionById(id) {
+    for (const [type, things] of Object.entries({
+      overrides: this._uaOverrides.getAvailableOverrides(),
+      interventions: this._injections.getAvailableInjections(),
+    })) {
+      for (const what of things) {
+        if (what.id === id) {
+          return { type, what };
+        }
+      }
+    }
+    return {};
+  }
+
+  bootup() {
+    browser.runtime.onMessage.addListener(msg => {
+      switch (msg.command || msg) {
+        case "toggle": {
+          const id = msg.id;
+          const { type, what } = this.getOverrideOrInterventionById(id);
+          if (!what) {
+            return Promise.reject(
+              `No such override or intervention to toggle: ${id}`
+            );
+          }
+          this.portsToAboutCompatTabs
+            .broadcast({ toggling: id, active: what.active })
+            .then(async () => {
+              switch (type) {
+                case "interventions": {
+                  if (what.active) {
+                    await this._injections.disableInjection(what);
+                  } else {
+                    await this._injections.enableInjection(what);
+                  }
+                  break;
+                }
+                case "overrides": {
+                  if (what.active) {
+                    await this._uaOverrides.disableOverride(what);
+                  } else {
+                    await this._uaOverrides.enableOverride(what);
+                  }
+                  break;
+                }
+              }
+              this.portsToAboutCompatTabs.broadcast({
+                toggled: id,
+                active: what.active,
+              });
+            });
+          break;
+        }
+        case "getOverridesAndInterventions": {
+          return Promise.resolve({
+            overrides:
+              (this._uaOverrides.isEnabled() &&
+                this.filterOverrides(
+                  this._uaOverrides.getAvailableOverrides()
+                )) ||
+              false,
+            interventions:
+              (this._injections.isEnabled() &&
+                this.filterOverrides(
+                  this._injections.getAvailableInjections()
+                )) ||
+              false,
+          });
+        }
+      }
+      return undefined;
+    });
+  }
+}
+
+module.exports = AboutCompatBroker;
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/lib/injections.js
@@ -0,0 +1,163 @@
+/* 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";
+
+/* globals browser, module */
+
+class Injections {
+  constructor(availableInjections) {
+    this.INJECTION_PREF = "perform_injections";
+
+    this._injectionsEnabled = true;
+
+    this._availableInjections = availableInjections;
+    this._activeInjections = new Map();
+  }
+
+  bindAboutCompatBroker(broker) {
+    this._aboutCompatBroker = broker;
+  }
+
+  bootup() {
+    browser.aboutConfigPrefs.onPrefChange.addListener(() => {
+      this.checkInjectionPref();
+    }, this.INJECTION_PREF);
+    this.checkInjectionPref();
+  }
+
+  checkInjectionPref() {
+    browser.aboutConfigPrefs.getPref(this.INJECTION_PREF).then(value => {
+      if (value === undefined) {
+        browser.aboutConfigPrefs.setPref(this.INJECTION_PREF, true);
+      } else if (value === false) {
+        this.unregisterContentScripts();
+      } else {
+        this.registerContentScripts();
+      }
+    });
+  }
+
+  getAvailableInjections() {
+    return this._availableInjections;
+  }
+
+  isEnabled() {
+    return this._injectionsEnabled;
+  }
+
+  async registerContentScripts() {
+    const platformMatches = ["all"];
+    let platformInfo = await browser.runtime.getPlatformInfo();
+    platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+    for (const injection of this._availableInjections) {
+      if (platformMatches.includes(injection.platform)) {
+        injection.availableOnPlatform = true;
+        await this.enableInjection(injection);
+      }
+    }
+
+    this._injectionsEnabled = true;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      interventionsChanged: this._aboutCompatBroker.filterOverrides(
+        this._availableInjections
+      ),
+    });
+  }
+
+  replaceStringInRequest(requestId, inString, outString, inEncoding = "utf-8") {
+    const filter = browser.webRequest.filterResponseData(requestId);
+    const decoder = new TextDecoder(inEncoding);
+    const encoder = new TextEncoder();
+    const RE = new RegExp(inString, "g");
+    const carryoverLength = inString.length;
+    let carryover = "";
+
+    filter.ondata = event => {
+      const replaced = (
+        carryover + decoder.decode(event.data, { stream: true })
+      ).replace(RE, outString);
+      filter.write(encoder.encode(replaced.slice(0, -carryoverLength)));
+      carryover = replaced.slice(-carryoverLength);
+    };
+
+    filter.onstop = event => {
+      if (carryover.length) {
+        filter.write(encoder.encode(carryover));
+      }
+      filter.close();
+    };
+  }
+
+  async enableInjection(injection) {
+    if (injection.active) {
+      return;
+    }
+
+    if ("pdk5fix" in injection) {
+      const { urls, types } = injection.pdk5fix;
+      const listener = (injection.pdk5fix.listener = ({ requestId }) => {
+        this.replaceStringInRequest(
+          requestId,
+          "VideoContextChromeAndroid",
+          "VideoContextAndroid"
+        );
+        return {};
+      });
+      browser.webRequest.onBeforeRequest.addListener(
+        listener,
+        { urls, types },
+        ["blocking"]
+      );
+      injection.active = true;
+      return;
+    }
+
+    try {
+      const handle = await browser.contentScripts.register(
+        injection.contentScripts
+      );
+      this._activeInjections.set(injection, handle);
+      injection.active = true;
+    } catch (ex) {
+      console.error(
+        "Registering WebCompat GoFaster content scripts failed: ",
+        ex
+      );
+    }
+  }
+
+  unregisterContentScripts() {
+    for (const injection of this._availableInjections) {
+      this.disableInjection(injection);
+    }
+
+    this._injectionsEnabled = false;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      interventionsChanged: false,
+    });
+  }
+
+  async disableInjection(injection) {
+    if (!injection.active) {
+      return;
+    }
+
+    if (injection.pdk5fix) {
+      const { listener } = injection.pdk5fix;
+      browser.webRequest.onBeforeRequest.removeListener(listener);
+      injection.active = false;
+      delete injection.pdk5fix.listener;
+      return;
+    }
+
+    const contentScript = this._activeInjections.get(injection);
+    await contentScript.unregister();
+    this._activeInjections.delete(injection);
+    injection.active = false;
+  }
+}
+
+module.exports = Injections;
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/lib/module_shim.js
@@ -0,0 +1,24 @@
+/* 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";
+
+/**
+ * We cannot yet use proper JS modules within webextensions, as support for them
+ * is highly experimental and highly instable. So we end up just including all
+ * the JS files we need as separate background scripts, and since they all are
+ * executed within the same context, this works for our in-browser deployment.
+ *
+ * However, this code is tracked outside of mozilla-central, and we work on
+ * shipping this code in other products, like android-components as well.
+ * Because of that, we have automated tests running within that repository. To
+ * make our lives easier, we add `module.exports` statements to the JS source
+ * files, so we can easily import their contents into our NodeJS-based test
+ * suite.
+ *
+ * This works fine, but obviously, `module` is not defined when running
+ * in-browser. So let's use this empty object as a shim, so we don't run into
+ * runtime exceptions because of that.
+ */
+var module = {};
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/lib/ua_overrides.js
@@ -0,0 +1,119 @@
+/* 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";
+
+/* globals browser, module */
+
+class UAOverrides {
+  constructor(availableOverrides) {
+    this.OVERRIDE_PREF = "perform_ua_overrides";
+
+    this._overridesEnabled = true;
+
+    this._availableOverrides = availableOverrides;
+    this._activeListeners = new Map();
+  }
+
+  bindAboutCompatBroker(broker) {
+    this._aboutCompatBroker = broker;
+  }
+
+  bootup() {
+    browser.aboutConfigPrefs.onPrefChange.addListener(() => {
+      this.checkOverridePref();
+    }, this.OVERRIDE_PREF);
+    this.checkOverridePref();
+  }
+
+  checkOverridePref() {
+    browser.aboutConfigPrefs.getPref(this.OVERRIDE_PREF).then(value => {
+      if (value === undefined) {
+        browser.aboutConfigPrefs.setPref(this.OVERRIDE_PREF, true);
+      } else if (value === false) {
+        this.unregisterUAOverrides();
+      } else {
+        this.registerUAOverrides();
+      }
+    });
+  }
+
+  getAvailableOverrides() {
+    return this._availableOverrides;
+  }
+
+  isEnabled() {
+    return this._overridesEnabled;
+  }
+
+  enableOverride(override) {
+    if (override.active) {
+      return;
+    }
+
+    const { matches, uaTransformer } = override.config;
+    const listener = details => {
+      for (const header of details.requestHeaders) {
+        if (header.name.toLowerCase() === "user-agent") {
+          header.value = uaTransformer(header.value);
+        }
+      }
+      return { requestHeaders: details.requestHeaders };
+    };
+
+    browser.webRequest.onBeforeSendHeaders.addListener(
+      listener,
+      { urls: matches },
+      ["blocking", "requestHeaders"]
+    );
+
+    this._activeListeners.set(override, listener);
+    override.active = true;
+  }
+
+  async registerUAOverrides() {
+    const platformMatches = ["all"];
+    let platformInfo = await browser.runtime.getPlatformInfo();
+    platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+    for (const override of this._availableOverrides) {
+      if (platformMatches.includes(override.platform)) {
+        override.availableOnPlatform = true;
+        this.enableOverride(override);
+      }
+    }
+
+    this._overridesEnabled = true;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      overridesChanged: this._aboutCompatBroker.filterOverrides(
+        this._availableOverrides
+      ),
+    });
+  }
+
+  unregisterUAOverrides() {
+    for (const override of this._availableOverrides) {
+      this.disableOverride(override);
+    }
+
+    this._overridesEnabled = false;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      overridesChanged: false,
+    });
+  }
+
+  disableOverride(override) {
+    if (!override.active) {
+      return;
+    }
+
+    browser.webRequest.onBeforeSendHeaders.removeListener(
+      this._activeListeners.get(override)
+    );
+    override.active = false;
+    this._activeListeners.delete(override);
+  }
+}
+
+module.exports = UAOverrides;
--- a/browser/extensions/webcompat/manifest.json
+++ b/browser/extensions/webcompat/manifest.json
@@ -1,48 +1,52 @@
 {
   "manifest_version": 2,
   "name": "Web Compat",
   "description": "Urgent post-release fixes for web compatibility.",
-  "version": "4.4.0",
+  "version": "5.0.2",
 
   "applications": {
     "gecko": {
       "id": "webcompat@mozilla.org",
       "strict_min_version": "59.0b5"
     }
   },
 
   "experiment_apis": {
     "aboutConfigPrefs": {
-      "schema": "aboutConfigPrefs.json",
+      "schema": "experiment-apis/aboutConfigPrefs.json",
       "parent": {
         "scopes": ["addon_parent"],
-        "script": "aboutConfigPrefs.js",
+        "script": "experiment-apis/aboutConfigPrefs.js",
         "paths": [["aboutConfigPrefs"]]
       }
     },
     "aboutPage": {
-      "schema": "aboutPage.json",
+      "schema": "about-compat/aboutPage.json",
       "parent": {
         "scopes": ["addon_parent"],
-        "script": "aboutPage.js",
+        "script": "about-compat/aboutPage.js",
         "events": ["startup"]
       }
     }
   },
 
-  "content_security_policy": "script-src 'self' 'sha256-HbSjs39Y0thRGfO3RHrNzLPKyC/tq6FdIuP3jEBAcJQ='; default-src 'self'; base-uri moz-extension://*;",
+  "content_security_policy": "script-src 'self' 'sha256-MmZkN2QaIHhfRWPZ8TVRjijTn5Ci1iEabtTEWrt9CCo='; default-src 'self'; base-uri moz-extension://*;",
 
   "permissions": [
     "webRequest",
     "webRequestBlocking",
     "<all_urls>"
   ],
 
   "background": {
     "scripts": [
-      "background.js",
-      "injections.js",
-      "ua_overrides.js"
+      "lib/module_shim.js",
+      "data/injections.js",
+      "data/ua_overrides.js",
+      "lib/about_compat_broker.js",
+      "lib/injections.js",
+      "lib/ua_overrides.js",
+      "run.js"
     ]
   }
 }
--- a/browser/extensions/webcompat/moz.build
+++ b/browser/extensions/webcompat/moz.build
@@ -3,44 +3,62 @@
 # 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'] += [
-  'aboutCompat.css',
-  'aboutCompat.html',
-  'aboutCompat.js',
-  'aboutConfigPrefs.js',
-  'aboutConfigPrefs.json',
-  'aboutPage.js',
-  'aboutPage.json',
-  'background.js',
-  'injections.js',
   'manifest.json',
-  'ua_overrides.js'
+  'run.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['about-compat'] += [
+  'about-compat/aboutCompat.css',
+  'about-compat/aboutCompat.html',
+  'about-compat/aboutCompat.js',
+  'about-compat/AboutCompat.jsm',
+  'about-compat/aboutPage.js',
+  'about-compat/aboutPage.json',
+  'about-compat/aboutPageProcessScript.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['data'] += [
+  'data/injections.js',
+  'data/ua_overrides.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['experiment-apis'] += [
+  'experiment-apis/aboutConfigPrefs.js',
+  'experiment-apis/aboutConfigPrefs.json'
 ]
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['css'] += [
-  'injections/css/bug0000000-dummy-css-injection.css',
+  'injections/css/bug0000000-testbed-css-injection.css',
   'injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css',
   'injections/css/bug1432935-breitbart.com-webkit-scrollbar.css',
   'injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css',
   'injections/css/bug1518781-twitch.tv-webkit-scrollbar.css',
   'injections/css/bug1526977-sreedharscce.in-login-fix.css',
-  'injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css'
+  'injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css',
+  'injections/css/bug1567610-dns.google.com-moz-fit-content.css',
+  'injections/css/bug1568256-zertifikate.commerzbank.de-flex.css'
 ]
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['js'] += [
-  'injections/js/bug0000000-dummy-js-injection.js',
+  'injections/js/bug0000000-testbed-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'
 ]
 
-JAR_MANIFESTS += ['jar.mn']
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['lib'] += [
+  'lib/about_compat_broker.js',
+  'lib/injections.js',
+  'lib/module_shim.js',
+  'lib/ua_overrides.js'
+]
 
 with Files('**'):
   BUG_COMPONENT = ('Web Compatibility Tools', 'Go Faster')
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/run.js
@@ -0,0 +1,20 @@
+/* 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";
+
+/* globals AVAILABLE_INJECTIONS, AVAILABLE_UA_OVERRIDES, AboutCompatBroker,
+           Injections, UAOverrides */
+
+const injections = new Injections(AVAILABLE_INJECTIONS);
+const uaOverrides = new UAOverrides(AVAILABLE_UA_OVERRIDES);
+
+const aboutCompatBroker = new AboutCompatBroker({
+  injections,
+  uaOverrides,
+});
+
+aboutCompatBroker.bootup();
+injections.bootup();
+uaOverrides.bootup();
rename from browser/extensions/webcompat/AboutCompat.jsm
rename to mobile/android/extensions/webcompat/about-compat/AboutCompat.jsm
--- a/browser/extensions/webcompat/AboutCompat.jsm
+++ b/mobile/android/extensions/webcompat/about-compat/AboutCompat.jsm
@@ -1,31 +1,37 @@
 /* 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";
 
 var EXPORTED_SYMBOLS = ["AboutCompat"];
 
-const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const addonID = "webcompat@mozilla.org";
-const addonPageRelativeURL = "/aboutCompat.html";
+const addonPageRelativeURL = "/about-compat/aboutCompat.html";
 
 function AboutCompat() {
-  this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(addonPageRelativeURL);
+  this.chromeURL = WebExtensionPolicy.getByID(addonID).getURL(
+    addonPageRelativeURL
+  );
 }
 AboutCompat.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIAboutModule]),
   getURIFlags() {
     return Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS;
   },
 
   newChannel(aURI, aLoadInfo) {
     const uri = Services.io.newURI(this.chromeURL);
     const channel = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
     channel.originalURI = aURI;
 
-    channel.owner = Services.scriptSecurityManager.createCodebasePrincipal(uri, aLoadInfo.originAttributes);
+    channel.owner = (Services.scriptSecurityManager.createContentPrincipal ||
+      Services.scriptSecurityManager.createCodebasePrincipal)(
+      uri,
+      aLoadInfo.originAttributes
+    );
     return channel;
   },
 };
rename from browser/extensions/webcompat/aboutCompat.css
rename to mobile/android/extensions/webcompat/about-compat/aboutCompat.css
--- a/browser/extensions/webcompat/aboutCompat.css
+++ b/mobile/android/extensions/webcompat/about-compat/aboutCompat.css
@@ -64,21 +64,21 @@
     background-color: var(--in-content-box-background-hover) !important;
     text-decoration: none;
   }
 
   h2.lighter-font-weight {
     font-weight: lighter;
   }
 
-  html[dir=ltr] th {
+  html[dir="ltr"] th {
     text-align: left;
   }
 
-  html[dir=rtl] th {
+  html[dir="rtl"] th {
     text-align: right;
   }
 }
 
 @media (any-pointer: coarse), (any-pointer: none) {
   * {
     margin: 0;
     padding: 0;
@@ -86,17 +86,19 @@
 
   html {
     font-family: sans-serif;
     font-size: 14px;
     -moz-text-size-adjust: none;
     background-color: #f5f5f5;
   }
 
-  table, tr, p {
+  table,
+  tr,
+  p {
     display: block;
     background: #fff;
   }
 
   table {
     border-top: 2px solid #0a84ff;
     margin-top: -2px;
     position: absolute;
rename from mobile/android/extensions/webcompat/aboutCompat.html
rename to mobile/android/extensions/webcompat/about-compat/aboutCompat.html
--- a/mobile/android/extensions/webcompat/aboutCompat.html
+++ b/mobile/android/extensions/webcompat/about-compat/aboutCompat.html
@@ -1,23 +1,25 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <base/>
+
   <!-- If you change this script tag you must update the hash in the extension's
-         `content_security_policy` 'sha256-HbSjs39Y0thRGfO3RHrNzLPKyC/tq6FdIuP3jEBAcJQ=' -->
-  <script>document.head.firstElementChild.href = browser.runtime.getURL("");</script>
+         `content_security_policy` 'sha256-MmZkN2QaIHhfRWPZ8TVRjijTn5Ci1iEabtTEWrt9CCo=' -->
+  <script>/* globals browser */ document.head.firstElementChild.href = browser.runtime.getURL("");</script>
+
   <meta charset="utf-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  <link rel="stylesheet" href="aboutCompat.css" />
+  <link rel="stylesheet" href="about-compat/aboutCompat.css" />
   <link rel="stylesheet" media="screen and (pointer:fine), projection" type="text/css"
           href="chrome://global/skin/in-content/common.css"/>
   <link rel="localization" href="toolkit/about/aboutCompat.ftl"/>
   <title data-l10n-id="text-title"></title>
-  <script src="aboutCompat.js"></script>
+  <script src="about-compat/aboutCompat.js"></script>
   </head>
 <body>
   <h2 class="tab active" data-l10n-id="label-overrides"></h2>
   <table id="overrides">
     <col/>
     <col/>
     <col/>
   </table>
rename from browser/extensions/webcompat/aboutCompat.js
rename to mobile/android/extensions/webcompat/about-compat/aboutCompat.js
--- a/browser/extensions/webcompat/aboutCompat.js
+++ b/mobile/android/extensions/webcompat/about-compat/aboutCompat.js
@@ -1,74 +1,83 @@
 /* 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";
 
 /* globals browser */
 
+let availablePatches;
+
 const portToAddon = (function() {
   let port;
 
   function connect() {
-    port = browser.runtime.connect({name: "AboutCompatTab"});
+    port = browser.runtime.connect({ name: "AboutCompatTab" });
     port.onMessage.addListener(onMessageFromAddon);
     port.onDisconnect.addListener(e => {
       port = undefined;
     });
   }
 
   connect();
 
   async function send(message) {
     if (port) {
       return port.postMessage(message);
     }
     return Promise.reject("background script port disconnected");
   }
 
-  return {send};
-}());
+  return { send };
+})();
 
-const $ = function(sel) { return document.querySelector(sel); };
+const $ = function(sel) {
+  return document.querySelector(sel);
+};
 
 const DOMContentLoadedPromise = new Promise(resolve => {
-  document.addEventListener("DOMContentLoaded", () => {
-    resolve();
-  }, {once: true});
+  document.addEventListener(
+    "DOMContentLoaded",
+    () => {
+      resolve();
+    },
+    { once: true }
+  );
 });
 
 Promise.all([
   browser.runtime.sendMessage("getOverridesAndInterventions"),
   DOMContentLoadedPromise,
 ]).then(([info]) => {
   document.body.addEventListener("click", async evt => {
     const ele = evt.target;
     if (ele.nodeName === "BUTTON") {
       const row = ele.closest("[data-id]");
       if (row) {
         evt.preventDefault();
         ele.disabled = true;
         const id = row.getAttribute("data-id");
         try {
-          await browser.runtime.sendMessage({command: "toggle", id});
+          await browser.runtime.sendMessage({ command: "toggle", id });
         } catch (_) {
           ele.disabled = false;
         }
       }
     } else if (ele.classList.contains("tab")) {
       document.querySelectorAll(".tab").forEach(tab => {
         tab.classList.remove("active");
       });
       ele.classList.add("active");
     }
   });
 
-  redraw(info);
+  availablePatches = info;
+  redraw();
 });
 
 function onMessageFromAddon(msg) {
   if ("interventionsChanged" in msg) {
     redrawTable($("#interventions"), msg.interventionsChanged);
   }
 
   if ("overridesChanged" in msg) {
@@ -76,71 +85,87 @@ function onMessageFromAddon(msg) {
   }
 
   const id = msg.toggling || msg.toggled;
   const button = $(`[data-id="${id}"] button`);
   if (!button) {
     return;
   }
   const active = msg.active;
-  document.l10n.setAttributes(button,
-                      active ? "label-disable" : "label-enable");
+  document.l10n.setAttributes(
+    button,
+    active ? "label-disable" : "label-enable"
+  );
   button.disabled = !!msg.toggling;
 }
 
-function redraw(info) {
-  const {overrides, interventions} = info;
-  redrawTable($("#overrides"), overrides);
-  redrawTable($("#interventions"), interventions);
+function redraw() {
+  if (!availablePatches) {
+    return;
+  }
+  const { overrides, interventions } = availablePatches;
+  const showHidden = location.hash === "#all";
+  redrawTable($("#overrides"), overrides, showHidden);
+  redrawTable($("#interventions"), interventions, showHidden);
 }
 
-function redrawTable(table, data) {
+function redrawTable(table, data, showHidden = false) {
   const df = document.createDocumentFragment();
-  table.querySelectorAll("tr").forEach(tr => { tr.remove(); });
+  table.querySelectorAll("tr").forEach(tr => {
+    tr.remove();
+  });
 
   let noEntriesMessage;
   if (data === false) {
     noEntriesMessage = "text-disabled-in-about-config";
   } else if (data.length === 0) {
-    noEntriesMessage = table.id === "overrides" ? "text-no-overrides"
-                                                : "text-no-interventions";
+    noEntriesMessage =
+      table.id === "overrides" ? "text-no-overrides" : "text-no-interventions";
   }
 
   if (noEntriesMessage) {
     const tr = document.createElement("tr");
     df.appendChild(tr);
 
     const td = document.createElement("td");
     td.setAttribute("colspan", "3");
     document.l10n.setAttributes(td, noEntriesMessage);
     tr.appendChild(td);
 
     table.appendChild(df);
     return;
   }
 
   for (const row of data) {
+    if (row.hidden && !showHidden) {
+      continue;
+    }
+
     const tr = document.createElement("tr");
     tr.setAttribute("data-id", row.id);
     df.appendChild(tr);
 
     let td = document.createElement("td");
     td.innerText = row.domain;
     tr.appendChild(td);
 
     td = document.createElement("td");
     const a = document.createElement("a");
     const bug = row.bug;
     a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`;
-    document.l10n.setAttributes(a, "label-more-information", {bug});
+    document.l10n.setAttributes(a, "label-more-information", { bug });
     a.target = "_blank";
     td.appendChild(a);
     tr.appendChild(td);
 
     td = document.createElement("td");
     tr.appendChild(td);
     const button = document.createElement("button");
-    document.l10n.setAttributes(button,
-                        row.active ? "label-disable" : "label-enable");
+    document.l10n.setAttributes(
+      button,
+      row.active ? "label-disable" : "label-enable"
+    );
     td.appendChild(button);
   }
   table.appendChild(df);
 }
+
+window.onhashchange = redraw;
rename from mobile/android/extensions/webcompat/aboutPage.js
rename to mobile/android/extensions/webcompat/about-compat/aboutPage.js
--- a/mobile/android/extensions/webcompat/aboutPage.js
+++ b/mobile/android/extensions/webcompat/about-compat/aboutPage.js
@@ -1,32 +1,40 @@
 /* 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, Services, XPCOMUtils */
 
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
+ChromeUtils.defineModuleGetter(
+  this,
+  "Services",
+  "resource://gre/modules/Services.jsm"
+);
 
-XPCOMUtils.defineLazyServiceGetter(this, "resProto",
-                                   "@mozilla.org/network/protocol;1?name=resource",
-                                   "nsISubstitutingProtocolHandler");
+XPCOMUtils.defineLazyServiceGetter(
+  this,
+  "resProto",
+  "@mozilla.org/network/protocol;1?name=resource",
+  "nsISubstitutingProtocolHandler"
+);
 
 const ResourceSubstitution = "webcompat";
 const ProcessScriptURL = "resource://webcompat/aboutPageProcessScript.js";
 
 this.aboutPage = class extends ExtensionAPI {
   onStartup() {
-    const {rootURI} = this.extension;
+    const { rootURI } = this.extension;
 
-    resProto.setSubstitution(ResourceSubstitution,
-                             Services.io.newURI("chrome/res/", null, rootURI));
+    resProto.setSubstitution(
+      ResourceSubstitution,
+      Services.io.newURI("about-compat/", null, rootURI)
+    );
 
     Services.ppmm.loadProcessScript(ProcessScriptURL, true);
   }
 
   onShutdown() {
     resProto.setSubstitution(ResourceSubstitution, null);
 
     Services.ppmm.removeDelayedProcessScript(ProcessScriptURL);
rename from mobile/android/extensions/webcompat/aboutPage.json
rename to mobile/android/extensions/webcompat/about-compat/aboutPage.json
rename from mobile/android/extensions/webcompat/aboutPageProcessScript.js
rename to mobile/android/extensions/webcompat/about-compat/aboutPageProcessScript.js
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/background.js
+++ /dev/null
@@ -1,92 +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";
-
-/* global browser, disableInjection, disableOverride, enableInjection,
-          enableOverride, InjectionsEnabled, UAOverridesEnabled */
-
-const UAOverrides = [];
-const Injections = [];
-
-function getOverrideOrInterventionById(id) {
-  for (const [type, things] of Object.entries({
-    overrides: UAOverrides,
-    interventions: Injections,
-  })) {
-    for (const what of things) {
-      if (what.id === id) {
-        return {type, what};
-      }
-    }
-  }
-  return {};
-}
-
-const portsToAboutCompatTabs = (function() {
-  const ports = new Set();
-
-  browser.runtime.onConnect.addListener(port => {
-    ports.add(port);
-    port.onDisconnect.addListener(function() {
-      ports.delete(port);
-    });
-  });
-
-  async function broadcast(message) {
-    for (const port of ports) {
-      port.postMessage(message);
-    }
-  }
-
-  return {broadcast};
-}());
-
-function filterOverrides(overrides) {
-  return overrides.filter(override => override.availableOnPlatform).map(override => {
-    const {id, active, bug, domain} = override;
-    return {id, active, bug, domain};
-  });
-}
-
-browser.runtime.onMessage.addListener(msg => {
-  switch (msg.command || msg) {
-    case "toggle": {
-      const id = msg.id;
-      const {type, what} = getOverrideOrInterventionById(id);
-      if (!what) {
-        return Promise.reject(`No such override or intervention to toggle: ${id}`);
-      }
-      portsToAboutCompatTabs.broadcast({toggling: id, active: what.active}).then(async () => {
-        switch (type) {
-          case "interventions": {
-            if (what.active) {
-              await disableInjection(what);
-            } else {
-              await enableInjection(what);
-            }
-            break;
-          }
-          case "overrides": {
-            if (what.active) {
-              await disableOverride(what);
-            } else {
-              await enableOverride(what);
-            }
-            break;
-          }
-        }
-        portsToAboutCompatTabs.broadcast({toggled: id, active: what.active});
-      });
-      break;
-    }
-    case "getOverridesAndInterventions": {
-      return Promise.resolve({
-        overrides: UAOverridesEnabled && filterOverrides(UAOverrides) || false,
-        interventions: InjectionsEnabled && filterOverrides(Injections) || false,
-      });
-    }
-  }
-  return undefined;
-});
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/data/injections.js
@@ -0,0 +1,244 @@
+/* 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";
+
+/* globals module */
+
+/**
+ * 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 AVAILABLE_INJECTIONS = [
+  {
+    id: "testbed-injection",
+    platform: "all",
+    domain: "webcompat-addon-testbed.herokuapp.com",
+    bug: "0000000",
+    hidden: true,
+    contentScripts: {
+      matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
+      css: [
+        {
+          file: "injections/css/bug0000000-testbed-css-injection.css",
+        },
+      ],
+      js: [
+        {
+          file: "injections/js/bug0000000-testbed-js-injection.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1452707",
+    platform: "desktop",
+    domain: "ib.absa.co.za",
+    bug: "1452707",
+    contentScripts: {
+      matches: ["https://ib.absa.co.za/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1457335",
+    platform: "desktop",
+    domain: "histography.io",
+    bug: "1457335",
+    contentScripts: {
+      matches: ["*://histography.io/*"],
+      js: [
+        {
+          file: "injections/js/bug1457335-histography.io-ua-change.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1472075",
+    platform: "desktop",
+    domain: "bankofamerica.com",
+    bug: "1472075",
+    contentScripts: {
+      matches: ["*://*.bankofamerica.com/*"],
+      js: [
+        {
+          file: "injections/js/bug1472075-bankofamerica.com-ua-change.js",
+        },
+      ],
+      runAt: "document_start",
+    },
+  },
+  {
+    id: "bug1472081",
+    platform: "desktop",
+    domain: "election.gov.np",
+    bug: "1472081",
+    contentScripts: {
+      matches: ["http://202.166.205.141/bbvrs/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js",
+        },
+      ],
+      runAt: "document_start",
+      allFrames: true,
+    },
+  },
+  {
+    id: "bug1482066",
+    platform: "desktop",
+    domain: "portalminasnet.com",
+    bug: "1482066",
+    contentScripts: {
+      matches: ["*://portalminasnet.com/*"],
+      js: [
+        {
+          file:
+            "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js",
+        },
+      ],
+      runAt: "document_start",
+      allFrames: true,
+    },
+  },
+  {
+    id: "bug1526977",
+    platform: "desktop",
+    domain: "sreedharscce.in",
+    bug: "1526977",
+    contentScripts: {
+      matches: ["*://*.sreedharscce.in/authenticate"],
+      css: [
+        {
+          file: "injections/css/bug1526977-sreedharscce.in-login-fix.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1518781",
+    platform: "desktop",
+    domain: "twitch.tv",
+    bug: "1518781",
+    contentScripts: {
+      matches: ["*://*.twitch.tv/*"],
+      css: [
+        {
+          file: "injections/css/bug1518781-twitch.tv-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1551672",
+    platform: "android",
+    domain: "Sites using PDK 5 video",
+    bug: "1551672",
+    pdk5fix: {
+      urls: ["https://*/*/tpPdk.js", "https://*/*/pdk/js/*/*.js"],
+      types: ["script"],
+    },
+  },
+  {
+    id: "bug1305028",
+    platform: "desktop",
+    domain: "gaming.youtube.com",
+    bug: "1305028",
+    contentScripts: {
+      matches: ["*://gaming.youtube.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1432935-discord",
+    platform: "desktop",
+    domain: "discordapp.com",
+    bug: "1432935",
+    contentScripts: {
+      matches: ["*://discordapp.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1432935-breitbart",
+    platform: "desktop",
+    domain: "breitbart.com",
+    bug: "1432935",
+    contentScripts: {
+      matches: ["*://*.breitbart.com/*"],
+      css: [
+        {
+          file: "injections/css/bug1432935-breitbart.com-webkit-scrollbar.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1561371",
+    platform: "android",
+    domain: "mail.google.com",
+    bug: "1561371",
+    contentScripts: {
+      matches: ["*://mail.google.com/*"],
+      css: [
+        {
+          file:
+            "injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1567610",
+    platform: "all",
+    domain: "dns.google.com",
+    bug: "1567610",
+    contentScripts: {
+      matches: ["*://dns.google.com/*"],
+      css: [
+        {
+          file: "injections/css/bug1567610-dns.google.com-moz-fit-content.css",
+        },
+      ],
+    },
+  },
+  {
+    id: "bug1568256",
+    platform: "android",
+    domain: "zertifikate.commerzbank.de",
+    bug: "1568256",
+    contentScripts: {
+      matches: ["*://*.zertifikate.commerzbank.de/webforms/mobile/*"],
+      css: [
+        {
+          file: "injections/css/bug1568256-zertifikate.commerzbank.de-flex.css",
+        },
+      ],
+    },
+  },
+];
+
+module.exports = AVAILABLE_INJECTIONS;
rename from browser/extensions/webcompat/ua_overrides.js
rename to mobile/android/extensions/webcompat/data/ua_overrides.js
--- a/browser/extensions/webcompat/ua_overrides.js
+++ b/mobile/android/extensions/webcompat/data/ua_overrides.js
@@ -1,48 +1,58 @@
+/* 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";
+
+/* globals module */
+
 /**
  * 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
  */
-
-"use strict";
-
-/* globals filterOverrides, portsToAboutCompatTabs, UAOverrides */
-
-let UAOverridesEnabled = true;
-
-for (const override of [
+const AVAILABLE_UA_OVERRIDES = [
   {
-    id: "testoverride",
+    id: "testbed-override",
     platform: "all",
-    domain: "webcompat-addon-testcases.schub.io",
-    bug: "1287966",
+    domain: "webcompat-addon-testbed.herokuapp.com",
+    bug: "0000000",
+    hidden: true,
     config: {
-      matches: ["*://webcompat-addon-testcases.schub.io/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36";
+      matches: ["*://webcompat-addon-testbed.herokuapp.com/*"],
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36 for WebCompat"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1563839 - rolb.santanderbank.com - Build UA override
      * WebCompat issue #33462 - https://webcompat.com/issues/33462
      *
      * santanderbank expects UA to have 'like Gecko', otherwise it runs
      * xmlDoc.onload whose support has been dropped. It results in missing labels in forms
      * and some other issues.  Adding 'like Gecko' fixes those issues.
      */
     id: "bug1563839",
     platform: "all",
     domain: "rolb.santanderbank.com",
     bug: "1563839",
     config: {
-      matches: ["*://*.santander.co.uk/*", "*://rolb.santanderbank.com/*"],
+      matches: [
+        "*://*.santander.co.uk/*",
+        "*://bob.santanderbank.com/*",
+        "*://rolb.santanderbank.com/*",
+      ],
       uaTransformer: originalUA => {
         return originalUA.replace("Gecko", "like Gecko");
       },
     },
   },
   {
     /*
      * Bug 1480710 - m.imgur.com - Build UA override
@@ -53,282 +63,265 @@ for (const override of [
      * receive the correct CSS and JS files.
      */
     id: "bug1480710",
     platform: "android",
     domain: "m.imgur.com",
     bug: "1480710",
     config: {
       matches: ["*://m.imgur.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.85 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * 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.
      */
     id: "bug945963",
     platform: "android",
     domain: "tieba.baidu.com",
     bug: "945963",
     config: {
       matches: ["*://tieba.baidu.com/*", "*://tiebac.baidu.com/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA + " AppleWebKit/537.36 (KHTML, like Gecko)";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
     id: "bug1177298-2",
     platform: "android",
     domain: "lohaco.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.lohaco.jp/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
     id: "bug1177298-3",
     platform: "android",
     domain: "nhk.or.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.nhk.or.jp/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA + " AppleWebKit";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1338260 - Add UA override for directTV
      * (Imported from ua-update.json.in)
      *
      * DirectTV has issues with scrolling and cut-off images. Pretending to be
      * Chrome for Android fixes those issues.
      */
     id: "bug1338260",
     platform: "android",
     domain: "directv.com",
     bug: "1338260",
     config: {
       matches: ["*://*.directv.com/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1385206 - Create UA override for rakuten.co.jp on Firefox Android
      * (Imported from ua-update.json.in)
      *
      * rakuten.co.jp serves a Desktop version if Firefox is included in the UA.
      */
     id: "bug1385206",
     platform: "android",
     domain: "rakuten.co.jp",
     bug: "1385206",
     config: {
       matches: ["*://*.rakuten.co.jp/*"],
-      uaTransformer: (originalUA) => {
+      uaTransformer: originalUA => {
         return originalUA.replace(/Firefox.+$/, "");
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 969844 - mobile.de sends desktop site to Firefox on Android
      *
      * mobile.de sends the desktop site to Fennec. Spooing as Chrome works fine.
      */
     id: "bug969844",
     platform: "android",
     domain: "mobile.de",
     bug: "969844",
     config: {
       matches: ["*://*.mobile.de/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509831 - cc.com - Add UA override for CC.com
      * WebCompat issue #329 - https://webcompat.com/issues/329
      *
      * ComedyCentral blocks Firefox for not being able to play HLS, which was
      * true in previous versions, but no longer is. With a spoofed Chrome UA,
      * the site works just fine.
      */
     id: "bug1509831",
     platform: "android",
     domain: "cc.com",
     bug: "1509831",
     config: {
       matches: ["*://*.cc.com/*"],
-      uaTransformer: (_) => {
+      uaTransformer: _ => {
         return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G920F Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1508516 - cineflix.com.br - Add UA override for cineflix.com.br/m/
      * WebCompat issue #21553 - https://webcompat.com/issues/21553
      *
      * The site renders a blank page with any Firefox snipped in the UA as it
      * is running into an exception. Spoofing as Chrome makes the site work
      * fine.
      */
     id: "bug1508516",
     platform: "android",
     domain: "cineflix.com.br",
     bug: "1508516",
     config: {
       matches: ["*://*.cineflix.com.br/m/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509852 - redbull.com - Add UA override for redbull.com
      * WebCompat issue #21439 - https://webcompat.com/issues/21439
      *
      * Redbull.com blocks some features, for example the live video player, for
      * Fennec. Spoofing as Chrome results in us rendering the video just fine,
      * and everything else works as well.
      */
     id: "bug1509852",
     platform: "android",
     domain: "redbull.com",
     bug: "1509852",
     config: {
       matches: ["*://*.redbull.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
-  }, {
+  },
+  {
     /*
      * Bug 1509873 - zmags.com - Add UA override for secure.viewer.zmags.com
      * WebCompat issue #21576 - https://webcompat.com/issues/21576
      *
      * The zmags viewer locks out Fennec with a "Browser unsupported" message,
      * but tests showed that it works just fine with a Chrome UA. Outreach
      * attempts were unsuccessful, and as the site has a relatively high rank,
      * we alter the UA.
      */
     id: "bug1509873",
     platform: "android",
     domain: "zmags.com",
     bug: "1509873",
     config: {
       matches: ["*://*.viewer.zmags.com/*"],
-      uaTransformer: (originalUA) => {
-        return UAHelpers.getPrefix(originalUA) + " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36";
+      uaTransformer: originalUA => {
+        return (
+          UAHelpers.getPrefix(originalUA) +
+          " AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36"
+        );
       },
     },
   },
-]) {
-  UAOverrides.push(override);
-}
-
-/* globals browser */
+  {
+    /*
+     * Bug 1566253 - posts.google.com - Add UA override for posts.google.com
+     * WebCompat issue #17870 - https://webcompat.com/issues/17870
+     *
+     * posts.google.com displaying "Your browser doesn't support this page".
+     * Spoofing as Chrome works fine.
+     */
+    id: "bug1566253",
+    platform: "android",
+    domain: "posts.google.com",
+    bug: "1566253",
+    config: {
+      matches: ["*://posts.google.com/*"],
+      uaTransformer: _ => {
+        return "Mozilla/5.0 (Linux; Android 6.0.1; SM-G900M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36";
+      },
+    },
+  },
+  {
+    /*
+     * Bug 1567945 - Create UA override for beeg.com on Firefox Android
+     * WebCompat issue #16648 - https://webcompat.com/issues/16648
+     *
+     * beeg.com is hiding content of a page with video if Firefox exists in UA,
+     * replacing "Firefox" with an empty string makes the page load
+     */
+    id: "bug1567945",
+    platform: "android",
+    domain: "beeg.com",
+    bug: "1567945",
+    config: {
+      matches: ["*://beeg.com/*"],
+      uaTransformer: originalUA => {
+        return originalUA.replace(/Firefox.+$/, "");
+      },
+    },
+  },
+];
 
 const UAHelpers = {
   getPrefix(originalUA) {
     return originalUA.substr(0, originalUA.indexOf(")") + 1);
   },
 };
 
-const ActiveListeners = new Map();
-
-function enableOverride(override) {
-  if (override.active) {
-    return;
-  }
-
-  const {matches, uaTransformer} = override.config;
-  const listener = (details) => {
-    for (var header of details.requestHeaders) {
-      if (header.name.toLowerCase() === "user-agent") {
-        header.value = uaTransformer(header.value);
-      }
-    }
-    return {requestHeaders: details.requestHeaders};
-  };
-
-  browser.webRequest.onBeforeSendHeaders.addListener(
-    listener,
-    {urls: matches},
-    ["blocking", "requestHeaders"]
-  );
-
-  ActiveListeners.set(override, listener);
-  override.active = true;
-}
-
-async function registerUAOverrides() {
-  const platformMatches = ["all"];
-  let platformInfo = await browser.runtime.getPlatformInfo();
-  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
-
-  for (const override of UAOverrides) {
-    if (platformMatches.includes(override.platform)) {
-      override.availableOnPlatform = true;
-      enableOverride(override);
-    }
-  }
-  UAOverridesEnabled = true;
-  portsToAboutCompatTabs.broadcast({overridesChanged: filterOverrides(UAOverrides)});
-}
-
-function unregisterUAOverrides() {
-  for (const override of UAOverrides) {
-    disableOverride(override);
-  }
-  UAOverridesEnabled = false;
-  portsToAboutCompatTabs.broadcast({overridesChanged: false});
-}
-
-function disableOverride(override) {
-  if (!override.active) {
-    return;
-  }
-
-  browser.webRequest.onBeforeSendHeaders.removeListener(ActiveListeners.get(override));
-  override.active = false;
-  ActiveListeners.delete(override);
-}
-
-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();
+module.exports = AVAILABLE_UA_OVERRIDES;
rename from mobile/android/extensions/webcompat/aboutConfigPrefs.js
rename to mobile/android/extensions/webcompat/experiment-apis/aboutConfigPrefs.js
rename from mobile/android/extensions/webcompat/aboutConfigPrefs.json
rename to mobile/android/extensions/webcompat/experiment-apis/aboutConfigPrefs.json
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/injections.js
+++ /dev/null
@@ -1,262 +0,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
- */
-
-"use strict";
-
-/* globals browser, filterOverrides, Injections, portsToAboutCompatTabs */
-
-let InjectionsEnabled = true;
-
-for (const injection of [
-  {
-    id: "testinjection",
-    platform: "all",
-    domain: "webcompat-addon-testcases.schub.io",
-    bug: "1287966",
-    contentScripts: {
-      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",
-    },
-  }, {
-    id: "bug1452707",
-    platform: "desktop",
-    domain: "ib.absa.co.za",
-    bug: "1452707",
-    contentScripts: {
-      matches: ["https://ib.absa.co.za/*"],
-      js: [{file: "injections/js/bug1452707-window.controllers-shim-ib.absa.co.za.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1457335",
-    platform: "desktop",
-    domain: "histography.io",
-    bug: "1457335",
-    contentScripts: {
-      matches: ["*://histography.io/*"],
-      js: [{file: "injections/js/bug1457335-histography.io-ua-change.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1472075",
-    platform: "desktop",
-    domain: "bankofamerica.com",
-    bug: "1472075",
-    contentScripts: {
-      matches: ["*://*.bankofamerica.com/*"],
-      js: [{file: "injections/js/bug1472075-bankofamerica.com-ua-change.js"}],
-      runAt: "document_start",
-    },
-  }, {
-    id: "bug1472081",
-    platform: "desktop",
-    domain: "election.gov.np",
-    bug: "1472081",
-    contentScripts: {
-      matches: ["http://202.166.205.141/bbvrs/*"],
-      js: [{file: "injections/js/bug1472081-election.gov.np-window.sidebar-shim.js"}],
-      runAt: "document_start",
-      allFrames: true,
-    },
-  }, {
-    id: "bug1482066",
-    platform: "desktop",
-    domain: "portalminasnet.com",
-    bug: "1482066",
-    contentScripts: {
-      matches: ["*://portalminasnet.com/*"],
-      js: [{file: "injections/js/bug1482066-portalminasnet.com-window.sidebar-shim.js"}],
-      runAt: "document_start",
-      allFrames: true,
-    },
-  }, {
-    id: "bug1526977",
-    platform: "desktop",
-    domain: "sreedharscce.in",
-    bug: "1526977",
-    contentScripts: {
-      matches: ["*://*.sreedharscce.in/authenticate"],
-      css: [{file: "injections/css/bug1526977-sreedharscce.in-login-fix.css"}],
-    },
-  }, {
-    id: "bug1518781",
-    platform: "desktop",
-    domain: "twitch.tv",
-    bug: "1518781",
-    contentScripts: {
-      matches: ["*://*.twitch.tv/*"],
-      css: [{file: "injections/css/bug1518781-twitch.tv-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1551672",
-    platform: "android",
-    domain: "Sites using PDK 5 video",
-    bug: "1551672",
-    pdk5fix: {
-      urls: ["https://*/*/tpPdk.js", "https://*/*/pdk/js/*/*.js"],
-      types: ["script"],
-    },
-  }, {
-    id: "bug1305028",
-    platform: "desktop",
-    domain: "gaming.youtube.com",
-    bug: "1305028",
-    contentScripts: {
-      matches: ["*://gaming.youtube.com/*"],
-      css: [{file: "injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1432935-discord",
-    platform: "desktop",
-    domain: "discordapp.com",
-    bug: "1432935",
-    contentScripts: {
-      matches: ["*://discordapp.com/*"],
-      css: [{file: "injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css"}],
-    },
-  }, {
-    id: "bug1432935-breitbart",
-    platform: "desktop",
-    domain: "breitbart.com",
-    bug: "1432935",
-    contentScripts: {
-      matches: ["*://*.breitbart.com/*"],
-      css: [{file: "injections/css/bug1432935-breitbart.com-webkit-scrollbar.css"}],
-    },
-  }, {
-    id: "bug1561371",
-    platform: "android",
-    domain: "mail.google.com",
-    bug: "1561371",
-    contentScripts: {
-      matches: ["*://mail.google.com/*"],
-      css: [{file: "injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css"}],
-    },
-  },
-]) {
-  Injections.push(injection);
-}
-
-let port = browser.runtime.connect();
-const ActiveInjections = new Map();
-
-async function registerContentScripts() {
-  const platformMatches = ["all"];
-  let platformInfo = await browser.runtime.getPlatformInfo();
-  platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
-
-  for (const injection of Injections) {
-    if (platformMatches.includes(injection.platform)) {
-      injection.availableOnPlatform = true;
-      await enableInjection(injection);
-    }
-  }
-
-  InjectionsEnabled = true;
-  portsToAboutCompatTabs.broadcast({interventionsChanged: filterOverrides(Injections)});
-}
-
-function replaceStringInRequest(requestId, inString, outString, inEncoding = "utf-8") {
-  const filter = browser.webRequest.filterResponseData(requestId);
-  const decoder = new TextDecoder(inEncoding);
-  const encoder = new TextEncoder();
-  const RE = new RegExp(inString, "g");
-  const carryoverLength = inString.length;
-  let carryover = "";
-  filter.ondata = event => {
-    const replaced = (carryover + decoder.decode(event.data, {stream: true})).replace(RE, outString);
-    filter.write(encoder.encode(replaced.slice(0, -carryoverLength)));
-    carryover = replaced.slice(-carryoverLength);
-  };
-  filter.onstop = event => {
-    if (carryover.length) {
-      filter.write(encoder.encode(carryover));
-    }
-    filter.close();
-  };
-}
-
-async function enableInjection(injection) {
-  if (injection.active) {
-    return;
-  }
-
-  if ("pdk5fix" in injection) {
-    const {urls, types} = injection.pdk5fix;
-    const listener = injection.pdk5fix.listener = ({requestId}) => {
-      replaceStringInRequest(requestId, "VideoContextChromeAndroid", "VideoContextAndroid");
-      return {};
-    };
-    browser.webRequest.onBeforeRequest.addListener(listener, {urls, types}, ["blocking"]);
-    injection.active = true;
-    return;
-  }
-
-  try {
-    const handle = await browser.contentScripts.register(injection.contentScripts);
-    ActiveInjections.set(injection, handle);
-    injection.active = true;
-  } catch (ex) {
-    console.error("Registering WebCompat GoFaster content scripts failed: ", ex);
-  }
-}
-
-function unregisterContentScripts() {
-  for (const injection of Injections) {
-    disableInjection(injection);
-  }
-  InjectionsEnabled = false;
-  portsToAboutCompatTabs.broadcast({interventionsChanged: false});
-}
-
-async function disableInjection(injection) {
-  if (!injection.active) {
-    return;
-  }
-
-  if (injection.pdk5fix) {
-    const {listener} = injection.pdk5fix;
-    browser.webRequest.onBeforeRequest.removeListener(listener);
-    injection.active = false;
-    delete injection.pdk5fix.listener;
-    return;
-  }
-
-  const contentScript = ActiveInjections.get(injection);
-  await contentScript.unregister();
-  ActiveInjections.delete(injection);
-  injection.active = false;
-}
-
-port.onMessage.addListener((message) => {
-  switch (message.type) {
-    case "injection-pref-changed":
-      if (message.prefState) {
-        registerContentScripts();
-      } else {
-        unregisterContentScripts();
-      }
-      break;
-  }
-});
-
-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/injections/css/bug0000000-dummy-css-injection.css
rename to mobile/android/extensions/webcompat/injections/css/bug0000000-testbed-css-injection.css
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/injections/css/bug1567610-dns.google.com-moz-fit-content.css
@@ -0,0 +1,12 @@
+/**
+ * dns.google.com - Page content is shifted to the left side of the page
+ * Bug #1567610 - https://bugzilla.mozilla.org/show_bug.cgi?id=1567610
+ * WebCompat issue #22494 - https://webcompat.com/issues/22494
+ *
+ * Affected element is styled with width:fit-content; which is not
+ * supported by Firefox yet, see https://bugzilla.mozilla.org/show_bug.cgi?id=1495868
+ * Adding -moz-fit-content fixes the issue
+ */
+main > .ng-star-inserted > .centered {
+  width: -moz-fit-content;
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/injections/css/bug1568256-zertifikate.commerzbank.de-flex.css
@@ -0,0 +1,12 @@
+/**
+ * zertifikate.commerzbank.de - clickable elements on the page are collapsed
+ * Bug #1568256 - https://bugzilla.mozilla.org/show_bug.cgi?id=1568256
+ * WebCompat issue #9102 - https://webcompat.com/issues/9102
+ *
+ * Affected elements have display:-webkit-box and display:flex applied, however,
+ * listed in wrong order, so display:-webkit-box is becoming the final say.
+ * Adding display: flex for those elements fixes the issue
+ */
+.x-layout-box {
+  display: flex !important;
+}
rename from mobile/android/extensions/webcompat/injections/js/bug0000000-dummy-js-injection.js
rename to mobile/android/extensions/webcompat/injections/js/bug0000000-testbed-js-injection.js
deleted file mode 100644
--- a/mobile/android/extensions/webcompat/jar.mn
+++ /dev/null
@@ -1,7 +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/.
-
-[features/webcompat@mozilla.org] chrome.jar:
-  res/AboutCompat.jsm (AboutCompat.jsm)
-  res/aboutPageProcessScript.js (aboutPageProcessScript.js)
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/lib/about_compat_broker.js
@@ -0,0 +1,123 @@
+/* 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 browser, module */
+
+class AboutCompatBroker {
+  constructor(bindings) {
+    this.portsToAboutCompatTabs = this.buildPorts();
+
+    this._injections = bindings.injections;
+    this._injections.bindAboutCompatBroker(this);
+
+    this._uaOverrides = bindings.uaOverrides;
+    this._uaOverrides.bindAboutCompatBroker(this);
+  }
+
+  buildPorts() {
+    const ports = new Set();
+
+    browser.runtime.onConnect.addListener(port => {
+      ports.add(port);
+      port.onDisconnect.addListener(function() {
+        ports.delete(port);
+      });
+    });
+
+    async function broadcast(message) {
+      for (const port of ports) {
+        port.postMessage(message);
+      }
+    }
+
+    return { broadcast };
+  }
+
+  filterOverrides(overrides) {
+    return overrides
+      .filter(override => override.availableOnPlatform)
+      .map(override => {
+        const { id, active, bug, domain, hidden } = override;
+        return { id, active, bug, domain, hidden };
+      });
+  }
+
+  getOverrideOrInterventionById(id) {
+    for (const [type, things] of Object.entries({
+      overrides: this._uaOverrides.getAvailableOverrides(),
+      interventions: this._injections.getAvailableInjections(),
+    })) {
+      for (const what of things) {
+        if (what.id === id) {
+          return { type, what };
+        }
+      }
+    }
+    return {};
+  }
+
+  bootup() {
+    browser.runtime.onMessage.addListener(msg => {
+      switch (msg.command || msg) {
+        case "toggle": {
+          const id = msg.id;
+          const { type, what } = this.getOverrideOrInterventionById(id);
+          if (!what) {
+            return Promise.reject(
+              `No such override or intervention to toggle: ${id}`
+            );
+          }
+          this.portsToAboutCompatTabs
+            .broadcast({ toggling: id, active: what.active })
+            .then(async () => {
+              switch (type) {
+                case "interventions": {
+                  if (what.active) {
+                    await this._injections.disableInjection(what);
+                  } else {
+                    await this._injections.enableInjection(what);
+                  }
+                  break;
+                }
+                case "overrides": {
+                  if (what.active) {
+                    await this._uaOverrides.disableOverride(what);
+                  } else {
+                    await this._uaOverrides.enableOverride(what);
+                  }
+                  break;
+                }
+              }
+              this.portsToAboutCompatTabs.broadcast({
+                toggled: id,
+                active: what.active,
+              });
+            });
+          break;
+        }
+        case "getOverridesAndInterventions": {
+          return Promise.resolve({
+            overrides:
+              (this._uaOverrides.isEnabled() &&
+                this.filterOverrides(
+                  this._uaOverrides.getAvailableOverrides()
+                )) ||
+              false,
+            interventions:
+              (this._injections.isEnabled() &&
+                this.filterOverrides(
+                  this._injections.getAvailableInjections()
+                )) ||
+              false,
+          });
+        }
+      }
+      return undefined;
+    });
+  }
+}
+
+module.exports = AboutCompatBroker;
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/lib/injections.js
@@ -0,0 +1,163 @@
+/* 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";
+
+/* globals browser, module */
+
+class Injections {
+  constructor(availableInjections) {
+    this.INJECTION_PREF = "perform_injections";
+
+    this._injectionsEnabled = true;
+
+    this._availableInjections = availableInjections;
+    this._activeInjections = new Map();
+  }
+
+  bindAboutCompatBroker(broker) {
+    this._aboutCompatBroker = broker;
+  }
+
+  bootup() {
+    browser.aboutConfigPrefs.onPrefChange.addListener(() => {
+      this.checkInjectionPref();
+    }, this.INJECTION_PREF);
+    this.checkInjectionPref();
+  }
+
+  checkInjectionPref() {
+    browser.aboutConfigPrefs.getPref(this.INJECTION_PREF).then(value => {
+      if (value === undefined) {
+        browser.aboutConfigPrefs.setPref(this.INJECTION_PREF, true);
+      } else if (value === false) {
+        this.unregisterContentScripts();
+      } else {
+        this.registerContentScripts();
+      }
+    });
+  }
+
+  getAvailableInjections() {
+    return this._availableInjections;
+  }
+
+  isEnabled() {
+    return this._injectionsEnabled;
+  }
+
+  async registerContentScripts() {
+    const platformMatches = ["all"];
+    let platformInfo = await browser.runtime.getPlatformInfo();
+    platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+    for (const injection of this._availableInjections) {
+      if (platformMatches.includes(injection.platform)) {
+        injection.availableOnPlatform = true;
+        await this.enableInjection(injection);
+      }
+    }
+
+    this._injectionsEnabled = true;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      interventionsChanged: this._aboutCompatBroker.filterOverrides(
+        this._availableInjections
+      ),
+    });
+  }
+
+  replaceStringInRequest(requestId, inString, outString, inEncoding = "utf-8") {
+    const filter = browser.webRequest.filterResponseData(requestId);
+    const decoder = new TextDecoder(inEncoding);
+    const encoder = new TextEncoder();
+    const RE = new RegExp(inString, "g");
+    const carryoverLength = inString.length;
+    let carryover = "";
+
+    filter.ondata = event => {
+      const replaced = (
+        carryover + decoder.decode(event.data, { stream: true })
+      ).replace(RE, outString);
+      filter.write(encoder.encode(replaced.slice(0, -carryoverLength)));
+      carryover = replaced.slice(-carryoverLength);
+    };
+
+    filter.onstop = event => {
+      if (carryover.length) {
+        filter.write(encoder.encode(carryover));
+      }
+      filter.close();
+    };
+  }
+
+  async enableInjection(injection) {
+    if (injection.active) {
+      return;
+    }
+
+    if ("pdk5fix" in injection) {
+      const { urls, types } = injection.pdk5fix;
+      const listener = (injection.pdk5fix.listener = ({ requestId }) => {
+        this.replaceStringInRequest(
+          requestId,
+          "VideoContextChromeAndroid",
+          "VideoContextAndroid"
+        );
+        return {};
+      });
+      browser.webRequest.onBeforeRequest.addListener(
+        listener,
+        { urls, types },
+        ["blocking"]
+      );
+      injection.active = true;
+      return;
+    }
+
+    try {
+      const handle = await browser.contentScripts.register(
+        injection.contentScripts
+      );
+      this._activeInjections.set(injection, handle);
+      injection.active = true;
+    } catch (ex) {
+      console.error(
+        "Registering WebCompat GoFaster content scripts failed: ",
+        ex
+      );
+    }
+  }
+
+  unregisterContentScripts() {
+    for (const injection of this._availableInjections) {
+      this.disableInjection(injection);
+    }
+
+    this._injectionsEnabled = false;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      interventionsChanged: false,
+    });
+  }
+
+  async disableInjection(injection) {
+    if (!injection.active) {
+      return;
+    }
+
+    if (injection.pdk5fix) {
+      const { listener } = injection.pdk5fix;
+      browser.webRequest.onBeforeRequest.removeListener(listener);
+      injection.active = false;
+      delete injection.pdk5fix.listener;
+      return;
+    }
+
+    const contentScript = this._activeInjections.get(injection);
+    await contentScript.unregister();
+    this._activeInjections.delete(injection);
+    injection.active = false;
+  }
+}
+
+module.exports = Injections;
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/lib/module_shim.js
@@ -0,0 +1,24 @@
+/* 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";
+
+/**
+ * We cannot yet use proper JS modules within webextensions, as support for them
+ * is highly experimental and highly instable. So we end up just including all
+ * the JS files we need as separate background scripts, and since they all are
+ * executed within the same context, this works for our in-browser deployment.
+ *
+ * However, this code is tracked outside of mozilla-central, and we work on
+ * shipping this code in other products, like android-components as well.
+ * Because of that, we have automated tests running within that repository. To
+ * make our lives easier, we add `module.exports` statements to the JS source
+ * files, so we can easily import their contents into our NodeJS-based test
+ * suite.
+ *
+ * This works fine, but obviously, `module` is not defined when running
+ * in-browser. So let's use this empty object as a shim, so we don't run into
+ * runtime exceptions because of that.
+ */
+var module = {};
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/lib/ua_overrides.js
@@ -0,0 +1,119 @@
+/* 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";
+
+/* globals browser, module */
+
+class UAOverrides {
+  constructor(availableOverrides) {
+    this.OVERRIDE_PREF = "perform_ua_overrides";
+
+    this._overridesEnabled = true;
+
+    this._availableOverrides = availableOverrides;
+    this._activeListeners = new Map();
+  }
+
+  bindAboutCompatBroker(broker) {
+    this._aboutCompatBroker = broker;
+  }
+
+  bootup() {
+    browser.aboutConfigPrefs.onPrefChange.addListener(() => {
+      this.checkOverridePref();
+    }, this.OVERRIDE_PREF);
+    this.checkOverridePref();
+  }
+
+  checkOverridePref() {
+    browser.aboutConfigPrefs.getPref(this.OVERRIDE_PREF).then(value => {
+      if (value === undefined) {
+        browser.aboutConfigPrefs.setPref(this.OVERRIDE_PREF, true);
+      } else if (value === false) {
+        this.unregisterUAOverrides();
+      } else {
+        this.registerUAOverrides();
+      }
+    });
+  }
+
+  getAvailableOverrides() {
+    return this._availableOverrides;
+  }
+
+  isEnabled() {
+    return this._overridesEnabled;
+  }
+
+  enableOverride(override) {
+    if (override.active) {
+      return;
+    }
+
+    const { matches, uaTransformer } = override.config;
+    const listener = details => {
+      for (const header of details.requestHeaders) {
+        if (header.name.toLowerCase() === "user-agent") {
+          header.value = uaTransformer(header.value);
+        }
+      }
+      return { requestHeaders: details.requestHeaders };
+    };
+
+    browser.webRequest.onBeforeSendHeaders.addListener(
+      listener,
+      { urls: matches },
+      ["blocking", "requestHeaders"]
+    );
+
+    this._activeListeners.set(override, listener);
+    override.active = true;
+  }
+
+  async registerUAOverrides() {
+    const platformMatches = ["all"];
+    let platformInfo = await browser.runtime.getPlatformInfo();
+    platformMatches.push(platformInfo.os == "android" ? "android" : "desktop");
+
+    for (const override of this._availableOverrides) {
+      if (platformMatches.includes(override.platform)) {
+        override.availableOnPlatform = true;
+        this.enableOverride(override);
+      }
+    }
+
+    this._overridesEnabled = true;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      overridesChanged: this._aboutCompatBroker.filterOverrides(
+        this._availableOverrides
+      ),
+    });
+  }
+
+  unregisterUAOverrides() {
+    for (const override of this._availableOverrides) {
+      this.disableOverride(override);
+    }
+
+    this._overridesEnabled = false;
+    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
+      overridesChanged: false,
+    });
+  }
+
+  disableOverride(override) {
+    if (!override.active) {
+      return;
+    }
+
+    browser.webRequest.onBeforeSendHeaders.removeListener(
+      this._activeListeners.get(override)
+    );
+    override.active = false;
+    this._activeListeners.delete(override);
+  }
+}
+
+module.exports = UAOverrides;
--- a/mobile/android/extensions/webcompat/manifest.json
+++ b/mobile/android/extensions/webcompat/manifest.json
@@ -1,48 +1,52 @@
 {
   "manifest_version": 2,
   "name": "Web Compat",
   "description": "Urgent post-release fixes for web compatibility.",
-  "version": "4.4.0",
+  "version": "5.0.2",
 
   "applications": {
     "gecko": {
       "id": "webcompat@mozilla.org",
       "strict_min_version": "59.0b5"
     }
   },
 
   "experiment_apis": {
     "aboutConfigPrefs": {
-      "schema": "aboutConfigPrefs.json",
+      "schema": "experiment-apis/aboutConfigPrefs.json",
       "parent": {
         "scopes": ["addon_parent"],
-        "script": "aboutConfigPrefs.js",
+        "script": "experiment-apis/aboutConfigPrefs.js",
         "paths": [["aboutConfigPrefs"]]
       }
     },
     "aboutPage": {
-      "schema": "aboutPage.json",
+      "schema": "about-compat/aboutPage.json",
       "parent": {
         "scopes": ["addon_parent"],
-        "script": "aboutPage.js",
+        "script": "about-compat/aboutPage.js",
         "events": ["startup"]
       }
     }
   },
 
-  "content_security_policy": "script-src 'self' 'sha256-HbSjs39Y0thRGfO3RHrNzLPKyC/tq6FdIuP3jEBAcJQ='; default-src 'self'; base-uri moz-extension://*;",
+  "content_security_policy": "script-src 'self' 'sha256-MmZkN2QaIHhfRWPZ8TVRjijTn5Ci1iEabtTEWrt9CCo='; default-src 'self'; base-uri moz-extension://*;",
 
   "permissions": [
     "webRequest",
     "webRequestBlocking",
     "<all_urls>"
   ],
 
   "background": {
     "scripts": [
-      "background.js",
-      "injections.js",
-      "ua_overrides.js"
+      "lib/module_shim.js",
+      "data/injections.js",
+      "data/ua_overrides.js",
+      "lib/about_compat_broker.js",
+      "lib/injections.js",
+      "lib/ua_overrides.js",
+      "run.js"
     ]
   }
 }
--- a/mobile/android/extensions/webcompat/moz.build
+++ b/mobile/android/extensions/webcompat/moz.build
@@ -3,44 +3,62 @@
 # 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'] += [
-  'aboutCompat.css',
-  'aboutCompat.html',
-  'aboutCompat.js',
-  'aboutConfigPrefs.js',
-  'aboutConfigPrefs.json',
-  'aboutPage.js',
-  'aboutPage.json',
-  'background.js',
-  'injections.js',
   'manifest.json',
-  'ua_overrides.js'
+  'run.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['about-compat'] += [
+  'about-compat/aboutCompat.css',
+  'about-compat/aboutCompat.html',
+  'about-compat/aboutCompat.js',
+  'about-compat/AboutCompat.jsm',
+  'about-compat/aboutPage.js',
+  'about-compat/aboutPage.json',
+  'about-compat/aboutPageProcessScript.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['data'] += [
+  'data/injections.js',
+  'data/ua_overrides.js'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['experiment-apis'] += [
+  'experiment-apis/aboutConfigPrefs.js',
+  'experiment-apis/aboutConfigPrefs.json'
 ]
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['css'] += [
-  'injections/css/bug0000000-dummy-css-injection.css',
+  'injections/css/bug0000000-testbed-css-injection.css',
   'injections/css/bug1305028-gaming.youtube.com-webkit-scrollbar.css',
   'injections/css/bug1432935-breitbart.com-webkit-scrollbar.css',
   'injections/css/bug1432935-discordapp.com-webkit-scorllbar-white-line.css',
   'injections/css/bug1518781-twitch.tv-webkit-scrollbar.css',
   'injections/css/bug1526977-sreedharscce.in-login-fix.css',
-  'injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css'
+  'injections/css/bug1561371-mail.google.com-allow-horizontal-scrolling.css',
+  'injections/css/bug1567610-dns.google.com-moz-fit-content.css',
+  'injections/css/bug1568256-zertifikate.commerzbank.de-flex.css'
 ]
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org']['injections']['js'] += [
-  'injections/js/bug0000000-dummy-js-injection.js',
+  'injections/js/bug0000000-testbed-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'
 ]
 
-JAR_MANIFESTS += ['jar.mn']
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['lib'] += [
+  'lib/about_compat_broker.js',
+  'lib/injections.js',
+  'lib/module_shim.js',
+  'lib/ua_overrides.js'
+]
 
 with Files('**'):
   BUG_COMPONENT = ('Web Compatibility Tools', 'Go Faster')
new file mode 100644
--- /dev/null
+++ b/mobile/android/extensions/webcompat/run.js
@@ -0,0 +1,20 @@
+/* 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";
+
+/* globals AVAILABLE_INJECTIONS, AVAILABLE_UA_OVERRIDES, AboutCompatBroker,
+           Injections, UAOverrides */
+
+const injections = new Injections(AVAILABLE_INJECTIONS);
+const uaOverrides = new UAOverrides(AVAILABLE_UA_OVERRIDES);
+
+const aboutCompatBroker = new AboutCompatBroker({
+  injections,
+  uaOverrides,
+});
+
+aboutCompatBroker.bootup();
+injections.bootup();
+uaOverrides.bootup();