Bug 1169459 - remove the loadFromJars/jarURIs prefs, r=Mossop.
authorFlorian Quèze <florian@queze.net>
Fri, 04 Sep 2015 11:50:49 +0200
changeset 260858 925475a10f088b50543a2a169bea2ca4d0a95257
parent 260857 6bde454912d5ed04e9d52b6209d3e91959f5a770
child 260859 dd89b3556b3d06ac6066c11a4cea29eecce2b588
push id29328
push userkwierso@gmail.com
push dateFri, 04 Sep 2015 22:13:38 +0000
treeherdermozilla-central@d41fa6f2f1da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
bugs1169459
milestone43.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 1169459 - remove the loadFromJars/jarURIs prefs, r=Mossop.
browser/app/profile/firefox.js
browser/components/dirprovider/DirectoryProvider.cpp
browser/locales/jar.mn
mobile/android/app/mobile.js
mobile/android/locales/jar.mn
toolkit/components/search/nsSearchService.js
toolkit/components/search/tests/xpcshell/head_search.js
toolkit/components/search/tests/xpcshell/test_async_app.js
toolkit/components/search/tests/xpcshell/test_json_cache.js
toolkit/components/search/tests/xpcshell/test_sync_app.js
toolkit/components/search/tests/xpcshell/xpcshell.ini
toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
xpcom/io/nsAppFileLocationProvider.cpp
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -351,20 +351,16 @@ pref("browser.download.panel.shown", fal
 
 #ifndef XP_MACOSX
 pref("browser.helperApps.deleteTempFileOnExit", true);
 #endif
 
 // search engines URL
 pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
 
-// Tell the search service to load search plugins from the locale JAR
-pref("browser.search.loadFromJars", true);
-pref("browser.search.jarURIs", "chrome://browser/locale/searchplugins/");
-
 // pointer to the default engine name
 pref("browser.search.defaultenginename",      "chrome://browser-region/locale/region.properties");
 
 // Ordering of Search Engines in the Engine list.
 pref("browser.search.order.1",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.2",                "chrome://browser-region/locale/region.properties");
 pref("browser.search.order.3",                "chrome://browser-region/locale/region.properties");
 
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -214,17 +214,16 @@ DirectoryProvider::GetFiles(const char *
    *
    *   - engines shipped in chrome (Loaded from jar files by the search
    *     service)
    *
    *   Then other locations, from NS_APP_SEARCH_DIR_LIST:
    *   - extension search plugin locations (prepended below using
    *     NS_NewUnionEnumerator)
    *   - user search plugin locations (profile)
-   *   - app search plugin location (shipped engines)
    */
 
   nsresult rv;
 
   if (!strcmp(aKey, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
     nsCOMPtr<nsIProperties> dirSvc
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (!dirSvc)
@@ -240,17 +239,16 @@ DirectoryProvider::GetFiles(const char *
     nsCOMPtr<nsIProperties> dirSvc
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (!dirSvc)
       return NS_ERROR_FAILURE;
 
     nsCOMArray<nsIFile> baseFiles;
 
     AppendFileKey(NS_APP_USER_SEARCH_DIR, dirSvc, baseFiles);
-    AppendFileKey(NS_APP_SEARCH_DIR, dirSvc, baseFiles);
 
     nsCOMPtr<nsISimpleEnumerator> baseEnum;
     rv = NS_NewArrayEnumerator(getter_AddRefs(baseEnum), baseFiles);
     if (NS_FAILED(rv))
       return rv;
 
     nsCOMPtr<nsISimpleEnumerator> list;
     rv = dirSvc->Get(XRE_EXTENSIONS_DIR_LIST,
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -146,16 +146,17 @@
     locale/browser/syncBrand.dtd                (%chrome/browser/syncBrand.dtd)
     locale/browser/syncSetup.dtd                (%chrome/browser/syncSetup.dtd)
     locale/browser/syncSetup.properties         (%chrome/browser/syncSetup.properties)
     locale/browser/syncGenericChange.properties         (%chrome/browser/syncGenericChange.properties)
     locale/browser/syncKey.dtd                  (%chrome/browser/syncKey.dtd)
     locale/browser/syncQuota.dtd                (%chrome/browser/syncQuota.dtd)
     locale/browser/syncQuota.properties         (%chrome/browser/syncQuota.properties)
 #endif
+% resource search-plugins chrome://browser/locale/searchplugins/
     locale/browser/searchplugins/list.txt       (.deps/generated_@AB_CD@/list.txt)
     locale/browser/searchplugins/               (.deps/generated_@AB_CD@/*.xml)
 % locale browser-region @AB_CD@ %locale/browser-region/
     locale/browser-region/region.properties        (%chrome/browser-region/region.properties)
 # the following files are browser-specific overrides
     locale/browser/netError.dtd                (%chrome/overrides/netError.dtd)
     locale/browser/appstrings.properties       (%chrome/overrides/appstrings.properties)
     locale/browser/downloads/settingsChange.dtd  (%chrome/overrides/settingsChange.dtd)
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -280,20 +280,16 @@ pref("browser.search.update", false);
 
 // enable tracking protection for private browsing
 pref("privacy.trackingprotection.pbmode.enabled", true);
 
 // disable search suggestions by default
 pref("browser.search.suggest.enabled", false);
 pref("browser.search.suggest.prompted", false);
 
-// Tell the search service to load search plugins from the locale JAR
-pref("browser.search.loadFromJars", true);
-pref("browser.search.jarURIs", "chrome://browser/locale/searchplugins/");
-
 // tell the search service that we don't really expose the "current engine"
 pref("browser.search.noCurrentEngine", true);
 
 // Control media casting & mirroring features
 pref("browser.casting.enabled", true);
 #ifdef RELEASE_BUILD
 // Chromecast mirroring is broken (bug 1131084)
 pref("browser.mirroring.enabled", false);
--- a/mobile/android/locales/jar.mn
+++ b/mobile/android/locales/jar.mn
@@ -37,16 +37,17 @@
   locale/@AB_CD@/browser/payments.properties      (%chrome/payments.properties)
   locale/@AB_CD@/browser/handling.properties      (%chrome/handling.properties)
   locale/@AB_CD@/browser/webapp.properties        (%chrome/webapp.properties)
   locale/@AB_CD@/browser/aboutLogins.dtd          (%chrome/aboutLogins.dtd)
   locale/@AB_CD@/browser/aboutLogins.properties  (%chrome/aboutLogins.properties)
 #ifdef NIGHTLY_BUILD
   locale/@AB_CD@/browser/webcompatReporter.properties (%chrome/webcompatReporter.properties)
 #endif
+% resource search-plugins chrome://browser/locale/searchplugins/
 
 # overrides for toolkit l10n, also for en-US
 relativesrcdir toolkit/locales:
   locale/@AB_CD@/browser/overrides/about.dtd                       (%chrome/global/about.dtd)
   locale/@AB_CD@/browser/overrides/aboutAbout.dtd                  (%chrome/global/aboutAbout.dtd)
   locale/@AB_CD@/browser/overrides/aboutReader.properties          (%chrome/global/aboutReader.properties)
   locale/@AB_CD@/browser/overrides/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/browser/overrides/charsetMenu.properties          (%chrome/global/charsetMenu.properties)
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -51,16 +51,21 @@ const MODE_TRUNCATE = 0x20;
 
 // Directory service keys
 const NS_APP_SEARCH_DIR_LIST  = "SrchPluginsDL";
 const NS_APP_DISTRIBUTION_SEARCH_DIR_LIST = "SrchPluginsDistDL";
 const NS_APP_USER_SEARCH_DIR  = "UsrSrchPlugns";
 const NS_APP_SEARCH_DIR       = "SrchPlugns";
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 
+// Loading plugins from NS_APP_SEARCH_DIR is no longer supported.
+// Instead, we now load plugins from APP_SEARCH_PREFIX, where a
+// list.txt file needs to exist to list available engines.
+const APP_SEARCH_PREFIX = "resource://search-plugins/";
+
 // Search engine "locations". If this list is changed, be sure to update
 // the engine's _isDefault function accordingly.
 const SEARCH_APP_DIR = 1;
 const SEARCH_PROFILE_DIR = 2;
 const SEARCH_IN_EXTENSION = 3;
 const SEARCH_JAR = 4;
 
 // See documentation in nsIBrowserSearchService.idl.
@@ -916,16 +921,30 @@ function makeURI(aURLSpec, aCharset) {
   try {
     return NetUtil.newURI(aURLSpec, aCharset);
   } catch (ex) { }
 
   return null;
 }
 
 /**
+ * Wrapper function for nsIIOService::newChannel2.
+ * @param url
+ *        The URL string from which to create an nsIChannel.
+ * @returns an nsIChannel object, or null if the url is invalid.
+ */
+function makeChannel(url) {
+  try {
+    return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true});
+  } catch (ex) { }
+
+  return null;
+}
+
+/**
  * Gets a directory from the directory service.
  * @param aKey
  *        The directory service key indicating the directory to get.
  */
 function getDir(aKey, aIFace) {
   if (!aKey)
     FAIL("getDir requires a directory key!");
 
@@ -1727,17 +1746,17 @@ Engine.prototype = {
     return deferred.promise;
   },
 
   _initFromURISync: function SRCH_ENG_initFromURISync() {
     ENSURE_WARN(this._uri instanceof Ci.nsIURI,
                 "Must have URI when calling _initFromURISync!",
                 Cr.NS_ERROR_UNEXPECTED);
 
-    ENSURE_WARN(this._uri.schemeIs("chrome"), "_initFromURISync called for non-chrome URI",
+    ENSURE_WARN(this._uri.schemeIs("resource"), "_initFromURISync called for non-resource URI",
                 Cr.NS_ERROR_FAILURE);
 
     LOG("_initFromURISync: Loading engine from: \"" + this._uri.spec + "\".");
 
     var chan = NetUtil.ioService.newChannelFromURI2(this._uri,
                                                     null,      // aLoadingNode
                                                     Services.scriptSecurityManager.getSystemPrincipal(),
                                                     null,      // aTriggeringPrincipal
@@ -3023,16 +3042,21 @@ Engine.prototype = {
     let leafName = this._getLeafName();
     if (!leafName)
       return "null";
 
     let prefix = "", suffix = "";
     let file = this._file;
     if (!file) {
       let uri = this._uri;
+      if (uri.schemeIs("resource")) {
+        uri = makeURI(Services.io.getProtocolHandler("resource")
+                              .QueryInterface(Ci.nsISubstitutingProtocolHandler)
+                              .resolveURI(uri));
+      }
       if (uri.schemeIs("chrome")) {
         let packageName = uri.hostPort;
         uri = gChromeReg.convertChromeURL(uri);
         if (uri instanceof Ci.nsINestedURI) {
           prefix = "jar:";
           suffix = "!" + packageName + "/" + leafName;
           uri = uri.innermostURI;
         }
@@ -3607,40 +3631,34 @@ SearchService.prototype = {
     // Extension-shipped plugins are the only exception to this, but their
     // directories are blown away during updates, so we'll detect their changes.
     cache.buildID = buildID;
     cache.locale = locale;
 
     cache.directories = {};
     cache.visibleDefaultEngines = this._visibleDefaultEngines;
 
-    function getParent(engine) {
+    let getParent = engine => {
       if (engine._file)
         return engine._file.parent;
 
       let uri = engine._uri;
-      if (!uri.schemeIs("chrome")) {
-        LOG("getParent: engine URI must be a chrome URI if it has no file");
+      if (!uri.schemeIs("resource")) {
+        LOG("getParent: engine URI must be a resource URI if it has no file");
         return null;
       }
 
-      // use the underlying JAR file, for chrome URIs
-      try {
-        uri = gChromeReg.convertChromeURL(uri);
-        if (uri instanceof Ci.nsINestedURI)
-          uri = uri.innermostURI;
-        uri.QueryInterface(Ci.nsIFileURL)
-
-        return uri.file;
-      } catch (ex) {
-        LOG("getParent: couldn't map chrome:// URI to a file: " + ex)
-      }
-
+      // use the underlying JAR file, for resource URIs
+      let chan = makeChannel(uri.spec);
+      if (chan)
+        return this._convertChannelToFile(chan);
+
+      LOG("getParent: couldn't map resource:// URI to a file");
       return null;
-    }
+    };
 
     for each (let engine in this._engines) {
       let parent = getParent(engine);
       if (!parent) {
         LOG("Error: no parent for engine " + engine._location + ", failing to cache it");
 
         continue;
       }
@@ -3682,25 +3700,17 @@ SearchService.prototype = {
     let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true);
     if (cacheEnabled) {
       let cacheFile = getDir(NS_APP_USER_PROFILE_50_DIR);
       cacheFile.append("search.json");
       if (cacheFile.exists())
         cache = this._readCacheFile(cacheFile);
     }
 
-    let chromeURIs = [], chromeFiles = [];
-    let loadFromJARs = false;
-    try {
-      loadFromJARs = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
-                             .getBoolPref("loadFromJars");
-    } catch (ex) {}
-
-    if (loadFromJARs)
-      [chromeFiles, chromeURIs] = this._findJAREngines();
+    let [chromeFiles, chromeURIs] = this._findJAREngines();
 
     let distDirs = [];
     let locations;
     try {
       locations = getDir(NS_APP_DISTRIBUTION_SEARCH_DIR_LIST,
                          Ci.nsISimpleEnumerator);
     } catch (e) {
       // NS_APP_DISTRIBUTION_SEARCH_DIR_LIST is defined by each app
@@ -3712,18 +3722,16 @@ SearchService.prototype = {
       if (dir.directoryEntries.hasMoreElements())
         distDirs.push(dir);
     }
 
     let otherDirs = [];
     locations = getDir(NS_APP_SEARCH_DIR_LIST, Ci.nsISimpleEnumerator);
     while (locations.hasMoreElements()) {
       let dir = locations.getNext().QueryInterface(Ci.nsIFile);
-      if (loadFromJARs && dir.equals(getDir(NS_APP_SEARCH_DIR)))
-        continue;
       if (dir.directoryEntries.hasMoreElements())
         otherDirs.push(dir);
     }
 
     let toLoad = chromeFiles.concat(distDirs, otherDirs);
 
     function modifiedDir(aDir) {
       return (!cache.directories || !cache.directories[aDir.path] ||
@@ -3780,28 +3788,19 @@ SearchService.prototype = {
       // See if we have a cache file so we don't have to parse a bunch of XML.
       let cache = {};
       let cacheEnabled = getBoolPref(BROWSER_SEARCH_PREF + "cache.enabled", true);
       if (cacheEnabled) {
         let cacheFilePath = OS.Path.join(OS.Constants.Path.profileDir, "search.json");
         cache = yield checkForSyncCompletion(this._asyncReadCacheFile(cacheFilePath));
       }
 
-      let chromeURIs = [], chromeFiles = [];
-      let loadFromJARs = false;
-      try {
-        loadFromJARs = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
-                               .getBoolPref("loadFromJars");
-      } catch (ex) {}
-
-      if (loadFromJARs) {
-        Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "find-jar-engines");
-        [chromeFiles, chromeURIs] =
-          yield checkForSyncCompletion(this._asyncFindJAREngines());
-      }
+      Services.obs.notifyObservers(null, SEARCH_SERVICE_TOPIC, "find-jar-engines");
+      let [chromeFiles, chromeURIs] =
+        yield checkForSyncCompletion(this._asyncFindJAREngines());
 
       // Get the non-empty distribution directories into distDirs...
       let distDirs = [];
       let locations;
       try {
         locations = getDir(NS_APP_DISTRIBUTION_SEARCH_DIR_LIST,
                            Ci.nsISimpleEnumerator);
       } catch (e) {
@@ -3825,22 +3824,16 @@ SearchService.prototype = {
       }
 
       // Add the non-empty directories of NS_APP_SEARCH_DIR_LIST to
       // otherDirs...
       let otherDirs = [];
       locations = getDir(NS_APP_SEARCH_DIR_LIST, Ci.nsISimpleEnumerator);
       while (locations.hasMoreElements()) {
         let dir = locations.getNext().QueryInterface(Ci.nsIFile);
-        // ... but skip the application directory if we are loading from JAR.
-        // Applications shipping JAR engines don't ship plain text
-        // engine files anymore.
-        if (loadFromJARs && dir.equals(getDir(NS_APP_SEARCH_DIR)))
-          continue;
-
         let iterator = new OS.File.DirectoryIterator(dir.path,
                                                      { winPattern: "*.xml" });
         try {
           // Add dir to otherDirs if it contains any files.
           yield checkForSyncCompletion(iterator.next());
           otherDirs.push(dir);
         } catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
           // Catch for StopIteration exception.
@@ -4220,148 +4213,103 @@ SearchService.prototype = {
         } catch (ex if ex.result != Cr.NS_ERROR_ALREADY_INITIALIZED) {
           LOG("_asyncLoadFromChromeURLs: failed to load engine: " + ex);
         }
       }
       throw new Task.Result(engines);
     }.bind(this));
   },
 
+  _convertChannelToFile: function(chan) {
+    let fileURI = chan.URI;
+    while (fileURI instanceof Ci.nsIJARURI)
+      fileURI = fileURI.JARFile;
+    fileURI.QueryInterface(Ci.nsIFileURL);
+
+    return fileURI.file;
+  },
+
   _findJAREngines: function SRCH_SVC_findJAREngines() {
     LOG("_findJAREngines: looking for engines in JARs")
 
-    let rootURIPref = ""
-    try {
-      rootURIPref = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
-                            .getCharPref("jarURIs");
-    } catch (ex) {}
-
-    if (!rootURIPref) {
-      LOG("_findJAREngines: no JAR URIs were specified");
-
+    let chan = makeChannel(APP_SEARCH_PREFIX + "list.txt");
+    if (!chan) {
+      LOG("_findJAREngines: " + APP_SEARCH_PREFIX + " isn't registered");
       return [[], []];
     }
 
-    let rootURIs = rootURIPref.split(",");
     let uris = [];
     let chromeFiles = [];
 
-    rootURIs.forEach(root => {
-      // Find the underlying JAR file for this chrome package (_loadEngines uses
-      // it to determine whether it needs to invalidate the cache)
-      let jarPackaging = false;
-      try {
-        let chromeURI = gChromeReg.convertChromeURL(makeURI(root));
-        if (chromeURI instanceof Ci.nsIJARURI) {
-          let fileURI = chromeURI;
-          while (fileURI instanceof Ci.nsIJARURI)
-            fileURI = fileURI.JARFile;
-          fileURI.QueryInterface(Ci.nsIFileURL);
-          chromeFiles.push(fileURI.file);
-          jarPackaging = true;
-        }
-      } catch (ex) {
-        LOG("_findJAREngines: failed to get chromeFile for " + root + ": " + ex);
-        return;
-      }
-
-      // Read list.txt from the chrome package to find the engines we need to
-      // load
-      let listURL = root + "list.txt";
-      try {
-        let chan = NetUtil.ioService.newChannelFromURI2(makeURI(listURL),
-                                                        null,      // aLoadingNode
-                                                        Services.scriptSecurityManager.getSystemPrincipal(),
-                                                        null,      // aTriggeringPrincipal
-                                                        Ci.nsILoadInfo.SEC_NORMAL,
-                                                        Ci.nsIContentPolicy.TYPE_OTHER);
-        let sis = Cc["@mozilla.org/scriptableinputstream;1"].
-                  createInstance(Ci.nsIScriptableInputStream);
-        sis.init(chan.open());
-        this._parseListTxt(sis.read(sis.available()), root, jarPackaging,
-                           chromeFiles, uris);
-      } catch (ex) {
-        LOG("_findJAREngines: failed to retrieve list.txt from " + listURL + ": " + ex);
-
-        return;
-      }
-    });
-
+    // Find the underlying JAR file (_loadEngines uses it to determine
+    // whether it needs to invalidate the cache)
+    let jarPackaging = false;
+    if (chan.URI instanceof Ci.nsIJARURI) {
+      chromeFiles.push(this._convertChannelToFile(chan));
+      jarPackaging = true;
+    }
+
+    let sis = Cc["@mozilla.org/scriptableinputstream;1"].
+                createInstance(Ci.nsIScriptableInputStream);
+    sis.init(chan.open());
+    this._parseListTxt(sis.read(sis.available()), jarPackaging,
+                       chromeFiles, uris);
     return [chromeFiles, uris];
   },
 
   /**
    * Loads jar engines asynchronously.
    *
    * @returns {Promise} A promise, resolved successfully if finding jar engines
    * succeeds.
    */
   _asyncFindJAREngines: function SRCH_SVC__asyncFindJAREngines() {
     return Task.spawn(function() {
       LOG("_asyncFindJAREngines: looking for engines in JARs")
 
-      let rootURIPref = "";
-      try {
-        rootURIPref = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF)
-                              .getCharPref("jarURIs");
-      } catch (ex) {}
-
-      if (!rootURIPref) {
-        LOG("_asyncFindJAREngines: no JAR URIs were specified");
+      let listURL = APP_SEARCH_PREFIX + "list.txt";
+      let chan = makeChannel(listURL);
+      if (!chan) {
+        LOG("_asyncFindJAREngines: " + APP_SEARCH_PREFIX + " isn't registered");
         throw new Task.Result([[], []]);
       }
 
-      let rootURIs = rootURIPref.split(",");
       let uris = [];
       let chromeFiles = [];
 
-      for (let root of rootURIs) {
-        // Find the underlying JAR file for this chrome package (_loadEngines uses
-        // it to determine whether it needs to invalidate the cache)
-        let jarPackaging = false;
-        try {
-          let chromeURI = gChromeReg.convertChromeURL(makeURI(root));
-          if (chromeURI instanceof Ci.nsIJARURI) {
-            let fileURI = chromeURI;
-            while (fileURI instanceof Ci.nsIJARURI)
-              fileURI = fileURI.JARFile;
-            fileURI.QueryInterface(Ci.nsIFileURL);
-            chromeFiles.push(fileURI.file);
-            jarPackaging = true;
-          }
-        } catch (ex) {
-          LOG("_asyncFindJAREngines: failed to get chromeFile for " + root + ": " + ex);
-          return;
-        }
-
-        // Read list.txt from the chrome package to find the engines we need to
-        // load
-        let listURL = root + "list.txt";
-        let deferred = Promise.defer();
-        let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                        createInstance(Ci.nsIXMLHttpRequest);
-        request.overrideMimeType("text/plain");
-        request.onload = function(aEvent) {
-          deferred.resolve(aEvent.target.responseText);
-        };
-        request.onerror = function(aEvent) {
-          LOG("_asyncFindJAREngines: failed to retrieve list.txt from " + listURL);
-          deferred.resolve("");
-        };
-        request.open("GET", NetUtil.newURI(listURL).spec, true);
-        request.send();
-        let list = yield deferred.promise;
-
-        this._parseListTxt(list, root, jarPackaging, chromeFiles, uris);
+      // Find the underlying JAR file (_loadEngines uses it to determine
+      // whether it needs to invalidate the cache)
+      let jarPackaging = false;
+      if (chan.URI instanceof Ci.nsIJARURI) {
+        chromeFiles.push(this._convertChannelToFile(chan));
+        jarPackaging = true;
       }
+
+      // Read list.txt to find the engines we need to load.
+      let deferred = Promise.defer();
+      let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+                      createInstance(Ci.nsIXMLHttpRequest);
+      request.overrideMimeType("text/plain");
+      request.onload = function(aEvent) {
+        deferred.resolve(aEvent.target.responseText);
+      };
+      request.onerror = function(aEvent) {
+        LOG("_asyncFindJAREngines: failed to read " + listURL);
+        deferred.resolve("");
+      };
+      request.open("GET", NetUtil.newURI(listURL).spec, true);
+      request.send();
+      let list = yield deferred.promise;
+
+      this._parseListTxt(list, jarPackaging, chromeFiles, uris);
       throw new Task.Result([chromeFiles, uris]);
     }.bind(this));
   },
 
-  _parseListTxt: function SRCH_SVC_parseListTxt(list, root, jarPackaging,
+  _parseListTxt: function SRCH_SVC_parseListTxt(list, jarPackaging,
                                                 chromeFiles, uris) {
     let names = list.split("\n").filter(function (n) !!n);
     // This maps the names of our built-in engines to a boolean
     // indicating whether it should be hidden by default.
     let jarNames = new Map();
     for (let name of names) {
       if (name.endsWith(":hidden")) {
         name = name.split(":")[0];
@@ -4400,23 +4348,26 @@ SearchService.prototype = {
       engineNames = [];
       for (let [name, hidden] of jarNames) {
         if (!hidden)
           engineNames.push(name);
       }
     }
 
     for (let name of engineNames) {
-      let uri = root + name + ".xml";
+      let uri = APP_SEARCH_PREFIX + name + ".xml";
       uris.push(uri);
       if (!jarPackaging) {
         // Flat packaging requires that _loadEngines checks the modification
         // time of each engine file.
-        uri = gChromeReg.convertChromeURL(makeURI(uri));
-        chromeFiles.push(uri.QueryInterface(Ci.nsIFileURL).file);
+        let chan = makeChannel(uri);
+        if (chan)
+          chromeFiles.push(this._convertChannelToFile(chan));
+        else
+          LOG("_findJAREngines: couldn't resolve " + uri);
       }
     }
 
     // Store this so that it can be used while writing the cache file.
     this._visibleDefaultEngines = engineNames;
   },
 
 
--- a/toolkit/components/search/tests/xpcshell/head_search.js
+++ b/toolkit/components/search/tests/xpcshell/head_search.js
@@ -69,30 +69,26 @@ if (!isChild) {
 function dumpn(text)
 {
   dump("search test: " + text + "\n");
 }
 
 /**
  * Configure preferences to load engines from
  * chrome://testsearchplugin/locale/searchplugins/
- * unless the loadFromJars parameter is set to false.
  */
-function configureToLoadJarEngines(loadFromJars = true)
+function configureToLoadJarEngines()
 {
   let defaultBranch = Services.prefs.getDefaultBranch(null);
 
   let url = "chrome://testsearchplugin/locale/searchplugins/";
-  defaultBranch.setCharPref("browser.search.jarURIs", url);
-
-  defaultBranch.setBoolPref("browser.search.loadFromJars", loadFromJars);
-
-  // Give the pref a user set value that is the opposite of the default,
-  // to ensure user set values are ignored.
-  Services.prefs.setBoolPref("browser.search.loadFromJars", !loadFromJars)
+  let resProt = Services.io.getProtocolHandler("resource")
+                        .QueryInterface(Ci.nsIResProtocolHandler);
+  resProt.setSubstitution("search-plugins",
+                          Services.io.newURI(url, null, null));
 
   // Ensure a test engine exists in the app dir anyway.
   let dir = Services.dirsvc.get(NS_APP_SEARCH_DIR, Ci.nsIFile);
   if (!dir.exists())
     dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
   do_get_file("data/engine-app.xml").copyTo(dir, "app.xml");
 }
 
deleted file mode 100644
--- a/toolkit/components/search/tests/xpcshell/test_async_app.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function run_test() {
-  do_test_pending();
-
-  removeMetadata();
-  removeCacheFile();
-
-  do_load_manifest("data/chrome.manifest");
-
-  configureToLoadJarEngines(false);
-
-  do_check_false(Services.search.isInitialized);
-
-  Services.search.init(function search_initialized(aStatus) {
-    do_check_true(Components.isSuccessCode(aStatus));
-    do_check_true(Services.search.isInitialized);
-
-    // test engine from dir is loaded.
-    let engine = Services.search.getEngineByName("TestEngineApp");
-    do_check_neq(engine, null);
-
-    // test jar engine is not loaded.
-    engine = Services.search.getEngineByName("bug645970");
-    do_check_eq(engine, null);
-
-    do_test_finished();
-  });
-}
--- a/toolkit/components/search/tests/xpcshell/test_json_cache.js
+++ b/toolkit/components/search/tests/xpcshell/test_json_cache.js
@@ -52,68 +52,56 @@ function run_test() {
   engineFile.parent.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
 
   // Copy the test engine to the test profile.
   let engineTemplateFile = do_get_file("data/engine.xml");
   engineTemplateFile.copyTo(engineFile.parent, "test-search-engine.xml");
 
   // Add the application's built-in plugin locations to the cache so it won't be ignored.
   let filesToIgnore = []
-  let defaultBranch = Services.prefs.getDefaultBranch(BROWSER_SEARCH_PREF);
-
-  let loadFromJARs = false;
-  try {
-    loadFromJARs = defaultBranch.getBoolPref("loadFromJars");
-  } catch (ex) {}
-
+  let chan = NetUtil.ioService.newChannel2("resource://search-plugins/list.txt",
+                                           null, // aOriginCharset
+                                           null, // aBaseURI
+                                           null, // aLoadingNode
+                                           Services.scriptSecurityManager.getSystemPrincipal(),
+                                           null, // aTriggeringPrincipal
+                                           Ci.nsILoadInfo.SEC_NORMAL,
+                                           Ci.nsIContentPolicy.TYPE_OTHER);
   let visibleDefaultEngines = [];
-  if (!loadFromJARs) {
-    filesToIgnore.push(getDir(NS_APP_SEARCH_DIR));
+  let sis = Cc["@mozilla.org/scriptableinputstream;1"].
+              createInstance(Ci.nsIScriptableInputStream);
+  sis.init(chan.open());
+  let list = sis.read(sis.available());
+  let names = list.split("\n").filter(n => !!n);
+  for (let name of names) {
+    if (name.endsWith(":hidden"))
+      continue;
+    visibleDefaultEngines.push(name);
+  }
+  let chromeURI = chan.URI;
+  if (chromeURI instanceof Ci.nsIJARURI) {
+    // JAR packaging, we only need the parent jar file.
+    let fileURI = chromeURI; // flat packaging
+    while (fileURI instanceof Ci.nsIJARURI)
+      fileURI = fileURI.JARFile;
+    fileURI.QueryInterface(Ci.nsIFileURL);
+    filesToIgnore.push(fileURI.file);
   } else {
-    let rootURIPref = defaultBranch.getCharPref("jarURIs");
-    let rootURIs = rootURIPref.split(",");
-    for (let root of rootURIs) {
-      let visibleEnginesForRoot = [];
-      let listURL = root + "list.txt";
-      let chan = NetUtil.ioService.newChannelFromURI2(makeURI(listURL),
-                                                      null, // aLoadingNode
-                                                      Services.scriptSecurityManager.getSystemPrincipal(),
-                                                      null, // aTriggeringPrincipal
-                                                      Ci.nsILoadInfo.SEC_NORMAL,
-                                                      Ci.nsIContentPolicy.TYPE_OTHER);
-      let sis = Cc["@mozilla.org/scriptableinputstream;1"].
-                createInstance(Ci.nsIScriptableInputStream);
-      sis.init(chan.open());
-      let list = sis.read(sis.available());
-      let names = list.split("\n").filter(n => !!n);
-      for (let name of names) {
-        if (name.endsWith(":hidden"))
-          continue;
-        visibleEnginesForRoot.push(name);
-      }
-
-      let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
-                        getService(Ci.nsIChromeRegistry);
-      let chromeURI = chromeReg.convertChromeURL(makeURI(root));
-      if (chromeURI instanceof Ci.nsIJARURI) {
-        // JAR packaging, we only need the parent jar file.
-        let fileURI = chromeURI; // flat packaging
-        while (fileURI instanceof Ci.nsIJARURI)
-          fileURI = fileURI.JARFile;
-        fileURI.QueryInterface(Ci.nsIFileURL);
-        filesToIgnore.push(fileURI.file);
-      } else {
-        // flat packaging, we need to find each .xml file.
-        for (let name of visibleEnginesForRoot) {
-          let uri = chromeReg.convertChromeURL(makeURI(root + name + ".xml"));
-          filesToIgnore.push(uri.QueryInterface(Ci.nsIFileURL).file);
-        }
-      }
-
-      visibleDefaultEngines = visibleDefaultEngines.concat(visibleEnginesForRoot);
+    // flat packaging, we need to find each .xml file.
+    for (let name of names) {
+      let url = "resource://search-plugins/" + name + ".xml";
+      let chan = NetUtil.ioService.newChannel2(url,
+                                               null, // aOriginCharset
+                                               null, // aBaseURI
+                                               null, // aLoadingNode
+                                               Services.scriptSecurityManager.getSystemPrincipal(),
+                                               null, // aTriggeringPrincipal
+                                               Ci.nsILoadInfo.SEC_NORMAL,
+                                               Ci.nsIContentPolicy.TYPE_OTHER);
+      filesToIgnore.push(chan.URI.QueryInterface(Ci.nsIFileURL).file);
     }
   }
 
   for (let file of filesToIgnore) {
     cacheTemplate.directories[file.path] = {
       lastModifiedTime: file.lastModifiedTime,
       engines: []
     };
deleted file mode 100644
--- a/toolkit/components/search/tests/xpcshell/test_sync_app.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-function run_test() {
-  removeMetadata();
-  removeCacheFile();
-
-  do_load_manifest("data/chrome.manifest");
-
-  configureToLoadJarEngines(false);
-
-  do_check_false(Services.search.isInitialized);
-
-  // test engine from dir is loaded.
-  let engine = Services.search.getEngineByName("TestEngineApp");
-  do_check_neq(engine, null);
-
-  do_check_true(Services.search.isInitialized);
-
-  // test jar engine is not loaded.
-  engine = Services.search.getEngineByName("bug645970");
-  do_check_eq(engine, null);
-}
--- a/toolkit/components/search/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini
@@ -56,25 +56,23 @@ support-files =
 [test_parseSubmissionURL.js]
 [test_SearchStaticData.js]
 [test_addEngine_callback.js]
 [test_multipleIcons.js]
 [test_resultDomain.js]
 [test_serialize_file.js]
 [test_searchSuggest.js]
 [test_async.js]
-[test_async_app.js]
 [test_async_addon.js]
 tags = addons
 [test_async_addon_no_override.js]
 tags = addons
 [test_async_distribution.js]
 [test_async_profile_engine.js]
 [test_sync.js]
-[test_sync_app.js]
 [test_sync_addon.js]
 tags = addons
 [test_sync_addon_no_override.js]
 tags = addons
 [test_sync_distribution.js]
 [test_sync_fallback.js]
 [test_sync_delay_fallback.js]
 [test_sync_profile_engine.js]
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -1031,19 +1031,21 @@ add_task(function* test_defaultSearchEng
   // initialized.
   let data = TelemetryEnvironment.currentEnvironment;
   checkEnvironmentData(data);
   Assert.ok(!("defaultSearchEngine" in data.settings));
   Assert.ok(!("defaultSearchEngineData" in data.settings));
 
   // Load the engines definitions from a custom JAR file: that's needed so that
   // the search provider reports an engine identifier.
-  let defaultBranch = Services.prefs.getDefaultBranch(null);
-  defaultBranch.setCharPref("browser.search.jarURIs", "chrome://testsearchplugin/locale/searchplugins/");
-  defaultBranch.setBoolPref("browser.search.loadFromJars", true);
+  let url = "chrome://testsearchplugin/locale/searchplugins/";
+  let resProt = Services.io.getProtocolHandler("resource")
+                        .QueryInterface(Ci.nsIResProtocolHandler);
+  resProt.setSubstitution("search-plugins",
+                          Services.io.newURI(url, null, null));
 
   // Initialize the search service.
   yield new Promise(resolve => Services.search.init(resolve));
 
   // Our default engine from the JAR file has an identifier. Check if it is correctly
   // reported.
   data = TelemetryEnvironment.currentEnvironment;
   checkEnvironmentData(data);
--- a/xpcom/io/nsAppFileLocationProvider.cpp
+++ b/xpcom/io/nsAppFileLocationProvider.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "nsAppFileLocationProvider.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsEnumeratorUtils.h"
 #include "nsIAtom.h"
 #include "nsIFile.h"
 #include "nsString.h"
 #include "nsXPIDLString.h"
 #include "nsISimpleEnumerator.h"
 #include "prenv.h"
 #include "nsCRT.h"
 
@@ -578,25 +579,28 @@ nsAppFileLocationProvider::GetFiles(cons
       keys[0] = &nullstr;
     }
     *aResult = new nsPathsDirectoryEnumerator(this, keys);
 #endif
     NS_ADDREF(*aResult);
     rv = NS_OK;
   }
   if (!nsCRT::strcmp(aProp, NS_APP_SEARCH_DIR_LIST)) {
-    static const char* keys[] = { nullptr, NS_APP_SEARCH_DIR, NS_APP_USER_SEARCH_DIR, nullptr };
+    static const char* keys[] = { nullptr, NS_APP_USER_SEARCH_DIR, nullptr };
     if (!keys[0] && !(keys[0] = PR_GetEnv("MOZ_SEARCH_ENGINE_PATH"))) {
       static const char nullstr = 0;
       keys[0] = &nullstr;
     }
     *aResult = new nsPathsDirectoryEnumerator(this, keys);
     NS_ADDREF(*aResult);
     rv = NS_OK;
   }
+  if (!strcmp(aProp, NS_APP_DISTRIBUTION_SEARCH_DIR_LIST)) {
+    return NS_NewEmptyEnumerator(aResult);
+  }
   return rv;
 }
 
 #if defined(MOZ_WIDGET_COCOA)
 bool
 nsAppFileLocationProvider::IsOSXLeopard()
 {
   static SInt32 version = 0;