Bug 1446585: Remove support for resource entries in bootstrapped chrome.manifest files. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 16 Mar 2018 20:18:46 -0700
changeset 769180 9f91a3b882a06915d61a19f8aa80eb19eb988b53
parent 768843 74f745d7c4e0a7b0c84be6ede6a20311304042c5
child 769181 3b302b24bd91ddb622e993f54e9eb903abcb4483
push id103061
push usermaglione.k@gmail.com
push dateSun, 18 Mar 2018 22:05:48 +0000
reviewersaswan
bugs1446585
milestone61.0a1
Bug 1446585: Remove support for resource entries in bootstrapped chrome.manifest files. r?aswan MozReview-Commit-ID: EjymzU6koYX
browser/extensions/activity-stream/bootstrap.js
browser/extensions/formautofill/bootstrap.js
browser/extensions/formautofill/test/unit/head.js
browser/extensions/onboarding/bootstrap.js
browser/extensions/onboarding/test/unit/head.js
toolkit/mozapps/extensions/ChromeManifestParser.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/moz.build
toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf
toolkit/mozapps/extensions/test/browser/addons/browser_update1_1.xpi
toolkit/mozapps/extensions/test/browser/addons/browser_update1_1/bootstrap.js
toolkit/mozapps/extensions/test/browser/addons/browser_update1_1/chrome.manifest
toolkit/mozapps/extensions/test/browser/addons/browser_update1_2.xpi
toolkit/mozapps/extensions/test/browser/addons/browser_update1_2/bootstrap.js
toolkit/mozapps/extensions/test/browser/addons/browser_update1_2/chrome.manifest
toolkit/mozapps/extensions/test/browser/browser_update.js
toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js
toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
toolkit/mozapps/extensions/test/xpcshell/test_invalid_install_rdf.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
xpcom/components/ManifestParser.cpp
--- a/browser/extensions/activity-stream/bootstrap.js
+++ b/browser/extensions/activity-stream/bootstrap.js
@@ -4,16 +4,22 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.importGlobalProperties(["fetch"]);
 
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+const RESOURCE_HOST = "activity-stream";
+
 const BROWSER_READY_NOTIFICATION = "sessionstore-windows-restored";
 const RESOURCE_BASE = "resource://activity-stream";
 
 const ACTIVITY_STREAM_OPTIONS = {newTabURL: "about:newtab"};
 
 let activityStream;
 let modulesToUnload = new Set();
 let startupData;
