Bug 1386807 - Part 2: Add embedded webextension to enable CSS and JS overrides r=kmag,mconley
authorDennis Schubert <dschubert@mozilla.com>
Fri, 09 Feb 2018 18:52:29 +0100
changeset 469614 3307b23d288947b48e8d74e521ae8b618a1ba70f
parent 469613 a97531cf72c34bdfb0902cc5185bc17dd8fd5574
child 469615 5e1fe3a9874b5575af9dfc4baf6d42b696aa3d66
push id9174
push userarchaeopteryx@coole-files.de
push dateMon, 30 Apr 2018 15:33:30 +0000
treeherdermozilla-beta@1b1a8ab75f1f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag, mconley
bugs1386807
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1386807 - Part 2: Add embedded webextension to enable CSS and JS overrides r=kmag,mconley MozReview-Commit-ID: JdSUZYHkuiV
browser/extensions/webcompat/bootstrap.js
browser/extensions/webcompat/content/data/ua_overrides.jsm
browser/extensions/webcompat/install.rdf.in
browser/extensions/webcompat/moz.build
browser/extensions/webcompat/webextension/background.js
browser/extensions/webcompat/webextension/injections/css/bug0000000-dummy-css-injection.css
browser/extensions/webcompat/webextension/injections/js/bug0000000-dummy-js-injection.js
browser/extensions/webcompat/webextension/manifest.json
--- a/browser/extensions/webcompat/bootstrap.js
+++ b/browser/extensions/webcompat/bootstrap.js
@@ -1,25 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 const PREF_BRANCH = "extensions.webcompat.";
-const PREF_DEFAULTS = {perform_ua_overrides: true};
+const PREF_DEFAULTS = {
+  perform_injections: true,
+  perform_ua_overrides: true
+};
+
+const INJECTIONS_ENABLE_PREF_NAME = "extensions.webcompat.perform_injections";
+
+const BROWSER_STARTUP_FINISHED_TOPIC = "browser-delayed-startup-finished";
 
 const UA_OVERRIDES_INIT_TOPIC = "useragentoverrides-initialized";
 const UA_ENABLE_PREF_NAME = "extensions.webcompat.perform_ua_overrides";
 
 ChromeUtils.defineModuleGetter(this, "UAOverrider", "chrome://webcompat/content/lib/ua_overrider.jsm");
 ChromeUtils.defineModuleGetter(this, "UAOverrides", "chrome://webcompat/content/data/ua_overrides.jsm");
 
 let overrider;
