Bug 1190995 - backout 0450f02a2b3b for M1 failures
authorFabrice Desré <fabrice@mozilla.com>
Fri, 14 Aug 2015 17:57:34 -0700
changeset 257907 2652473796381aa8e2dd247f287abf8289c0d65e
parent 257906 33055236649c9cd4f66571064cf7e3b2ea97a85e
child 257908 fca7b12ccd91d05bcbb504021d8d443f2c921cf2
push id17014
push userfdesre@mozilla.com
push dateSat, 15 Aug 2015 00:57:44 +0000
treeherderb2g-inbound@265247379638 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1190995
milestone43.0a1
Bug 1190995 - backout 0450f02a2b3b for M1 failures
b2g/app/b2g.js
b2g/components/ProcessGlobal.js
b2g/installer/package-manifest.in
dom/apps/UserCustomizations.jsm
dom/apps/Webapps.jsm
dom/apps/tests/addons/application.zip
dom/apps/tests/addons/index.html
dom/apps/tests/addons/manifest.json
dom/apps/tests/addons/manifest.webapp
dom/apps/tests/addons/script.js
dom/apps/tests/addons/script2.js
dom/browser-element/BrowserElementChildPreload.js
dom/browser-element/BrowserElementParent.js
dom/ipc/extensions.js
dom/ipc/jar.mn
dom/ipc/preload.js
netwerk/protocol/res/SubstitutingProtocolHandler.cpp
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/ExtensionManagement.jsm
toolkit/components/extensions/ExtensionStorage.jsm
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/ext-alarms.js
toolkit/components/extensions/ext-notifications.js
toolkit/components/extensions/ext-runtime.js
toolkit/components/extensions/ext-storage.js
toolkit/components/extensions/ext-webNavigation.js
toolkit/components/extensions/ext-webRequest.js
toolkit/modules/addons/MatchPattern.jsm
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1169,8 +1169,9 @@ pref("dom.audiochannel.mutedByDefault", 
 // requests.
 pref("dom.bluetooth.app-origin", "app://bluetooth.gaiamobile.org");
 
 // Default device name for Presentation API
 pref("dom.presentation.device.name", "Firefox OS");
 
 // Enable notification of performance timing
 pref("dom.performance.enable_notify_performance_timing", true);
+
--- a/b2g/components/ProcessGlobal.js
+++ b/b2g/components/ProcessGlobal.js
@@ -160,17 +160,19 @@ ProcessGlobal.prototype = {
   observe: function pg_observe(subject, topic, data) {
     switch (topic) {
     case 'app-startup': {
       Services.obs.addObserver(this, 'console-api-log-event', false);
       let inParent = Cc["@mozilla.org/xre/app-info;1"]
                        .getService(Ci.nsIXULRuntime)
                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
       if (inParent) {
-        Services.ppmm.addMessageListener("getProfD", function(message) {
+        let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+                     .getService(Ci.nsIMessageListenerManager);
+        ppmm.addMessageListener("getProfD", function(message) {
           return Services.dirsvc.get("ProfD", Ci.nsIFile).path;
         });
 
         this.cleanupAfterFactoryReset();
       }
       break;
     }
     case 'console-api-log-event': {
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -1001,11 +1001,8 @@ bin/libfreebl_32int64_3.so
 #ifdef MOZ_EME
 @RESPATH@/gmp-clearkey/0.1/@DLL_PREFIX@clearkey@DLL_SUFFIX@
 @RESPATH@/gmp-clearkey/0.1/clearkey.info
 #endif
 
 #ifdef PKG_LOCALE_MANIFEST
 #include @PKG_LOCALE_MANIFEST@
 #endif
-
-@RESPATH@/components/simpleServices.js
-@RESPATH@/components/utils.manifest
--- a/dom/apps/UserCustomizations.jsm
+++ b/dom/apps/UserCustomizations.jsm
@@ -7,69 +7,369 @@
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["UserCustomizations"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Extension.jsm");
+Cu.import("resource://gre/modules/AppsUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+                                   "@mozilla.org/parentprocessmessagemanager;1",
+                                   "nsIMessageBroadcaster");
+
+XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
+                                   "@mozilla.org/childprocessmessagemanager;1",
+                                   "nsIMessageSender");
 
 XPCOMUtils.defineLazyServiceGetter(this, "console",
                                    "@mozilla.org/consoleservice;1",
                                    "nsIConsoleService");
+/**
+  * Customization scripts and CSS stylesheets can be specified in an
+  * application manifest with the following syntax:
+  * "customizations": [
+  *  {
+  *    "filter": "http://youtube.com",
+  *    "css": ["file1.css", "file2.css"],
+  *    "scripts": ["script1.js", "script2.js"]
+  *  }
+  * ]
+  */
 
 function debug(aMsg) {
   if (!UserCustomizations._debug) {
     return;
   }
-  dump("-*-*- UserCustomizations " + aMsg + "\n");
+  dump("-*-*- UserCustomizations (" +
+           (UserCustomizations._inParent ? "parent" : "child") +
+           "): " + aMsg + "\n");
 }
 
 function log(aStr) {
   console.logStringMessage(aStr);
 }
 
 this.UserCustomizations = {
-  extensions: new Map(), // id -> extension. Needed to disable extensions.
+  _debug: false,
+  _items: [],
+  _loaded : {},   // Keep track per manifestURL of css and scripts loaded.
+  _windows: null, // Set of currently opened windows.
+  _enabled: false,
+
+  _addItem: function(aItem) {
+    debug("_addItem: " + uneval(aItem));
+    this._items.push(aItem);
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Add", [aItem]);
+    }
+  },
 
-  register: function(aApp) {
+  _removeItem: function(aHash) {
+    debug("_removeItem: " + aHash);
+    let index = -1;
+    this._items.forEach((script, pos) => {
+      if (script.hash == aHash ) {
+        index = pos;
+      }
+    });
+
+    if (index != -1) {
+      this._items.splice(index, 1);
+    }
+
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Remove", aHash);
+    }
+  },
+
+  register: function(aManifest, aApp) {
+    debug("Starting customization registration for " + aApp.manifestURL);
+
     if (!this._enabled || !aApp.enabled || aApp.role != "addon") {
       debug("Rejecting registration (global enabled=" + this._enabled +
             ") (app role=" + aApp.role +
             ", enabled=" + aApp.enabled + ")");
+      debug(uneval(aApp));
+      return;
+    }
+
+    let customizations = aManifest.customizations;
+    if (customizations === undefined || !Array.isArray(customizations)) {
       return;
     }
 
-    debug("Starting customization registration for " + aApp.manifestURL + "\n");
+    let base = Services.io.newURI(aApp.origin, null, null);
+
+    customizations.forEach(item => {
+      // The filter property is mandatory.
+      if (!item.filter || (typeof item.filter !== "string")) {
+        log("Mandatory filter property not found in this customization item: " +
+            uneval(item) + " in " + aApp.manifestURL);
+        return;
+      }
 
-    let extension = new Extension({
-      id: aApp.manifestURL,
-      resourceURI: Services.io.newURI(aApp.origin + "/", null, null)
+      // Create a new object with resolved urls and a hash that we reuse to
+      // remove items.
+      let custom = {
+        filter: item.filter,
+        status: aApp.appStatus,
+        manifestURL: aApp.manifestURL,
+        css: [],
+        scripts: []
+      };
+      custom.hash = AppsUtils.computeObjectHash(item);
+
+      if (item.css && Array.isArray(item.css)) {
+        item.css.forEach((css) => {
+          custom.css.push(base.resolve(css));
+        });
+      }
+
+      if (item.scripts && Array.isArray(item.scripts)) {
+        item.scripts.forEach((script) => {
+          custom.scripts.push(base.resolve(script));
+        });
+      }
+
+      this._addItem(custom);
     });
-
-    this.extensions.set(aApp.manifestURL, extension);
-    extension.startup();
+    this._updateAllWindows();
   },
 
-  unregister: function(aApp) {
+  _updateAllWindows: function() {
+    debug("UpdateWindows");
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:UpdateWindows", {});
+    }
+    // Inject in all currently opened windows.
+    this._windows.forEach(this._injectInWindow.bind(this));
+  },
+
+  unregister: function(aManifest, aApp) {
     if (!this._enabled) {
       return;
     }
 
     debug("Starting customization unregistration for " + aApp.manifestURL);
-    if (this.extensions.has(aApp.manifestURL)) {
-      this.extensions.get(aApp.manifestURL).shutdown();
-      this.extensions.delete(aApp.manifestURL);
+    let customizations = aManifest.customizations;
+    if (customizations === undefined || !Array.isArray(customizations)) {
+      return;
+    }
+
+    customizations.forEach(item => {
+      this._removeItem(AppsUtils.computeObjectHash(item));
+    });
+    this._unloadForManifestURL(aApp.manifestURL);
+  },
+
+  _unloadForManifestURL: function(aManifestURL) {
+    debug("_unloadForManifestURL " + aManifestURL);
+
+    if (this._inParent) {
+      ppmm.broadcastAsyncMessage("UserCustomizations:Unload", aManifestURL);
+    }
+
+    if (!this._loaded[aManifestURL]) {
+      return;
+    }
+
+    if (this._loaded[aManifestURL].scripts &&
+        this._loaded[aManifestURL].scripts.length > 0) {
+      // We can't rollback script changes, so don't even try to unload in this
+      // situation.
+      return;
+    }
+
+    this._loaded[aManifestURL].css.forEach(aItem => {
+      try {
+        debug("unloading " + aItem.uri.spec);
+        let utils = aItem.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDOMWindowUtils);
+        utils.removeSheet(aItem.uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
+      } catch(e) {
+        log("Error unloading stylesheet " + aItem.uri.spec + " : " + e);
+      }
+    });
+
+    this._loaded[aManifestURL] = null;
+  },
+
+  _injectItem: function(aWindow, aItem, aInjected) {
+    debug("Injecting item " + uneval(aItem) + " in " + aWindow.location.href);
+    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+
+    let manifestURL = aItem.manifestURL;
+
+    // Load the stylesheets only in this window.
+    aItem.css.forEach(aCss => {
+      if (aInjected.indexOf(aCss) !== -1) {
+        debug("Skipping duplicated css: " + aCss);
+        return;
+      }
+
+      let uri = Services.io.newURI(aCss, null, null);
+      try {
+        utils.loadSheet(uri, Ci.nsIDOMWindowUtils.AUTHOR_SHEET);
+        if (!this._loaded[manifestURL]) {
+          this._loaded[manifestURL] = { css: [], scripts: [] };
+        }
+        this._loaded[manifestURL].css.push({ window: aWindow, uri: uri });
+        aInjected.push(aCss);
+      } catch(e) {
+        log("Error loading stylesheet " + aCss + " : " + e);
+      }
+    });
+
+    let sandbox;
+    if (aItem.scripts.length > 0) {
+      sandbox = Cu.Sandbox([aWindow],
+                           { wantComponents: false,
+                             sandboxPrototype: aWindow });
+    }
+
+    // Load the scripts using a sandbox.
+    aItem.scripts.forEach(aScript => {
+      debug("Sandboxing " + aScript);
+      if (aInjected.indexOf(aScript) !== -1) {
+        debug("Skipping duplicated script: " + aScript);
+        return;
+      }
+
+      try {
+        let options = {
+          target: sandbox,
+          charset: "UTF-8",
+          async: true
+        }
+        Services.scriptloader.loadSubScriptWithOptions(aScript, options);
+        if (!this._loaded[manifestURL]) {
+          this._loaded[manifestURL] = { css: [], scripts: [] };
+        }
+        this._loaded[manifestURL].scripts.push({ sandbox: sandbox, uri: aScript });
+        aInjected.push(aScript);
+      } catch(e) {
+        log("Error sandboxing " + aScript + " : " + e);
+      }
+    });
+
+    // Makes sure we get rid of the sandbox.
+    if (sandbox) {
+      aWindow.addEventListener("unload", () => {
+        Cu.nukeSandbox(sandbox);
+        sandbox = null;
+      });
+    }
+  },
+
+  _injectInWindow: function(aWindow) {
+    debug("_injectInWindow");
+
+    if (!aWindow || !aWindow.document) {
+      return;
+    }
+
+    let principal = aWindow.document.nodePrincipal;
+    debug("principal status: " + principal.appStatus);
+
+    let href = aWindow.location.href;
+
+    // The list of resources loaded in this window, used to filter out
+    // duplicates.
+    let injected = [];
+
+    this._items.forEach((aItem) => {
+      // We only allow customizations to apply to apps with an equal or lower
+      // privilege level.
+      if (principal.appStatus > aItem.status) {
+        return;
+      }
+
+      let regexp = new RegExp(aItem.filter, "g");
+      if (regexp.test(href)) {
+        this._injectItem(aWindow, aItem, injected);
+        debug("Currently injected: " + injected.toString());
+      }
+    });
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic === "content-document-global-created") {
+      let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
+      let href = window.location.href;
+      if (!href || href == "about:blank") {
+        return;
+      }
+
+      let id = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindowUtils)
+                     .currentInnerWindowID;
+      this._windows.set(id, window);
+
+      debug("document created: " + href);
+      this._injectInWindow(window);
+    } else if (aTopic === "inner-window-destroyed") {
+      let winId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+      this._windows.delete(winId);
     }
   },
 
   init: function() {
     this._enabled = false;
     try {
       this._enabled = Services.prefs.getBoolPref("dom.apps.customization.enabled");
     } catch(e) {}
+
+    if (!this._enabled) {
+      return;
+    }
+
+    this._windows = new Map(); // Can't be a WeakMap because we need to enumerate.
+    this._inParent = Cc["@mozilla.org/xre/runtime;1"]
+                       .getService(Ci.nsIXULRuntime)
+                       .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+
+    debug("init");
+
+    Services.obs.addObserver(this, "content-document-global-created",
+                             /* ownsWeak */ false);
+    Services.obs.addObserver(this, "inner-window-destroyed",
+                             /* ownsWeak */ false);
+
+    if (this._inParent) {
+      ppmm.addMessageListener("UserCustomizations:List", this);
+    } else {
+      cpmm.addMessageListener("UserCustomizations:Add", this);
+      cpmm.addMessageListener("UserCustomizations:Remove", this);
+      cpmm.addMessageListener("UserCustomizations:Unload", this);
+      cpmm.addMessageListener("UserCustomizations:UpdateWindows", this);
+      cpmm.sendAsyncMessage("UserCustomizations:List", {});
+    }
   },
+
+  receiveMessage: function(aMessage) {
+    let name = aMessage.name;
+    let data = aMessage.data;
+
+    switch(name) {
+      case "UserCustomizations:List":
+        aMessage.target.sendAsyncMessage("UserCustomizations:Add", this._items);
+        break;
+      case "UserCustomizations:Add":
+        data.forEach(this._addItem, this);
+        break;
+      case "UserCustomizations:Remove":
+        this._removeItem(data);
+        break;
+      case "UserCustomizations:Unload":
+        this._unloadForManifestURL(data);
+        break;
+      case "UserCustomizations:UpdateWindows":
+        this._updateAllWindows();
+        break;
+    }
+  }
 }
 
 UserCustomizations.init();
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -434,17 +434,17 @@ this.DOMApplicationRegistry = {
 
         let localeManifest = new ManifestHelper(aResult.manifest, app.origin, app.manifestURL);
         this._saveWidgetsFullPath(localeManifest, app);
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(aResult.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
-        UserCustomizations.register(app);
+        UserCustomizations.register(aResult.manifest, app);
         Langpacks.register(app, aResult.manifest);
       });
 
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
   }),
 
@@ -1159,17 +1159,17 @@ this.DOMApplicationRegistry = {
 
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         app.kind = this.appKind(app, aResult.manifest);
         this._registerSystemMessages(manifest, app);
         this._registerInterAppConnections(manifest, app);
         appsToRegister.push({ manifest: manifest, app: app });
-        UserCustomizations.register(app);
+        UserCustomizations.register(manifest, app);
         Langpacks.register(app, manifest);
       });
       this._safeToClone.resolve();
       this._registerActivitiesForApps(appsToRegister, aRunUpdate);
     });
   },
 
   observe: function(aSubject, aTopic, aData) {
@@ -2008,20 +2008,20 @@ this.DOMApplicationRegistry = {
       this._registerInterAppConnections(aNewManifest, aApp);
     } else {
       // Nothing else to do but notifying we're ready.
       this.notifyAppsRegistryReady();
     }
 
     // Update user customizations and langpacks.
     if (aOldManifest) {
-      UserCustomizations.unregister(aApp);
+      UserCustomizations.unregister(aOldManifest, aApp);
       Langpacks.unregister(aApp, aOldManifest);
     }
-    UserCustomizations.register(aApp);
+    UserCustomizations.register(aNewManifest, aApp);
     Langpacks.register(aApp, aNewManifest);
   },
 
   checkForUpdate: function(aData, aMm) {
     debug("checkForUpdate for " + aData.manifestURL);
 
     let sendError = (aError) => {
       debug("checkForUpdate error " + aError);
@@ -4120,17 +4120,17 @@ this.DOMApplicationRegistry = {
     this._clearPrivateData(aApp.localId, false);
 
     // Then notify observers.
     Services.obs.notifyObservers(null, "webapps-uninstall", JSON.stringify(aApp));
 
     if (supportSystemMessages()) {
       this._unregisterActivities(aApp.manifest, aApp);
     }
-    UserCustomizations.unregister(aApp);
+    UserCustomizations.unregister(aApp.manifest, aApp);
     Langpacks.unregister(aApp, aApp.manifest);
 
     let dir = this._getAppDir(id);
     try {
       dir.remove(true);
     } catch (e) {}
 
     delete this.webapps[id];
@@ -4540,21 +4540,20 @@ this.DOMApplicationRegistry = {
       MessageBroadcaster.broadcastMessage("Webapps:UpdateState", {
         app: app,
         id: app.id
       });
       MessageBroadcaster.broadcastMessage("Webapps:SetEnabled:Return", app);
     });
 
     // Update customization.
-    if (app.enabled) {
-      UserCustomizations.register(app);
-    } else {
-      UserCustomizations.unregister(app);
-    }
+    this.getManifestFor(app.manifestURL).then((aManifest) => {
+      app.enabled ? UserCustomizations.register(aManifest, app)
+                  : UserCustomizations.unregister(aManifest, app);
+    });
   },
 
   getManifestFor: function(aManifestURL, aEntryPoint) {
     let id = this._appIdForManifestURL(aManifestURL);
     let app = this.webapps[id];
     if (!id || (app.installState == "pending" && !app.retryingDownload)) {
       return Promise.resolve(null);
     }
index 2d2ece845300a67ebd38ee5da44e12fa4acd408f..543587c8869ba38506c5889fa66d28755389274b
GIT binary patch
literal 1725
zc$^FHW@Zs#U|`^25Gk18%CYF^3_V5$1}P>61}+8}hRnQ_)C#?flH8oo5Kac>_SF+Y
zK)AGmn}Lz#1v3K!n3x)L(2LnnVDESBW45{cWiR9I>Se4|=nz}ebiBm)E!(D<s<R`)
zU+%Z|j1sY_^i-St{hm!+Q0yH$UGx0cqFdHo{<_3V+TrS=2@4-w(p<jr-j=L}xHp1)
zUfDO4N>r};Eo^zqvCdT>C~nfqt``403pcJR|C;jL_pyki#Dx>tz1MilQ{+99@+xK?
z5`XOa;`rw?58I<B6=vM|(zX6xYipZHWW<LW<(CGxm6dnhvz~H3?~3}u3vtn}A~S15
zZza!jjT1|~)N<p&=fY2qPghM=7q6G~%Bnf;*YwRRStI{g^~`idm6Vq&{^rL?b{-cA
zDfH92^8CYU-IIbRGK7jIx^~~xyivy2Rjtncl{xZ_!`J5bGwke}PsJ}VS{?86nB9Hu
zuP5QzL3?=gFJCd}+3z6u|A2V_Jj64rC%8WS_95;NkQWUMb$(!o=O*T5rll5_=#{4?
zB^DHbgZ<k62_bj(V+4EXgoAv|3OsG^?Oc!F^mvr{aog{E2i`a|xb`$`ygTXak9W<B
zP9IzN^8Wlf-8G^O9H*i$HtvuRW?oS#R;nl6T~%njQ({x~R+f8B+>5WW9Ay&a$v6`H
z@&f;o7k8td%86WXoPUZz`*_L2cWRrwm+`t3t1s=ZD&UP=@@`Jw8A&hUy*}9w^g{1(
zSF!|OI?(ch^-}gG-!M~=i*l!0m(QB>(>U_ZzA2xY<{qguo_1R2R;=ujc>Qka;%y?5
zn$s*ks(;$F=+-L!0F=bHFGBNs1CTcZh&h2lQJh?qSx};vRg5p~nfC7GI%FVl{JY)J
z%3!8T;v9Ne3tPp+4_bcl<2_>Sak;7EeV);dzd!0HoqOG_x_#b?+gv?~Cw1oV%+tT@
zsT%HUYxq#^-Rd)zD<rt*vn71yocD&4&*A@E_UC#<M;})!G+%w*a&2nOQ!|gq_v<f5
zMBOOv;WGD~SoUI8{>~4DiLdJrUJzQ|=PE0{N}(0Vo6i8rKM*e%!Ti9I)fXZM#29|?
z1^J=%yX{ef)htW6w;$}?ny~O_uQ<odK<2kBleR2)bk|p){C)d(JFdLgh@$MyUu!!q
zE<bZ9@Zn3NQpcjRO{!{l4(5tZuN7GO@Ge7+*`a3LgN!f!7eBvqKIZ91%L#{W72jEG
zmKxyudg-0{KyReoSgYB7bpMP4e-koh`77SE+-MdL@(33L2!P_EEMu;cB#<o$i;I%V
zoK(H!V!UyooRONCl3Jut&847_oS&0lWTjA)nqtjWi!fUi7)UQ|^zx;FY-v~^fy_37
z8?FirAS|gJYPb=y=}9@IsbJ$7ndF#pm0}WLi-BB*EsY=+a+$^o@e7(&$mU_@QDpOq
zfD}fKWrgHeJceSXc4R|qfzC!s`-I(ynevd0b6{pbNr$YEbcoeFTnPZ#JWdt{<m7<Q
hdAQ;n*|=1gaYk_SRIxdal?|ka4G13tH6CFB@c`i<Q~&?~
--- a/dom/apps/tests/addons/index.html
+++ b/dom/apps/tests/addons/index.html
@@ -5,22 +5,18 @@
     <script>
 function sendAlertsForNode(node) {
   alert(node.textContent);
   var color = window.getComputedStyle(node).getPropertyValue("color");
   alert(color);
 }
 
 function run() {
-  // We need to wait for the next tick because add-ons are injected in the
-  // onload event too.
-  window.setTimeout(function() {
-    sendAlertsForNode(document.getElementById("header"));
-    sendAlertsForNode(document.getElementById("header2"));
-  }, 0);
+  sendAlertsForNode(document.getElementById("header"));
+  sendAlertsForNode(document.getElementById("header2"));
 }
     </script>
   </head>
   <body onload="run()">
     <h1 id="header">Lorem ipsum</h1>
     <h2 id="header2">Uncustomized content</h2>
   </body>
 </html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/apps/tests/addons/manifest.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "name": "Addon app",
-  "version": "1.0",
-  "manifest_version": 2,
-  "permissions": ["tabs"],
-  "description": "Let me inject script and css!",
-  "content_scripts": [
-    {"matches": ["http://mochi.test/tests/dom/apps/tests/addons/index.html"],
-     "js": ["script.js", "script2.js", "invalid.js", "script.js"],
-     "css": ["style.css", "style2.css"]}
-  ]
-}
--- a/dom/apps/tests/addons/manifest.webapp
+++ b/dom/apps/tests/addons/manifest.webapp
@@ -1,5 +1,12 @@
 {
   "name": "Addon app",
   "description": "Let me inject script and css!",
+  "customizations" : [
+    {
+      "filter": "http://mochi.test:8888/tests/dom/apps/tests/addons",
+      "css": ["style.css", "style2.css", "invalid.css", "style.css"],
+      "scripts": ["script.js", "script2.js", "invalid.js", "script.js"]
+    }
+  ],
   "role": "addon"
 }
--- a/dom/apps/tests/addons/script.js
+++ b/dom/apps/tests/addons/script.js
@@ -1,5 +1,4 @@
-// Simple script that changes an element's content.
-
-var head = document.getElementById("header");
-head.innerHTML = "Hello World!";
-
+document.addEventListener("DOMContentLoaded", function() {
+  var head = document.getElementById("header");
+  head.innerHTML = "Hello World!";
+}, false);
--- a/dom/apps/tests/addons/script2.js
+++ b/dom/apps/tests/addons/script2.js
@@ -1,6 +1,4 @@
-// Simple script that changes an element's content.
-
-var head = document.getElementById("header2");
-head.innerHTML = "Customized content";
-
-
+document.addEventListener("DOMContentLoaded", function() {
+  var head = document.getElementById("header2");
+  head.innerHTML = "Customized content";
+}, false);
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1727,8 +1727,9 @@ BrowserElementChild.prototype = {
   },
 
   get messageManager() {
     return this._messageManagerPublic;
   }
 };
 
 var api = new BrowserElementChild();
+
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -229,18 +229,16 @@ BrowserElementParent.prototype = {
 
       if (aMsg.data.msg_name in mmCalls) {
         return mmCalls[aMsg.data.msg_name].apply(self, arguments);
       } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) {
         return mmSecuritySensitiveCalls[aMsg.data.msg_name]
                  .apply(self, arguments);
       }
     });
-
-    this._mm.loadFrameScript("chrome://global/content/extensions.js", true);
   },
 
   /**
    * You shouldn't touch this._frameElement or this._window if _isAlive is
    * false.  (You'll likely get an exception if you do.)
    */
   _isAlive: function() {
     return !Cu.isDeadWrapper(this._frameElement) &&
deleted file mode 100644
--- a/dom/ipc/extensions.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-dump("######################## extensions.js loaded\n");
-
-Components.utils.import("resource://gre/modules/ExtensionContent.jsm");
-
-ExtensionContent.init(this);
-
-addEventListener("unload", () => {
-  ExtensionContent.uninit(this);
-});
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -5,11 +5,10 @@
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
         content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
         content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
 *       content/global/BrowserElementPanningAPZDisabled.js (../browser-element/BrowserElementPanningAPZDisabled.js)
-        content/global/extensions.js (extensions.js)
         content/global/manifestMessages.js (manifestMessages.js)
         content/global/preload.js (preload.js)
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -22,16 +22,21 @@ const BrowserElementIsPreloaded = true;
   Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
   Cu.import("resource://gre/modules/FileUtils.jsm");
   Cu.import("resource://gre/modules/Geometry.jsm");
   Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
   Cu.import("resource://gre/modules/NetUtil.jsm");
   Cu.import("resource://gre/modules/Services.jsm");
   Cu.import("resource://gre/modules/SettingsDB.jsm");
   Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+  try {
+    if (Services.prefs.getBoolPref("dom.apps.customization.enabled")) {
+      Cu.import("resource://gre/modules/UserCustomizations.jsm");
+    }
+  } catch(e) {}
 
   Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
   Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
   Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
   Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
   Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
   Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
   Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
--- a/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
+++ b/netwerk/protocol/res/SubstitutingProtocolHandler.cpp
@@ -303,18 +303,17 @@ SubstitutingProtocolHandler::SetSubstitu
     return NS_OK;
   }
 
   // If baseURI isn't a same-scheme URI, we can set the substitution immediately.
   nsAutoCString scheme;
   nsresult rv = baseURI->GetScheme(scheme);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!scheme.Equals(mScheme)) {
-    if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")
-        && !scheme.EqualsLiteral("app")) {
+    if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")) {
       NS_WARNING("Refusing to create substituting URI to non-file:// target");
       return NS_ERROR_INVALID_ARG;
     }
 
     mSubstitutions.Put(root, baseURI);
     SendSubstitution(root, baseURI);
     return NS_OK;
   }
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1,15 +1,15 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["Extension"];
+const EXPORTED_SYMBOLS = ["Extension"];
 
 /*
  * This file is the main entry point for extensions. When an extension
  * loads, its bootstrap.js file creates a Extension instance
  * and calls .startup() on it. It calls .shutdown() when the extension
  * unloads. Extension manages any extension-specific state in
  * the chrome process.
  */
@@ -301,17 +301,17 @@ let GlobalManager = {
       context.unload();
     };
     eventHandler.addEventListener("unload", listener, true);
   },
 };
 
 // We create one instance of this class per extension. |addonData|
 // comes directly from bootstrap.js when initializing.
-this.Extension = function(addonData)
+function Extension(addonData)
 {
   let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
   let uuid = uuidGenerator.generateUUID().number;
   uuid = uuid.substring(1, uuid.length - 1); // Strip of { and } off the UUID.
   this.uuid = uuid;
 
   this.addonData = addonData;
   this.id = addonData.id;
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -1,30 +1,29 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["ExtensionContent"];
+const EXPORTED_SYMBOLS = ["ExtensionContent"];
 
 /*
  * This file handles the content process side of extensions. It mainly
  * takes care of content script injection, content script APIs, and
  * messaging.
  */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
                                   "resource://gre/modules/ExtensionManagement.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
@@ -143,30 +142,18 @@ Script.prototype = {
         let url = "data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode);
         runSafeWithoutClone(winUtils.loadSheetUsingURIString, url, winUtils.AUTHOR_SHEET);
       }
     }
 
     let scheduled = this.run_at || "document_idle";
     if (shouldRun(scheduled)) {
       for (let url of this.js) {
-        // On gonk we need to load the resources asynchronously because the
-        // app: channels only support asyncOpen. This is safe only in the
-        // `document_idle` state.
-        if (AppConstants.platform == "gonk" && scheduled != "document_idle") {
-          Cu.reportError(`Script injection: ignoring ${url} at ${scheduled}`);
-        }
         url = extension.baseURI.resolve(url);
-
-        let options = {
-          target: sandbox,
-          charset: "UTF-8",
-          async: AppConstants.platform == "gonk"
-        }
-        Services.scriptloader.loadSubScriptWithOptions(url, options);
+        Services.scriptloader.loadSubScript(url, sandbox);
       }
 
       if (this.options.jsCode) {
         Cu.evalInSandbox(this.options.jsCode, sandbox, "latest");
       }
     }
   },
 };
@@ -233,17 +220,16 @@ ExtensionContext.prototype = {
   },
 
   execute(script, shouldRun) {
     script.tryInject(this.extension, this.contentWindow, this.sandbox, shouldRun);
   },
 
   callOnClose(obj) {
     this.onClose.add(obj);
-    Cu.nukeSandbox(this.sandbox);
   },
 
   forgetOnClose(obj) {
     this.onClose.delete(obj);
   },
 
   close() {
     for (let obj of this.onClose) {
@@ -485,17 +471,17 @@ let ExtensionManager = {
       extension.shutdown();
       DocumentManager.shutdownExtension(data.id);
       this.extensions.delete(data.id);
       break;
     }
   }
 };
 
-this.ExtensionContent = {
+let ExtensionContent = {
   globals: new Map(),
 
   init(global) {
     let broker = new MessageBroker([global]);
     this.globals.set(global, broker);
 
     global.addMessageListener("Extension:Execute", this);
 
--- a/toolkit/components/extensions/ExtensionManagement.jsm
+++ b/toolkit/components/extensions/ExtensionManagement.jsm
@@ -1,15 +1,15 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["ExtensionManagement"];
+const EXPORTED_SYMBOLS = ["ExtensionManagement"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -183,18 +183,19 @@ let Service = {
     }
 
     let uuid = uri.host;
     let extension = this.uuidMap.get(uuid);
     return extension ? extension.id : undefined;
   },
 };
 
-this.ExtensionManagement = {
+let ExtensionManagement = {
   startupExtension: Service.startupExtension.bind(Service),
   shutdownExtension: Service.shutdownExtension.bind(Service),
 
   registerScript: Scripts.register.bind(Scripts),
   getScripts: Scripts.getScripts.bind(Scripts),
 
   getFrameId: Frames.getId.bind(Frames),
   getParentFrameId: Frames.getParentId.bind(Frames),
 };
+
--- a/toolkit/components/extensions/ExtensionStorage.jsm
+++ b/toolkit/components/extensions/ExtensionStorage.jsm
@@ -1,30 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = ["ExtensionStorage"];
+const EXPORTED_SYMBOLS = ["ExtensionStorage"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/osfile.jsm")
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 
 let Path = OS.Path;
 let profileDir = OS.Constants.Path.profileDir;
 
-this.ExtensionStorage = {
+let ExtensionStorage = {
   cache: new Map(),
   listeners: new Map(),
 
   extensionDir: Path.join(profileDir, "browser-extension-data"),
 
   getExtensionDir(extensionId) {
     return Path.join(this.extensionDir, extensionId);
   },
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -1,15 +1,15 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = ["ExtensionUtils"];
+const EXPORTED_SYMBOLS = ["ExtensionUtils"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -522,18 +522,17 @@ Messenger.prototype = {
       this.broker.addListener("connect", listener, this.filter);
       return () => {
         this.broker.removeListener("connect", listener);
       };
     }).api();
   },
 };
 
-this.ExtensionUtils = {
-  runSafeWithoutClone,
+let ExtensionUtils = {
   runSafe,
   DefaultWeakMap,
   EventManager,
   SingletonEventManager,
   ignoreEvent,
   injectAPI,
   MessageBroker,
   Messenger,
--- a/toolkit/components/extensions/ext-alarms.js
+++ b/toolkit/components/extensions/ext-alarms.js
@@ -1,10 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 let {
   EventManager,
   ignoreEvent,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> Set[Alarm]]
 let alarmsMap = new WeakMap();
--- a/toolkit/components/extensions/ext-notifications.js
+++ b/toolkit/components/extensions/ext-notifications.js
@@ -1,10 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 let {
   EventManager,
   ignoreEvent,
 } = ExtensionUtils;
 
 // WeakMap[Extension -> Set[Notification]]
 let notificationsMap = new WeakMap();
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/ext-runtime.js
@@ -1,10 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 let {
   EventManager,
   ignoreEvent,
 } = ExtensionUtils;
 
 extensions.registerAPI((extension, context) => {
   return {
--- a/toolkit/components/extensions/ext-storage.js
+++ b/toolkit/components/extensions/ext-storage.js
@@ -1,10 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
                                   "resource://gre/modules/ExtensionStorage.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 let {
   EventManager,
   ignoreEvent,
   runSafe,
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -1,12 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
                                   "resource://gre/modules/ExtensionManagement.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigation",
                                   "resource://gre/modules/WebNavigation.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -1,12 +1,8 @@
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
                                   "resource://gre/modules/WebRequest.jsm");
 
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 let {
   SingletonEventManager,
--- a/toolkit/modules/addons/MatchPattern.jsm
+++ b/toolkit/modules/addons/MatchPattern.jsm
@@ -1,19 +1,19 @@
 /* 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";
 
 const Cu = Components.utils;
 
-this.EXPORTED_SYMBOLS = ["MatchPattern"];
+const EXPORTED_SYMBOLS = ["MatchPattern"];
 
-const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "app"];
+const PERMITTED_SCHEMES = ["http", "https", "file", "ftp"];
 
 // This function converts a glob pattern (containing * and possibly ?
 // as wildcards) to a regular expression.
 function globToRegexp(pat, allowQuestion)
 {
   // Escape everything except ? and *.
   pat = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&");
 
@@ -32,17 +32,17 @@ function SingleMatchPattern(pat)
 {
   if (pat == "<all_urls>") {
     this.scheme = PERMITTED_SCHEMES;
     this.host = "*";
     this.path = new RegExp('.*');
   } else if (!pat) {
     this.scheme = [];
   } else {
-    let re = new RegExp("^(http|https|file|ftp|app|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$");
+    let re = new RegExp("^(http|https|file|ftp|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$");
     let match = re.exec(pat);
     if (!match) {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.scheme = [];
       return;
     }
 
     if (match[1] == '*') {
@@ -86,17 +86,17 @@ SingleMatchPattern.prototype = {
     if (!ignorePath && !this.path.test(uri.path)) {
       return false;
     }
 
     return true;
   }
 };
 
-this.MatchPattern = function(pat)
+function MatchPattern(pat)
 {
   this.pat = pat;
   if (!pat) {
     this.matchers = [];
   } else if (pat instanceof String || typeof(pat) == "string") {
     this.matchers = [new SingleMatchPattern(pat)];
   } else {
     this.matchers = [for (p of pat) new SingleMatchPattern(p)];