@@ -143,31 +149,37 @@ function observe(subject, topic, data) {
   }
 }
 
 // The functions below are required by bootstrap.js
 
 this.install = function install(data, reason) {};
 
 this.startup = function startup(data, reason) {
+  resProto.setSubstitutionWithFlags(RESOURCE_HOST,
+                                    Services.io.newURI("chrome/content/", null, data.resourceURI),
+                                    resProto.ALLOW_CONTENT_ACCESS);
+
   // Cache startup data which contains stuff like the version number, etc.
   // so we can use it when we init
   startupData = data;
   startupReason = reason;
 
   // Only start Activity Stream up when the browser UI is ready
   if (Services.startup.startingUp) {
     Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
   } else {
     // Handle manual install or automatic install after manual uninstall
     onBrowserReady();
   }
 };
 
 this.shutdown = function shutdown(data, reason) {
+  resProto.setSubstitution(RESOURCE_HOST, null);
+
   // Uninitialize Activity Stream
   startupData = null;
   startupReason = null;
   uninit(reason);
 
   // Stop waiting for browser to be ready
   if (waitingForBrowserReady) {
     Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -15,16 +15,22 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.defineModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
                                "resource://gre/modules/AddonManager.jsm");
 ChromeUtils.defineModuleGetter(this, "formAutofillParent",
                                "resource://formautofill/FormAutofillParent.jsm");
 ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
                                "resource://formautofill/FormAutofillUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+const RESOURCE_HOST = "formautofill";
+
 function insertStyleSheet(domWindow, url) {
   let doc = domWindow.document;
   let styleSheetAttr = `href="${url}" type="text/css"`;
   let styleSheet = doc.createProcessingInstruction("xml-stylesheet", styleSheetAttr);
 
   doc.insertBefore(styleSheet, doc.documentElement);
 
   if (CACHED_STYLESHEETS.has(domWindow)) {
@@ -75,16 +81,19 @@ function startup(data) {
     // reset the sync related prefs incase the feature was previously available
     // but isn't now.
     Services.prefs.clearUserPref("services.sync.engine.addresses.available");
     Services.prefs.clearUserPref("services.sync.engine.creditcards.available");
     Services.telemetry.scalarSet("formautofill.availability", false);
     return;
   }
 
+  resProto.setSubstitution(RESOURCE_HOST,
+                           Services.io.newURI("chrome/res/", null, data.resourceURI));
+
   if (data.hasOwnProperty("instanceID") && data.instanceID) {
     if (AddonManagerPrivate.isDBLoaded()) {
       addUpgradeListener(data.instanceID);
     } else {
       // Wait for the extension database to be loaded so we don't cause its init.
       Services.obs.addObserver(function xpiDatabaseLoaded() {
         Services.obs.removeObserver(xpiDatabaseLoaded, "xpi-database-loaded");
         addUpgradeListener(data.instanceID);
@@ -116,16 +125,18 @@ function startup(data) {
   formAutofillParent.init().catch(Cu.reportError);
   Services.ppmm.loadProcessScript("data:,new " + function() {
     ChromeUtils.import("resource://formautofill/FormAutofillContent.jsm");
   }, true);
   Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true);
 }
 
 function shutdown() {
+  resProto.setSubstitution(RESOURCE_HOST, null);
+
   Services.mm.removeMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
 
   let enumerator = Services.wm.getEnumerator("navigator:browser");
   while (enumerator.hasMoreElements()) {
     let win = enumerator.getNext();
     let domWindow = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
     let cachedStyleSheets = CACHED_STYLESHEETS.get(domWindow);
 
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -17,16 +17,20 @@ ChromeUtils.import("resource://testing-c
 ChromeUtils.import("resource://testing-common/MockDocument.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "DownloadPaths",
                                "resource://gre/modules/DownloadPaths.jsm");
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
 do_get_profile();
 
 // ================================================
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/releases/v2.3.2/
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
@@ -45,16 +49,19 @@ let bootstrapURI = Services.io.newFileUR
 if (!extensionDir.exists()) {
   extensionDir = extensionDir.parent;
   extensionDir.append(EXTENSION_ID + ".xpi");
   let jarURI = Services.io.newFileURI(extensionDir);
   bootstrapURI = "jar:" + jarURI.spec + "!/bootstrap.js";
 }
 Components.manager.addBootstrappedManifestLocation(extensionDir);
 
+let resURI = Services.io.newURI("chrome/res/", null, Services.io.newURI(bootstrapURI));
+resProto.setSubstitution("formautofill", resURI);
+
 // Returns a reference to a temporary file that is guaranteed not to exist and
 // is cleaned up later. See FileTestUtils.getTempFile for details.
 function getTempFile(leafName) {
   return FileTestUtils.getTempFile(leafName);
 }
 
 async function initProfileStorage(fileName, records, collectionName = "addresses") {
   let {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
--- a/browser/extensions/onboarding/bootstrap.js
+++ b/browser/extensions/onboarding/bootstrap.js
@@ -8,16 +8,22 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetters(this, {
   OnboardingTourType: "resource://onboarding/modules/OnboardingTourType.jsm",
   OnboardingTelemetry: "resource://onboarding/modules/OnboardingTelemetry.jsm",
   Services: "resource://gre/modules/Services.jsm",
   UIState: "resource://services-sync/UIState.jsm",
 });
 
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+const RESOURCE_HOST = "onboarding";
+
 const {PREF_STRING, PREF_BOOL, PREF_INT} = Ci.nsIPrefBranch;
 
 const BROWSER_READY_NOTIFICATION = "browser-delayed-startup-finished";
 const BROWSER_SESSION_STORE_NOTIFICATION = "sessionstore-windows-restored";
 const PREF_WHITELIST = [
   ["browser.onboarding.enabled", PREF_BOOL],
   ["browser.onboarding.state", PREF_STRING],
   ["browser.onboarding.notification.finished", PREF_BOOL],
@@ -192,29 +198,35 @@ function observe(subject, topic, data) {
   }
 }
 
 function install(aData, aReason) {}
 
 function uninstall(aData, aReason) {}
 
 function startup(aData, aReason) {
+  resProto.setSubstitutionWithFlags(RESOURCE_HOST,
+                                    Services.io.newURI("chrome/content/", null, aData.resourceURI),
+                                    resProto.ALLOW_CONTENT_ACCESS);
+
   // Cache startup data which contains stuff like the version number, etc.
   // so we can use it when we init the telemetry
   startupData = aData;
   // Only start Onboarding when the browser UI is ready
   if (Services.startup.startingUp) {
     Services.obs.addObserver(observe, BROWSER_READY_NOTIFICATION);
     Services.obs.addObserver(observe, BROWSER_SESSION_STORE_NOTIFICATION);
   } else {
     onBrowserReady();
     syncTourChecker.init();
   }
 }
 
 function shutdown(aData, aReason) {
+  resProto.setSubstitution(RESOURCE_HOST, null);
+
   startupData = null;
   // Stop waiting for browser to be ready
   if (waitingForBrowserReady) {
     Services.obs.removeObserver(observe, BROWSER_READY_NOTIFICATION);
   }
   syncTourChecker.uninit();
 }
--- a/browser/extensions/onboarding/test/unit/head.js
+++ b/browser/extensions/onboarding/test/unit/head.js
@@ -4,29 +4,40 @@
 
 "use strict";
 
 /* global Cc, Ci, Cu */
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
 // Load our bootstrap extension manifest so we can access our chrome/resource URIs.
 // Cargo culted from formautofill system add-on
 const EXTENSION_ID = "onboarding@mozilla.org";
 let extensionDir = Services.dirsvc.get("GreD", Ci.nsIFile);
 extensionDir.append("browser");
 extensionDir.append("features");
 extensionDir.append(EXTENSION_ID);
+let resourceURI;
 // If the unpacked extension doesn't exist, use the packed version.
 if (!extensionDir.exists()) {
   extensionDir.leafName += ".xpi";
+
+  resourceURI = "jar:" + Services.io.newFileURI(extensionDir).spec + "!/chrome/content/";
+} else {
+  resourceURI = Services.io.newFileURI(extensionDir).spec + "/chrome/content/";
 }
 Components.manager.addBootstrappedManifestLocation(extensionDir);
 
+resProto.setSubstitution("onboarding", Services.io.newURI(resourceURI));
+
 const TOURSET_VERSION = 1;
 const NEXT_TOURSET_VERSION = 2;
 const PREF_TOUR_TYPE = "browser.onboarding.tour-type";
 const PREF_TOURSET_VERSION = "browser.onboarding.tourset-version";
 const PREF_SEEN_TOURSET_VERSION = "browser.onboarding.seen-tourset-version";
 
 function resetOnboardingDefaultState() {
   // All the prefs should be reset to what prefs should looks like in a new user profile
deleted file mode 100644
--- a/toolkit/mozapps/extensions/ChromeManifestParser.jsm
+++ /dev/null
@@ -1,151 +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";
-
-var EXPORTED_SYMBOLS = ["ChromeManifestParser"];
-
-ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-
-const MSG_JAR_FLUSH = "AddonJarFlush";
-
-
-/**
- * Sends local and remote notifications to flush a JAR file cache entry
- *
- * @param aJarFile
- *        The ZIP/XPI/JAR file as a nsIFile
- */
-function flushJarCache(aJarFile) {
-  Services.obs.notifyObservers(aJarFile, "flush-cache-entry");
-  Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
-    .broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
-}
-
-
-/**
- * Parses chrome manifest files.
- */
-var ChromeManifestParser = {
-
-  /**
-   * Reads and parses a chrome manifest file located at a specified URI, and all
-   * secondary manifests it references.
-   *
-   * @param  aURI
-   *         A nsIURI pointing to a chrome manifest.
-   *         Typically a file: or jar: URI.
-   * @return Array of objects describing each manifest instruction, in the form:
-   *         { type: instruction-type, baseURI: string-uri, args: [arguments] }
-   **/
-  parseSync(aURI) {
-    function parseLine(aLine) {
-      let line = aLine.trim();
-      if (line.length == 0 || line.charAt(0) == "#")
-        return;
-      let tokens = line.split(/\s+/);
-      let type = tokens.shift();
-      if (type == "manifest") {
-        let uri = NetUtil.newURI(tokens.shift(), null, aURI);
-        data = data.concat(this.parseSync(uri));
-      } else {
-        data.push({type, baseURI, args: tokens});
-      }
-    }
-
-    let contents = "";
-    try {
-      if (aURI.scheme == "jar")
-        contents = this._readFromJar(aURI);
-      else
-        contents = this._readFromFile(aURI);
-    } catch (e) {
-      // Silently fail.
-    }
-
-    if (!contents)
-      return [];
-
-    let baseURI = NetUtil.newURI(".", null, aURI).spec;
-
-    let data = [];
-    let lines = contents.split("\n");
-    lines.forEach(parseLine.bind(this));
-    return data;
-  },
-
-  _readFromJar(aURI) {
-    let data = "";
-    let entries = [];
-    let readers = [];
-
-    try {
-      // Deconstrict URI, which can be nested jar: URIs.
-      let uri = aURI.clone();
-      while (uri instanceof Ci.nsIJARURI) {
-        entries.push(uri.JAREntry);
-        uri = uri.JARFile;
-      }
-
-      // Open the base jar.
-      let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
-                   createInstance(Ci.nsIZipReader);
-      reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
-      readers.push(reader);
-
-      // Open the nested jars.
-      for (let i = entries.length - 1; i > 0; i--) {
-        let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
-                          createInstance(Ci.nsIZipReader);
-        innerReader.openInner(reader, entries[i]);
-        readers.push(innerReader);
-        reader = innerReader;
-      }
-
-      // First entry is the actual file we want to read.
-      let zis = reader.getInputStream(entries[0]);
-      data = NetUtil.readInputStreamToString(zis, zis.available());
-    } finally {
-      // Close readers in reverse order.
-      for (let i = readers.length - 1; i >= 0; i--) {
-        readers[i].close();
-        flushJarCache(readers[i].file);
-      }
-    }
-
-    return data;
-  },
-
-  _readFromFile(aURI) {
-    let file = aURI.QueryInterface(Ci.nsIFileURL).file;
-    if (!file.exists() || !file.isFile())
-      return "";
-
-    let data = "";
-    let fis = Cc["@mozilla.org/network/file-input-stream;1"].
-              createInstance(Ci.nsIFileInputStream);
-    try {
-      fis.init(file, -1, -1, false);
-      data = NetUtil.readInputStreamToString(fis, fis.available());
-    } finally {
-      fis.close();
-    }
-    return data;
-  },
-
-  /**
-  * Detects if there were any instructions of a specified type in a given
-  * chrome manifest.
-  *
-  * @param  aManifest
-  *         Manifest data, as returned by ChromeManifestParser.parseSync().
-  * @param  aType
-  *         Instruction type to filter by.
-  * @return True if any matching instructions were found in the manifest.
-  */
-  hasType(aManifest, aType) {
-    return aManifest.some(entry => entry.type == aType);
-  }
-};
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -11,17 +11,16 @@ var EXPORTED_SYMBOLS = ["XPIProvider", "
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonRepository: "resource://gre/modules/addons/AddonRepository.jsm",
   AddonSettings: "resource://gre/modules/addons/AddonSettings.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
-  ChromeManifestParser: "resource://gre/modules/ChromeManifestParser.jsm",
   Extension: "resource://gre/modules/Extension.jsm",
   Langpack: "resource://gre/modules/Extension.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   ZipUtils: "resource://gre/modules/ZipUtils.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   PermissionsUtils: "resource://gre/modules/PermissionsUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
@@ -40,18 +39,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   loadManifestFromFile: "resource://gre/modules/addons/XPIInstall.jsm",
   verifyBundleSignedState: "resource://gre/modules/addons/XPIInstall.jsm",
 });
 
 const {nsIBlocklistService} = Ci;
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   Blocklist: ["@mozilla.org/extensions/blocklist;1", "nsIBlocklistService"],
-  ChromeRegistry: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
-  ResProtocolHandler: ["@mozilla.org/network/protocol;1?name=resource", "nsIResProtocolHandler"],
   AddonPolicyService: ["@mozilla.org/addons/policy-service;1", "nsIAddonPolicyService"],
   aomStartup: ["@mozilla.org/addons/addon-manager-startup;1", "amIAddonManagerStartup"],
 });
 
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
   return new TextDecoder();
 });
 
@@ -1904,76 +1901,16 @@ var XPIProvider = {
         c.cancel();
       } catch (e) {
         logger.warn("Cancel failed", e);
       }
     }
   },
 
   /**
-   * Resolve a URI back to physical file.
-   *
-   * Of course, this works only for URIs pointing to local resources.
-   *
-   * @param  aURI
-   *         URI to resolve
-   * @return
-   *         resolved nsIFileURL
-   */
-  _resolveURIToFile(aURI) {
-    switch (aURI.scheme) {
-      case "jar":
-      case "file":
-        if (aURI instanceof Ci.nsIJARURI) {
-          return this._resolveURIToFile(aURI.JARFile);
-        }
-        return aURI;
-
-      case "chrome":
-        aURI = ChromeRegistry.convertChromeURL(aURI);
-        return this._resolveURIToFile(aURI);
-
-      case "resource":
-        aURI = Services.io.newURI(ResProtocolHandler.resolveURI(aURI));
-        return this._resolveURIToFile(aURI);
-
-      case "view-source":
-        aURI = Services.io.newURI(aURI.pathQueryRef);
-        return this._resolveURIToFile(aURI);
-
-      case "about":
-        if (aURI.spec == "about:blank") {
-          // Do not attempt to map about:blank
-          return null;
-        }
-
-        let chan;
-        try {
-          chan = NetUtil.newChannel({
-            uri: aURI,
-            loadUsingSystemPrincipal: true
-          });
-        } catch (ex) {
-          return null;
-        }
-        // Avoid looping
-        if (chan.URI.equals(aURI)) {
-          return null;
-        }
-        // We want to clone the channel URI to avoid accidentially keeping
-        // unnecessary references to the channel or implementation details
-        // around.
-        return this._resolveURIToFile(chan.URI.clone());
-
-      default:
-        return null;
-    }
-  },
-
-  /**
    * Starts the XPI provider initializes the install locations and prefs.
    *
    * @param  aAppChanged
    *         A tri-state value. Undefined means the current profile was created
    *         for this session, true means the profile already existed but was
    *         last used with an application with a different version number,
    *         false means that the profile was last used by this version of the
    *         application.
@@ -4375,23 +4312,16 @@ var XPIProvider = {
       if (aMethod == "startup" && aReason != BOOTSTRAP_REASONS.APP_STARTUP) {
         for (let addon of this.getDependentAddons(aAddon))
           this.updateAddonDisabledState(addon);
       }
 
       if (CHROME_TYPES.has(aAddon.type) && aMethod == "shutdown" && aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
         logger.debug("Removing manifest for " + aFile.path);
         Components.manager.removeBootstrappedManifestLocation(aFile);
-
-        let manifest = getURIForResourceInFile(aFile, "chrome.manifest");
-        for (let line of ChromeManifestParser.parseSync(manifest)) {
-          if (line.type == "resource") {
-            ResProtocolHandler.setSubstitution(line.args[0], null);
-          }
-        }
       }
       this.setTelemetry(aAddon.id, aMethod + "_MS", new Date() - timeStart);
     }
   },
 
   /**
    * Updates the disabled state for an add-on. Its appDisabled property will be
    * calculated and if the add-on is changed the database will be saved and
--- a/toolkit/mozapps/extensions/moz.build
+++ b/toolkit/mozapps/extensions/moz.build
@@ -31,17 +31,16 @@ EXTRA_COMPONENTS += [
 ]
 
 EXTRA_PP_COMPONENTS += [
     'extensions.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'AddonManager.jsm',
-    'ChromeManifestParser.jsm',
     'LightweightThemeManager.jsm',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 EXPORTS.mozilla += [
     'AddonContentPolicy.h',
     'AddonManagerStartup.h',
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-resource test-addon-1 .
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/addons/test_chromemanifest_6/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<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>addon6@tests.mozilla.org</em:id>
-    <em:version>1.0</em:version>
-
-    <!-- Front End MetaData -->
-    <em:name>Test 6</em:name>
-    <em:description>Test Description</em:description>
-    <em:bootstrap>true</em:bootstrap>
-
-    <em:targetApplication>
-      <Description>
-        <em:id>xpcshell@tests.mozilla.org</em:id>
-        <em:minVersion>1</em:minVersion>
-        <em:maxVersion>2</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-  </Description>
-</RDF>
index 956812aaa7f947f34c6e35076c200cb61f13690a..e52172baf2e300ed03e5f585cc3eec9975cfcc03
GIT binary patch
literal 1318
zc$^FHW@Zs#W?<l8;43TgnRitGLoOo&!y+aI1|9|mhNS%blH!u0!~(sn;<<r)^KKgm
z-1{oR?pw5A!r3V04DRv_v&C-97BM@|Q{ATGuh8!~chUQMlM^lN9i|m1b-sVU@3Qy@
zRoMfVN>0>CNY*g6pNl^HqJgu*<;Jz$4|HpmX4C}n#vC|dQ+wrBn#r5@7lV4HbS<)Z
z@sgv=eNEjFpI=_J7o0Bj&UiS#^OH{Ojg)o4{Hbf+yiol5U~)~{1!v#ouYI!2l9zRC
zf8!izckau&vk@!Ox+f)`SfRXAYQqcJXVv)|SH!DsTU~lNLP$~Ii`itJVqWRri~q{J
zyx5-5S1A06`RV<tS&Y*T{dyH_B)DF@{aZTo0=6fU%eH^inmRjnulAdroBR)dbNQP#
ziF-!+{aW8?wo6l&os%@%%VOAiiR<4#YvpN5amq(ZJJ)vYnExlu>-w?7OAGsgZXfmu
z_P%lZz4_{`2Ip;kf_t<k6e_8_-?aIr{~c3>=arq^3+|;${@WyzcxjQu8($5LRsIvt
zYggw#3vsSk8F6{G>tFT&c(f=q=lXD^9@ba|jE?I-%n!tAMTxnoy2Z&wnFS@ln6W*x
zJB!(or}bgoe5ahJ5;k48ml%0UbF|nTNO60kwZ^S^iRqg8_og3u+W7v@j`rZ&3o`Ea
zEVpcs)e6<KT;Zf~#OK4_f4VUbf>n#XncpP#y~yu4s=+>g<%$k7|19Be?Cbcu#f^?8
zR~^00+5RxuO!OCToXy6Ptv7nz)~y!Z^;At+WbJFGKKqn(y(cg4r`A-;&)Z!x@yV4}
zC%1>~J7Md;MLN~`Nz_)^zVcdDgtzjx>w9nXlDrxVOl@&M5pJNjGV_W{5_59&ic-?{
z24D0$WFS&|Km5wiw4f+6r5s6S;U{Os7w?N-kns4L-rJ)KkJWFNp4>F+M?%v5s&|&(
z=S{wsHaRwOLVDtwD_Jt%s%_>xah!1X+_NL0Q)JuwJ3>!I#9rTP^)7RB;$Ia{iT((Y
zz|B#?zZVIVh1~pXpz!WZ>q-s(ElcaBx-#ZAcYJ0PPF-xr|M|emO*1!mD{GgSap-&z
zJO1$6iY>uQgCbiNEl}0iDJC{|)yyfKMpIYUM6YFVt?ycw)UTepMzx}7x=FcX?AwCx
zEv?rwE;>wIe6wrX<Ao0#oDHNK!$U()s6JTO%>QVk^8EZH%SVRs{4b3jIj~P}HuV*Y
zxlqSrsG=l!Z1KVB^j{&dUSBU%KHa-xX~hd}(X#nJT%Er(l%Mp=dAD$W(qBuXtXLb*
zIdZnA*8luxdslL4O5PTyqP1a9+!W3*GzPr-?&PTX>37-r6S14q);*Rh_1)KdX2x%a
z$`6bI-i%E4%(!yB3IhZvG%RUkKq~{d7!<&Aj0_SC4D;M>GVQ<2h)yG$j+uCoO`pz)
zk(7}wXb6ARptKuku)F8`U3)Jx!uZG*U?wAE3-Xvy5*3Q!p2lA`=phVeSQ0t=l@rKt
SRyL3tW+0RS8v2D9!~+1(XeMC*
--- a/toolkit/mozapps/extensions/test/browser/addons/browser_update1_1/bootstrap.js
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_update1_1/bootstrap.js
@@ -1,13 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 /* exported startup, shutdown, install, uninstall */
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"]
+  .getService(Ci.nsISubstitutingProtocolHandler);
+
 function install(data, reason) {}
 function startup(data, reason) {
-  ChromeUtils.import("resource://gre/modules/Services.jsm");
+  resProto.setSubstitution("my-addon", data.resourceURI);
   Services.ppmm.loadProcessScript(
     "resource://my-addon/frame-script.js", false);
 }
-function shutdown(data, reason) {}
+function shutdown(data, reason) {
+  resProto.setSubstitution("my-addon", null);
+}
 function uninstall(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/browser/addons/browser_update1_1/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-resource my-addon .
index 7ef3db940c5cd0bb3bade8c70ae4bd25e5d1c2a2..7e48119c8ef2d658ce8482bb1e95a9f2d11487bb
GIT binary patch
literal 1318
zc$^FHW@Zs#W?<l8;4LfinRitGLoOo&!y+aI1|9|mhNS%blH!u0!~(sn;<<r)^KKgm
z-1{oR?pw5A!r3V04DRv_v&C-97BM@|Q{ATGuh8!~chUQMlM^lN9i|m1b-sVU@3Qy@
zRoMfVN>0>CNY*g6pNl^HqJgu*<;Jz$4|HpmX4C}n#vC|dQ+wrBn#r5@7lV4HbS<)Z
z@sgv=eNEjFpI=_J7o0Bj&UiS#^OH{Ojg)o4{Hbf+yiol5U~)~{1!v#ouYI!2l9zRC
zf8!izckau&vk@!Ox+f)`SfRXAYQqcJXVv)|SH!DsTU~lNLP$~Ii`itJVqWRri~q{J
zyx5-5S1A06`RV<tS&Y*T{dyH_B)DF@{aZTo0=6fU%eH^inmRjnulAdroBR)dbNQP#
ziF-!+{aW8?wo6l&os%@%%VOAiiR<4#YvpN5amq(ZJJ)vYnExlu>-w?7OAGsgZXfmu
z_P%lZz4_{`2Ip;kf_t<k6e_8_-?aIr{~c3>=arq^3+|;${@WyzcxjQu8($5LRsIvt
zYggw#3vsSk8F6{G>tFT&c(f=q=la}|PtIQjjE?I-%n!tAMTxnoy2Z&wnFS@ln6W*x
zJB!(or}bgoe5ahJ5;k5zYEwOLIVe0~R5MNrPc)TMS)2Ix%RUi3{+j2<ZDnq=x&BVG
zIm`GaU}e!sM^Pumw5Io8^B*$J*P0nEmB3%@QtzzeX8FU#)pbi{(CiKO3-?CbOxE1D
zBz@1k0tvgc;Dz@e92NEs@4lPyWLMO}>^vi<i@Wrm{BSON<TUSJ?6KwFA8zy(_w-vU
zSDXE``_5;V-GSSj*M;7G_;r~+!dpe#^}W|G3$cp@rnWes2shAMnR&$}i8(oXMJZ`}
zgD?6WG7zb~AAaR$+NG^IE;o$W)K1LScdOm+aPZjL*jwETj@56Mp4>F+M?%v5s&|&(
z=S{x%>~U=9r1Zo!SF%LDDarM9E1bw*Zv05=RNo=@BUZ^dyYIT)R=JdRwkmU$k=$mB
zWobJXeREZOvtrZpM29;!xs3zoW@!J_IxTdI&G31x+A6nNIkCpzr@@KhZr5Jq2+VlR
z+jpopFmm~+C7U=>J+%XhRCTOEjWxwX)uta^7RS_ek0th~od2mv?+;J(G6mi5T;(hG
zpOM`w#(RZzR&R!-92cJn%Vx2P(CbTM804&eD9BlV`l%-7JnzHR2E8P{E%i6!FP5BR
zeXBabLF<yE(VP9{%lxlS4dI^~E~lz{)H!~|hTli@r53U8@h(d$=Km}5`uxPe*PlEe
zJ$VsWclP`Hm*+H8jTehf%HFDC&XmZ-(Gs*@oJ%Y4&h~1zce6~3Wxub;{*;???hd<F
z6Muj=Ba=Ndu3WFe009aOOBxx_$^b3~1+W|=g9Jmvt(xmh`!6%1)5xY{CSGLIr!!(C
zWfTj-Uo|N0zRbwr?)iS#UZ4d~KC%Uv$q3nkJSLPxg<|;M?>{!^Aq;0&5;^;o6UcB@
RHjo--Ad~?b`h^+90{~3yANv3R
--- a/toolkit/mozapps/extensions/test/browser/addons/browser_update1_2/bootstrap.js
+++ b/toolkit/mozapps/extensions/test/browser/addons/browser_update1_2/bootstrap.js
@@ -1,13 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 /* exported startup, shutdown, install, uninstall */
 
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"]
+  .getService(Ci.nsISubstitutingProtocolHandler);
+
 function install(data, reason) {}
 function startup(data, reason) {
-  ChromeUtils.import("resource://gre/modules/Services.jsm");
+  resProto.setSubstitution("my-addon", data.resourceURI);
   Services.ppmm.loadProcessScript(
     "resource://my-addon/frame-script.js", false);
 }
-function shutdown(data, reason) {}
+function shutdown(data, reason) {
+  resProto.setSubstitution("my-addon", null);
+}
 function uninstall(data, reason) {}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/browser/addons/browser_update1_2/chrome.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-resource my-addon .
--- a/toolkit/mozapps/extensions/test/browser/browser_update.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_update.js
@@ -5,16 +5,17 @@
 // Tests that updates correctly flush caches and that new files gets updated.
 
 function test() {
   requestLongerTimeout(2);
   waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["extensions.checkUpdateSecurity", false],
+    ["xpinstall.signatures.required", false]
   ]});
 
   run_next_test();
 }
 
 // Install a first version
 add_test(function() {
   AddonManager.getInstallForURL(TESTROOT + "addons/browser_update1_1.xpi",
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
+++ /dev/null
@@ -1,108 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// Tests ChromeManifestParser.js
-
-ChromeUtils.import("resource://gre/modules/ChromeManifestParser.jsm");
-
-
-function run_test() {
-  do_test_pending();
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
-
-  startupManager();
-
-  installAllFiles([do_get_addon("test_chromemanifest_1"),
-                   do_get_addon("test_chromemanifest_2"),
-                   do_get_addon("test_chromemanifest_3"),
-                   do_get_addon("test_chromemanifest_4")],
-                  function() {
-
-    restartManager();
-    run_test_1();
-  });
-}
-
-function run_test_1() {
-  AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
-                               "addon2@tests.mozilla.org",
-                               "addon3@tests.mozilla.org",
-                               "addon4@tests.mozilla.org"],
-                              function([a1, a2, a3, a4]) {
-    // addon1
-    let a1Uri = a1.getResourceURI("/").spec;
-    let expected = [
-      {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]},
-      {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
-      {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
-      {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}
-    ];
-    let manifestURI = a1.getResourceURI("chrome.manifest");
-    let manifest = ChromeManifestParser.parseSync(manifestURI);
-
-    Assert.ok(Array.isArray(manifest));
-    Assert.equal(manifest.length, expected.length);
-    for (let i = 0; i < manifest.length; i++) {
-      Assert.equal(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
-    }
-
-    // addon2
-    let a2Uri = a2.getResourceURI("/").spec;
-    expected = [
-      {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]},
-      {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
-      {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
-      {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
-      {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]}
-    ];
-    manifestURI = a2.getResourceURI("chrome.manifest");
-    manifest = ChromeManifestParser.parseSync(manifestURI);
-
-    Assert.ok(Array.isArray(manifest));
-    Assert.equal(manifest.length, expected.length);
-    for (let i = 0; i < manifest.length; i++) {
-      Assert.equal(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
-    }
-
-    // addon3
-    let a3Uri = a3.getResourceURI("/").spec;
-    expected = [
-      {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]},
-      {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
-      {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
-      {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
-      {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]},
-      {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]},
-    ];
-    manifestURI = a3.getResourceURI("chrome.manifest");
-    manifest = ChromeManifestParser.parseSync(manifestURI);
-
-    Assert.ok(Array.isArray(manifest));
-    Assert.equal(manifest.length, expected.length);
-    for (let i = 0; i < manifest.length; i++) {
-      Assert.equal(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
-    }
-
-    // addon4
-    let a4Uri = a4.getResourceURI("/").spec;
-    expected = [
-      {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]},
-      {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
-      {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
-      {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
-      {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]},
-      {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]}
-    ];
-    manifestURI = a4.getResourceURI("chrome.manifest");
-    manifest = ChromeManifestParser.parseSync(manifestURI);
-
-    Assert.ok(Array.isArray(manifest));
-    Assert.equal(manifest.length, expected.length);
-    for (let i = 0; i < manifest.length; i++) {
-      Assert.equal(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
-    }
-
-    executeSoon(do_test_finished);
-  });
-}
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bootstrap_resource.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// Tests that resource protocol substitutions are set and unset for bootstrapped add-ons.
-
-const profileDir = gProfD.clone();
-profileDir.append("extensions");
-
-
-function run_test() {
-  do_test_pending();
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
-
-  let resourceProtocol = Services.io.getProtocolHandler("resource")
-                                 .QueryInterface(Ci.nsIResProtocolHandler);
-  startupManager();
-
-  installAllFiles([do_get_addon("test_chromemanifest_6")],
-                  function() {
-
-    AddonManager.getAddonByID("addon6@tests.mozilla.org", function(addon) {
-      Assert.notEqual(addon, null);
-      Assert.ok(addon.isActive);
-      Assert.ok(resourceProtocol.hasSubstitution("test-addon-1"));
-
-      prepare_test({
-        "addon6@tests.mozilla.org": [
-          ["onDisabling", false],
-          "onDisabled"
-        ]
-      });
-
-      Assert.equal(addon.operationsRequiringRestart &
-                   AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
-      addon.userDisabled = true;
-      ensure_test_completed();
-      Assert.ok(!resourceProtocol.hasSubstitution("test-addon-1"));
-
-      prepare_test({
-        "addon6@tests.mozilla.org": [
-          ["onEnabling", false],
-          "onEnabled"
-        ]
-      });
-
-      Assert.equal(addon.operationsRequiringRestart &
-                   AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
-      addon.userDisabled = false;
-      ensure_test_completed();
-      Assert.ok(resourceProtocol.hasSubstitution("test-addon-1"));
-
-      executeSoon(do_test_finished);
-    });
-  });
-}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_cacheflush.js
@@ -113,15 +113,15 @@ function run_test_3() {
 function run_test_4() {
   AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
     // We should flush the installed XPI when uninstalling
     gExpectedFile = gProfD.clone();
     gExpectedFile.append("extensions");
     gExpectedFile.append("addon2@tests.mozilla.org.xpi");
 
     a2.uninstall();
-    Assert.equal(gCacheFlushCount, 2);
+    Assert.equal(gCacheFlushCount, 1);
     gExpectedFile = null;
     gCacheFlushCount = 0;
 
     executeSoon(do_test_finished);
   });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_invalid_install_rdf.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_invalid_install_rdf.js
@@ -15,66 +15,76 @@ const userAppDir = AddonTestUtils.profil
 userAppDir.append("app-extensions");
 userAppDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 AddonTestUtils.registerDirectory("XREUSysExt", userAppDir);
 
 const userExtensions = userAppDir.clone();
 userExtensions.append(APP_ID);
 userExtensions.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
+XPCOMUtils.defineLazyServiceGetters(this, {
+  ChromeRegistry: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
+});
+
+function hasChromeEntry(package) {
+  try {
+    void ChromeRegistry.convertChromeURL(Services.io.newURI(`chrome://${package}/content/`));
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
 add_task(async function() {
   await promiseWriteInstallRDFToXPI({
     id: "langpack-foo@addons.mozilla.org",
     version: "1.0",
     type: 8,
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Invalid install.rdf extension",
   }, userExtensions, undefined, {
     "chrome.manifest": `
-      resource foo-langpack ./
+      content foo-langpack ./
     `,
   });
 
   await promiseWriteInstallRDFToXPI({
     id: "foo@addons.mozilla.org",
     version: "1.0",
     targetApplications: [{
       id: "xpcshell@tests.mozilla.org",
       minVersion: "1",
       maxVersion: "1"
     }],
     name: "Invalid install.rdf extension",
   }, userExtensions, undefined, {
     "chrome.manifest": `
-      resource foo ./
+      content foo ./
     `,
   });
 
-  const resProto = Services.io.getProtocolHandler("resource");
-  resProto.QueryInterface(Ci.nsIResProtocolHandler);
-
-  equal(resProto.hasSubstitution("foo-langpack"), false,
+  equal(hasChromeEntry("foo-langpack"), false,
         "Should not foo-langpack resource before AOM startup");
-  equal(resProto.hasSubstitution("foo"), false,
+  equal(hasChromeEntry("foo"), false,
         "Should not foo resource before AOM startup");
 
   await promiseStartupManager();
 
-  equal(resProto.hasSubstitution("foo-langpack"), false,
+  equal(hasChromeEntry("foo-langpack"), false,
         "Should not have registered chrome manifest for invalid extension");
-  equal(resProto.hasSubstitution("foo"), true,
+  equal(hasChromeEntry("foo"), true,
         "Should have registered chrome manifest for valid extension");
 
   await promiseRestartManager();
 
-  equal(resProto.hasSubstitution("foo-langpack"), false,
+  equal(hasChromeEntry("foo-langpack"), false,
         "Should still not have registered chrome manifest for invalid extension after restart");
-  equal(resProto.hasSubstitution("foo"), true,
+  equal(hasChromeEntry("foo"), true,
         "Should still have registered chrome manifest for valid extension after restart");
 
   await promiseShutdownManager();
 
   userAppDir.remove(true);
 });
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -88,17 +88,16 @@ tags = blocklist
 skip-if = os == "android"
 tags = blocklist
 [test_blocklist_regexp.js]
 skip-if = os == "android"
 tags = blocklist
 [test_bootstrap.js]
 skip-if = true # Bug 1358846 Bug 1365021 Bug 676992
 [test_bootstrap_const.js]
-[test_bootstrap_resource.js]
 [test_bug299716.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 run-sequentially = Uses hardcoded ports in xpi files.
 [test_bug299716_2.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 run-sequentially = Hardcoded port in install.rdf.
@@ -210,17 +209,16 @@ tags = blocklist
 [test_bug659772.js]
 [test_bug675371.js]
 [test_bug740612.js]
 [test_bug753900.js]
 [test_bug757663.js]
 [test_bug953156.js]
 [test_checkcompatibility.js]
 [test_childprocess.js]
-[test_ChromeManifestParser.js]
 [test_compatoverrides.js]
 [test_corrupt.js]
 [test_corrupt_strictcompat.js]
 [test_corruptfile.js]
 [test_dataDirectory.js]
 [test_db_path.js]
 head =
 [test_default_providers_pref.js]
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -117,17 +117,17 @@ static const ManifestDirective kParsingT
   },
   {
     // NB: note that while skin manifests can use this, they are only allowed
     // to use it for chrome://../skin/ URLs
     "override",         2, false, false, true, true, false,
     nullptr, &nsChromeRegistry::ManifestOverride,
   },
   {
-    "resource",         2, false, true, true, true, true,
+    "resource",         2, false, true, true, false, true,
     nullptr, &nsChromeRegistry::ManifestResource,
   }
 };
 
 static const char kWhitespace[] = "\t ";
 
 static bool
 IsNewline(char aChar)