+let webextensionPort;
+
+function InjectionsEnablePrefObserver() {
+  let isEnabled = Services.prefs.getBoolPref(INJECTIONS_ENABLE_PREF_NAME);
+  webextensionPort.postMessage({
+    type: "injection-pref-changed",
+    prefState: isEnabled
+  });
+}
 
 function UAEnablePrefObserver() {
   let isEnabled = Services.prefs.getBoolPref(UA_ENABLE_PREF_NAME);
   if (isEnabled && !overrider) {
     overrider = new UAOverrider(UAOverrides);
     overrider.init();
   } else if (!isEnabled && overrider) {
     overrider = null;
@@ -52,32 +68,55 @@ this.install = function() {};
 this.uninstall = function() {};
 
 this.startup = function({webExtension}) {
   setDefaultPrefs();
 
   // Intentionally reset the preference on every browser restart to avoid site
   // breakage by accidentally toggled preferences or by leaving it off after
   // debugging a site.
+  Services.prefs.clearUserPref(INJECTIONS_ENABLE_PREF_NAME);
+  Services.prefs.addObserver(INJECTIONS_ENABLE_PREF_NAME, InjectionsEnablePrefObserver);
+
   Services.prefs.clearUserPref(UA_ENABLE_PREF_NAME);
   Services.prefs.addObserver(UA_ENABLE_PREF_NAME, UAEnablePrefObserver);
 
   // Listen to the useragentoverrides-initialized notification we get and
   // initialize our overrider there. This is done to avoid slowing down the
   // apparent startup proces, since we avoid loading anything before the first
   // window is visible to the user. See bug 1371442 for details.
-  let startupWatcher = {
+  let uaStartupObserver = {
     observe(aSubject, aTopic, aData) {
       if (aTopic !== UA_OVERRIDES_INIT_TOPIC) {
         return;
       }
 
       Services.obs.removeObserver(this, UA_OVERRIDES_INIT_TOPIC);
       overrider = new UAOverrider(UAOverrides);
       overrider.init();
     }
   };
-  Services.obs.addObserver(startupWatcher, UA_OVERRIDES_INIT_TOPIC);
+  Services.obs.addObserver(uaStartupObserver, UA_OVERRIDES_INIT_TOPIC);
+
+  // Observe browser-delayed-startup-finished and only initialize our embedded
+  // WebExtension after that. Otherwise, we'd try to initialize as soon as the
+  // browser starts up, which adds a heavy startup penalty.
+  let appStartupObserver = {
+    observe(aSubject, aTopic, aData) {
+      webExtension.startup().then((api) => {
+        api.browser.runtime.onConnect.addListener((port) => {
+          webextensionPort = port;
+        });
+
+        return Promise.resolve();
+      }).catch((ex) => {
+        console.error(ex);
+      });
+      Services.obs.removeObserver(this, BROWSER_STARTUP_FINISHED_TOPIC);
+    }
+  };
+  Services.obs.addObserver(appStartupObserver, BROWSER_STARTUP_FINISHED_TOPIC);
 };
 
 this.shutdown = function() {
+  Services.prefs.removeObserver(INJECTIONS_ENABLE_PREF_NAME, InjectionsEnablePrefObserver);
   Services.prefs.removeObserver(UA_ENABLE_PREF_NAME, UAEnablePrefObserver);
 };
--- a/browser/extensions/webcompat/content/data/ua_overrides.jsm
+++ b/browser/extensions/webcompat/content/data/ua_overrides.jsm
@@ -1,68 +1,31 @@
 /* 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/. */
 
 /**
- * This is an array of objects that specify user agent overrides. Each object
- * can have three attributes:
- *
- *   * `baseDomain`, required: The base domain that further checks and user
- *     agents override are applied to. This does not include subdomains.
- *   * `applications`: Array of applications this override is valid in.
- *     Defaults to ["firefox"], can be one or more of:
- *     * `firefox`: Firefox Desktop (regardless of the operating system)
- *     * `fennec`: Firefox for Android
- *   * `uriMatcher`: Function that gets the requested URI passed in the first
- *     argument and needs to return boolean whether or not the override should
- *     be applied. If not provided, the user agent override will be applied
- *     every time.
- *   * `uaTransformer`, required: Function that gets the original Firefox user
- *     agent passed as its first argument and needs to return a string that
- *     will be used as the the user agent for this URI.
- *
- * Examples:
+ * For detailed information on our policies, and a documention on this format
+ * and its possibilites, please check the Mozilla-Wiki at
  *
- * Gets applied for all requests to mozilla.org and subdomains made on
- * Firefox Desktop:
- *
- * ```
- *   {
- *     baseDomain: "mozilla.org",
- *     uriMatcher: (uri) => uri.includes("/app/"),
- *     uaTransformer: (originalUA) => `Ohai Mozilla, it's me, ${originalUA}`
- *   }
- * ```
- *
- * Applies to *.example.com/app/* on Firefox for Android:
- *
- * ```
- *   {
- *     baseDomain: "example.com",
- *     applications: ["fennec"],
- *     uriMatcher: (uri) => uri.includes("/app/"),
- *     uaTransformer: (originalUA) => originalUA.replace("Firefox", "Otherfox")
- *   }
- * ```
+ * https://wiki.mozilla.org/Compatibility/Go_Faster_Addon/Override_Policies_and_Workflows#User_Agent_overrides
  */
-
 const UAOverrides = [
 
   /*
    * This is a dummy override that applies a Chrome UA to a dummy site that
    * blocks all browsers but Chrome.
    *
    * This was only put in place to allow QA to test this system addon on an
    * actual site, since we were not able to find a proper override in time.
    */
   {
     baseDomain: "schub.io",
     applications: ["firefox", "fennec"],
-    uriMatcher: (uri) => uri.includes("webcompat-ua-dummy.schub.io"),
+    uriMatcher: (uri) => uri.includes("webcompat-addon-testcases.schub.io"),
     uaTransformer: (originalUA) => {
       let prefix = originalUA.substr(0, originalUA.indexOf(")") + 1);
       return `${prefix} AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36`;
     }
   }
 ];
 
 var EXPORTED_SYMBOLS = ["UAOverrides"]; /* exported UAOverrides */
--- a/browser/extensions/webcompat/install.rdf.in
+++ b/browser/extensions/webcompat/install.rdf.in
@@ -5,20 +5,21 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:id>webcompat@mozilla.org</em:id>
-    <em:version>1.1</em:version>
+    <em:version>2.0</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
+    <em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
 
     <!-- Firefox Desktop -->
     <em:targetApplication>
       <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
         <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
       </Description>
--- a/browser/extensions/webcompat/moz.build
+++ b/browser/extensions/webcompat/moz.build
@@ -6,16 +6,29 @@
 
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
 
 FINAL_TARGET_FILES.features['webcompat@mozilla.org'] += [
   'bootstrap.js'
 ]
 
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension'] += [
+  'webextension/background.js',
+  'webextension/manifest.json'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension']['injections']['css'] += [
+  'webextension/injections/css/bug0000000-dummy-css-injection.css'
+]
+
+FINAL_TARGET_FILES.features['webcompat@mozilla.org']['webextension']['injections']['js'] += [
+  'webextension/injections/js/bug0000000-dummy-js-injection.js'
+]
+
 FINAL_TARGET_PP_FILES.features['webcompat@mozilla.org'] += [
   'install.rdf.in'
 ]
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 JAR_MANIFESTS += ['jar.mn']
 
 with Files('**'):
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/webextension/background.js
@@ -0,0 +1,55 @@
+/**
+ * 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 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"
+  }
+];
+
+/* globals browser */
+
+let port = browser.runtime.connect();
+let registeredContentScripts = [];
+
+function registerContentScripts() {
+  contentScripts.forEach(async (contentScript) => {
+    try {
+      let handle = await browser.contentScripts.register(contentScript);
+      registeredContentScripts.push(handle);
+    } catch (ex) {
+      console.error("Registering WebCompat GoFaster content scripts failed: ", ex);
+    }
+  });
+}
+
+function unregisterContentScripts() {
+  registeredContentScripts.forEach((contentScript) => {
+    contentScript.unregister();
+  });
+}
+
+port.onMessage.addListener((message) => {
+  switch (message.type) {
+    case "injection-pref-changed":
+      if (message.prefState) {
+        registerContentScripts();
+      } else {
+        unregisterContentScripts();
+      }
+      break;
+  }
+});
+
+/**
+ * Note that we reset all preferences on extension startup, so the injections will
+ * never be disabled when this loads up. Because of that, we can simply register
+ * right away.
+ */
+registerContentScripts();
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/webextension/injections/css/bug0000000-dummy-css-injection.css
@@ -0,0 +1,3 @@
+#css-injection.red {
+  background-color: #0f0;
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/webextension/injections/js/bug0000000-dummy-js-injection.js
@@ -0,0 +1,11 @@
+"use strict";
+
+/* globals exportFunction */
+
+Object.defineProperty(window.wrappedJSObject, "isTestFeatureSupported", {
+  get: exportFunction(function() {
+    return true;
+  }, window),
+
+  set: exportFunction(function() {}, window)
+});
new file mode 100644
--- /dev/null
+++ b/browser/extensions/webcompat/webextension/manifest.json
@@ -0,0 +1,23 @@
+{
+  "manifest_version": 2,
+  "name": "Web Compat",
+  "description": "Urgent post-release fixes for web compatibility.",
+  "version": "2.0",
+
+  "applications": {
+    "gecko": {
+      "id": "webcompat@mozilla.org",
+      "strict_min_version": "59.0b5"
+    }
+  },
+
+  "permissions": [
+    "<all_urls>"
+  ],
+
+  "background": {
+    "scripts": [
+      "background.js"
+    ]
+  }
+}