Backed out 3 changesets (bug 1192924) for Android bustage CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Tue, 22 Sep 2015 11:02:55 -0700
changeset 263858 3e33e2914ac53e92686061591a3f58650359aadb
parent 263857 f45c7ca272a0f3ef811ba05bc94f45eb404f62cf
child 263865 46842118f0c94cb300d0eb7416dddf43b777dbab
child 263964 f80d1207c76c601f9165a44173b0b042c7566340
push id65448
push userkwierso@gmail.com
push dateTue, 22 Sep 2015 23:46:56 +0000
treeherdermozilla-inbound@eb9c01341845 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1192924
milestone44.0a1
backs out30d61283668592adfaec7a00aeeabc9d32a688c2
a6860f880e01572924f05421adf26bee06a72cb4
10e1fa2cc23a348e0266d28a76bf932fd2efdf2f
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
Backed out 3 changesets (bug 1192924) for Android bustage CLOSED TREE Backed out changeset 30d612836685 (bug 1192924) Backed out changeset a6860f880e01 (bug 1192924) Backed out changeset 10e1fa2cc23a (bug 1192924)
browser/base/content/aboutDialog.js
browser/base/content/browser.js
browser/base/content/newtab/newTab.js
browser/base/content/test/general/browser_remoteTroubleshoot.js
browser/components/loop/modules/MozLoopAPI.jsm
browser/components/nsBrowserGlue.js
browser/components/preferences/in-content/main.js
browser/experiments/Experiments.jsm
browser/modules/DirectoryLinksProvider.jsm
mobile/android/chrome/content/aboutFeedback.js
netwerk/protocol/http/UserAgentUpdates.jsm
services/datareporting/policy.jsm
services/datareporting/tests/xpcshell/test_policy.js
services/healthreport/healthreporter.jsm
services/healthreport/providers.jsm
testing/profiles/prefs_general.js
testing/talos/talos/config.py
testing/xpcshell/head.js
toolkit/components/telemetry/TelemetryController.jsm
toolkit/components/telemetry/TelemetryEnvironment.jsm
toolkit/components/telemetry/TelemetryReportingPolicy.jsm
toolkit/components/telemetry/TelemetrySession.jsm
toolkit/components/urlformatter/nsURLFormatter.js
toolkit/content/tests/unit/test_updateChannelModule.js
toolkit/content/tests/unit/xpcshell.ini
toolkit/modules/GMPInstallManager.jsm
toolkit/modules/GMPUtils.jsm
toolkit/modules/Troubleshoot.jsm
toolkit/modules/UpdateChannel.jsm
toolkit/modules/UpdateUtils.jsm
toolkit/modules/moz.build
toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js
toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
toolkit/modules/tests/xpcshell/xpcshell.ini
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/internal/GMPProvider.jsm
toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/internal/moz.build
toolkit/mozapps/extensions/nsBlocklistService.js
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.txt
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.xml
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad2.xml
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/empty.xml
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/good.xml
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/missing.xml
toolkit/mozapps/extensions/test/xpcshell/data/productaddons/unsigned.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app0/empty
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/bootstrap.js
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/install.rdf
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app3/features/system1@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app3/features/system3@tests.mozilla.org.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_2.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_1.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_2.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_3.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_1.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_2.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_3.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system4_1.xpi
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system5_1.xpi
toolkit/mozapps/extensions/test/xpcshell/test_ProductAddonChecker.js
toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
toolkit/mozapps/update/nsUpdateService.js
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -67,35 +67,35 @@ function init(aEvent)
   }
 
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
   let defaults = Services.prefs.getDefaultBranch("");
   let channelLabel = document.getElementById("currentChannel");
   let currentChannelText = document.getElementById("currentChannelText");
-  channelLabel.value = UpdateUtils.UpdateChannel;
+  channelLabel.value = UpdateChannel.get();
   if (/^release($|\-)/.test(channelLabel.value))
       currentChannelText.hidden = true;
 #endif
 
 #ifdef XP_MACOSX
   // it may not be sized at this point, and we need its width to calculate its position
   window.sizeToContent();
   window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
 #endif
 }
 
 #ifdef MOZ_UPDATER
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 var gAppUpdater;
 
 function onUnload(aEvent) {
   if (gAppUpdater.isChecking)
     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
   // Safe to call even when there isn't a download in progress.
   gAppUpdater.removeDownloadListener();
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -37,18 +37,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
                                   "resource:///modules/AboutHome.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
                                    "@mozilla.org/browser/favicon-service;1",
                                    "mozIAsyncFavicons");
 XPCOMUtils.defineLazyServiceGetter(this, "gDNSService",
                                    "@mozilla.org/network/dns-service;1",
                                    "nsIDNSService");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
@@ -2913,17 +2913,17 @@ var BrowserOnClick = {
       port: location.port,
       timestamp: Math.round(Date.now() / 1000),
       errorCode: transportSecurityInfo.errorCode,
       failedCertChain: asciiCertChain,
       userAgent: window.navigator.userAgent,
       version: 1,
       build: gAppInfo.appBuildID,
       product: gAppInfo.name,
-      channel: UpdateUtils.UpdateChannel
+      channel: UpdateChannel.get()
     }
 
     let reportURL = Services.prefs.getCharPref("security.ssl.errorReporting.url");
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
         .createInstance(Ci.nsIXMLHttpRequest);
     try {
       xhr.open("POST", reportURL);
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -13,16 +13,18 @@ Cu.import("resource://gre/modules/PageTh
 Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm");
 Cu.import("resource:///modules/DirectoryLinksProvider.jsm");
 Cu.import("resource://gre/modules/NewTabUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Rect",
   "resource://gre/modules/Geometry.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+  "resource://gre/modules/UpdateChannel.jsm");
 
 var {
   links: gLinks,
   allPages: gAllPages,
   linkChecker: gLinkChecker,
   pinnedLinks: gPinnedLinks,
   blockedLinks: gBlockedLinks,
   gridPrefs: gGridPrefs
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js
+++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js
@@ -55,17 +55,17 @@ add_task(function*() {
   Assert.ok(got.message.graphics, "should have graphics");
 
   // Check we have channel and build ID info:
   Assert.equal(got.message.application.buildID, Services.appinfo.appBuildID,
                "should have correct build ID");
 
   let updateChannel = null;
   try {
-    updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
+    updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
   } catch (ex) {}
   if (!updateChannel) {
     Assert.ok(!('updateChannel' in got.message.application),
                 "should not have update channel where not available.");
   } else {
     Assert.equal(got.message.application.updateChannel, updateChannel,
                  "should have correct update channel.");
   }
--- a/browser/components/loop/modules/MozLoopAPI.jsm
+++ b/browser/components/loop/modules/MozLoopAPI.jsm
@@ -20,18 +20,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
                                         "resource:///modules/loop/LoopStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
                                         "resource://gre/modules/MozSocialAPI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
                                         "resource://gre/modules/PageMetadata.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                         "resource://gre/modules/PluralForm.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                        "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                        "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                         "resource:///modules/UITour.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Social",
                                         "resource:///modules/Social.jsm");
 XPCOMUtils.defineLazyGetter(this, "appInfo", function() {
   return Cc["@mozilla.org/xre/app-info;1"]
            .getService(Ci.nsIXULAppInfo)
            .QueryInterface(Ci.nsIXULRuntime);
@@ -805,17 +805,17 @@ function injectLoopAPI(targetWindow) {
     appVersionInfo: {
       enumerable: true,
       get: function() {
         if (!appVersionInfo) {
           // If the lazy getter explodes, we're probably loaded in xpcshell,
           // which doesn't have what we need, so log an error.
           try {
             appVersionInfo = Cu.cloneInto({
-              channel: UpdateUtils.UpdateChannel,
+              channel: UpdateChannel.get(),
               version: appInfo.version,
               OS: appInfo.OS
             }, targetWindow);
           } catch (ex) {
             // only log outside of xpcshell to avoid extra message noise
             if (typeof targetWindow !== 'undefined' && "console" in targetWindow) {
               MozLoopService.log.error("Failed to construct appVersionInfo; if this isn't " +
                                        "an xpcshell unit test, something is wrong", ex);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -119,18 +119,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
                                   "resource:///modules/SignInToWebsite.jsm");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
 #ifdef E10S_TESTING_ONLY
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashReporter",
                                   "resource:///modules/ContentCrashReporters.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                   "resource:///modules/ContentCrashReporters.jsm");
 #endif
@@ -2972,17 +2972,17 @@ var E10SUINotification = {
   // e10s testing period to Nightly users.
   CURRENT_NOTICE_COUNT: 4,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
-      let updateChannel = UpdateUtils.UpdateChannel;
+      let updateChannel = UpdateChannel.get();
       let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
 
       skipE10sChecks = !channelAuthorized ||
                        Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y");
     } catch(e) {}
 
     if (skipE10sChecks) {
       return;
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -160,18 +160,18 @@ var gMainPane = {
       shouldProceed = !cancelQuit.data;
 
       if (shouldProceed) {
         for (let prefToChange of prefsToChange) {
           prefToChange.value = e10sCheckbox.checked;
         }
 
         let tmp = {};
-        Components.utils.import("resource://gre/modules/UpdateUtils.jsm", tmp);
-        if (!e10sCheckbox.checked && tmp.UpdateUtils.UpdateChannel != "default") {
+        Components.utils.import("resource://gre/modules/UpdateChannel.jsm", tmp);
+        if (!e10sCheckbox.checked && tmp.UpdateChannel.get() != "default") {
           Services.prefs.setBoolPref("browser.requestE10sFeedback", true);
           Services.prompt.alert(window, brandName, bundle.getString("e10sFeedbackAfterRestart"));
         }
         Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit |  Ci.nsIAppStartup.eRestart);
       }
     }
 
     // Revert the checkbox in case we didn't quit
--- a/browser/experiments/Experiments.jsm
+++ b/browser/experiments/Experiments.jsm
@@ -15,18 +15,18 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
@@ -270,17 +270,17 @@ Experiments.Policy.prototype = {
     return new Date(this.now().getTime() + offset);
   },
 
   oneshotTimer: function (callback, timeout, thisObj, name) {
     return CommonUtils.namedTimer(callback, timeout, thisObj, name);
   },
 
   updatechannel: function () {
-    return UpdateUtils.UpdateChannel;
+    return UpdateChannel.get();
   },
 
   locale: function () {
     let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
     return chrome.getSelectedLocale("global");
   },
 
   /**
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -21,18 +21,18 @@ Cu.import("resource://gre/modules/Timer.
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NewTabUtils",
   "resource://gre/modules/NewTabUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
   "resource://gre/modules/osfile.jsm")
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "eTLD",
   "@mozilla.org/network/effective-tld-service;1",
   "nsIEffectiveTLDService");
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => {
   return new TextDecoder();
 });
 XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
   return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
@@ -275,17 +275,17 @@ var DirectoryLinksProvider = {
       this._setupStartEndTime(link);
       this._suggestedLinks.set(suggestedSite, suggestedMap);
     }
   },
 
   _fetchAndCacheLinks: function DirectoryLinksProvider_fetchAndCacheLinks(uri) {
     // Replace with the same display locale used for selecting links data
     uri = uri.replace("%LOCALE%", this.locale);
-    uri = uri.replace("%CHANNEL%", UpdateUtils.UpdateChannel);
+    uri = uri.replace("%CHANNEL%", UpdateChannel.get());
 
     return this._downloadJsonData(uri).then(json => {
       return OS.File.writeAtomic(this._directoryFilePath, json, {tmpPath: this._directoryFilePath + ".tmp"});
     });
   },
 
   /**
    * Downloads a links with json content
--- a/mobile/android/chrome/content/aboutFeedback.js
+++ b/mobile/android/chrome/content/aboutFeedback.js
@@ -17,17 +17,17 @@ const HEARTY_ICON_XXHDPI = "chrome://bro
 
 const FLOATY_ICON_MDPI = "chrome://browser/skin/images/icon_floaty_mdpi.png";
 const FLOATY_ICON_HDPI = "chrome://browser/skin/images/icon_floaty_hdpi.png";
 const FLOATY_ICON_XHDPI = "chrome://browser/skin/images/icon_floaty_xhdpi.png";
 const FLOATY_ICON_XXHDPI = "chrome://browser/skin/images/icon_floaty_xxhdpi.png";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Messaging.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
+Cu.import("resource://gre/modules/UpdateChannel.jsm");
 document.addEventListener("DOMContentLoaded", init, false);
 
 function dump(a) {
   Services.console.logStringMessage(a);
 }
 
 function init() {
   let anchors = document.querySelectorAll(".maybe-later");
@@ -133,17 +133,17 @@ function sendFeedback(aEvent) {
     data["url"] = urlElement.value;
   }
 
   data["device"] = Services.sysinfo.get("device");
   data["manufacturer"] = Services.sysinfo.get("manufacturer");
   data["platform"] = Services.appinfo.OS;
   data["version"] = Services.appinfo.version;
   data["locale"] = Services.locale.getSystemLocale().getCategory("NSILOCALE_CTYPE");
-  data["channel"] = UpdateUtils.UpdateChannel;
+  data["channel"] = UpdateChannel.get();
 
   // Source field is added only when Fennec prompts the user.
   let getParam = window.location.href.split("?");
   if (getParam.length > 1) {
     let urlParam = new URLSearchParams(getParam[1]);
     if(urlParam.get("source")) {
       data["source"] = urlParam.get("source");
     }
--- a/netwerk/protocol/http/UserAgentUpdates.jsm
+++ b/netwerk/protocol/http/UserAgentUpdates.jsm
@@ -22,17 +22,17 @@ XPCOMUtils.defineLazyModuleGetter(
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "OS", "resource://gre/modules/osfile.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "Promise", "resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
-  this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
+  this, "UpdateChannel", "resource://gre/modules/UpdateChannel.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(
   this, "gUpdateTimer", "@mozilla.org/updates/timer-manager;1", "nsIUpdateTimerManager");
 
 XPCOMUtils.defineLazyGetter(this, "gApp",
   function() {
     return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo)
                                             .QueryInterface(Ci.nsIXULRuntime);
@@ -187,17 +187,17 @@ this.UserAgentUpdates = {
   _getParameters() {
     return {
       "%DATE%": function() { return Date.now().toString(); },
       "%PRODUCT%": function() { return gApp.name; },
       "%APP_ID%": function() { return gApp.ID; },
       "%APP_VERSION%": function() { return gApp.version; },
       "%BUILD_ID%": function() { return gApp.appBuildID; },
       "%OS%": function() { return gApp.OS; },
-      "%CHANNEL%": function() { return UpdateUtils.UpdateChannel; },
+      "%CHANNEL%": function() { return UpdateChannel.get(); },
       "%DISTRIBUTION%": function() { return this._getPref(PREF_APP_DISTRIBUTION, ""); },
       "%DISTRIBUTION_VERSION%": function() { return this._getPref(PREF_APP_DISTRIBUTION_VERSION, ""); },
     };
   },
 
   _getUpdateURL: function() {
     let url = this._getPref(PREF_UPDATES_URL, "");
     let params = this._getParameters();
--- a/services/datareporting/policy.jsm
+++ b/services/datareporting/policy.jsm
@@ -20,17 +20,17 @@ this.EXPORTED_SYMBOLS = [
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 #endif
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
+Cu.import("resource://gre/modules/UpdateChannel.jsm");
 
 // The current policy version number. If the version number stored in the prefs
 // is smaller than this, data upload will be disabled until the user is re-notified
 // about the policy changes.
 const DATAREPORTING_POLICY_VERSION = 1;
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
@@ -383,17 +383,17 @@ this.DataReportingPolicy.prototype = Obj
   },
 
   /**
    * The minimum policy version which for dataSubmissionPolicyAccepted to
    * to be valid.
    */
   get minimumPolicyVersion() {
     // First check if the current channel has an ove
-    let channel = UpdateUtils.getUpdateChannel(false);
+    let channel = UpdateChannel.get(false);
     let channelPref = this._prefs.get("minimumPolicyVersion.channel-" + channel);
     return channelPref !== undefined ?
            channelPref : this._prefs.get("minimumPolicyVersion", 1);
   },
 
   get dataSubmissionPolicyAcceptedVersion() {
     return this._prefs.get("dataSubmissionPolicyAcceptedVersion", 0);
   },
--- a/services/datareporting/tests/xpcshell/test_policy.js
+++ b/services/datareporting/tests/xpcshell/test_policy.js
@@ -3,32 +3,32 @@
 
 "use strict";
 
 const {utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/services/datareporting/policy.jsm");
 Cu.import("resource://testing-common/services/datareporting/mocks.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
+Cu.import("resource://gre/modules/UpdateChannel.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 
 function getPolicy(name,
                    aCurrentPolicyVersion = 1,
                    aMinimumPolicyVersion = 1,
                    aBranchMinimumVersionOverride) {
   let branch = "testing.datareporting." + name;
 
   // The version prefs should not be removed on reset, so set them in the
   // default branch.
   let defaultPolicyPrefs = new Preferences({ branch: branch + ".policy."
                                            , defaultBranch: true });
   defaultPolicyPrefs.set("currentPolicyVersion", aCurrentPolicyVersion);
   defaultPolicyPrefs.set("minimumPolicyVersion", aMinimumPolicyVersion);
-  let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateUtils.getUpdateChannel(false);
+  let branchOverridePrefName = "minimumPolicyVersion.channel-" + UpdateChannel.get(false);
   if (aBranchMinimumVersionOverride !== undefined)
     defaultPolicyPrefs.set(branchOverridePrefName, aBranchMinimumVersionOverride);
   else
     defaultPolicyPrefs.reset(branchOverridePrefName);
 
   let policyPrefs = new Preferences(branch + ".policy.");
   let healthReportPrefs = new Preferences(branch + ".healthreport.");
 
--- a/services/healthreport/healthreporter.jsm
+++ b/services/healthreport/healthreporter.jsm
@@ -25,18 +25,18 @@ Cu.import("resource://gre/modules/osfile
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryController",
                                   "resource://gre/modules/TelemetryController.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 // Oldest year to allow in date preferences. This module was implemented in
 // 2012 and no dates older than that should be encountered.
 const OLDEST_ALLOWED_YEAR = 2012;
 
 const DAYS_IN_PAYLOAD = 180;
 
 const DEFAULT_DATABASE_NAME = "healthreport.sqlite";
@@ -1098,17 +1098,17 @@ AbstractHealthReporter.prototype = Objec
         out[k] = ai[v];
       }
     } catch (ex) {
       this._log.warn("Could not obtain Services.appinfo: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     try {
-      out["updateChannel"] = UpdateUtils.UpdateChannel;
+      out["updateChannel"] = UpdateChannel.get();
     } catch (ex) {
       this._log.warn("Could not obtain update channel: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     return out;
   },
 });
--- a/services/healthreport/providers.jsm
+++ b/services/healthreport/providers.jsm
@@ -41,18 +41,18 @@ Cu.import("resource://gre/modules/osfile
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://services-common/utils.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
                                   "resource://gre/modules/PlacesDBUtils.jsm");
 
 
 const LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_LAST_NUMERIC};
 const LAST_TEXT_FIELD = {type: Metrics.Storage.FIELD_LAST_TEXT};
 const DAILY_DISCRETE_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_DISCRETE_NUMERIC};
 const DAILY_LAST_NUMERIC_FIELD = {type: Metrics.Storage.FIELD_DAILY_LAST_NUMERIC};
@@ -328,17 +328,17 @@ AppInfoProvider.prototype = Object.freez
       try {
         yield m.setLastText(k, ai[v]);
       } catch (ex) {
         this._log.warn("Error obtaining Services.appinfo." + v);
       }
     }
 
     try {
-      yield m.setLastText("updateChannel", UpdateUtils.UpdateChannel);
+      yield m.setLastText("updateChannel", UpdateChannel.get());
     } catch (ex) {
       this._log.warn("Could not obtain update channel: " +
                      CommonUtils.exceptionStr(ex));
     }
 
     yield m.setLastText("distributionID", this._prefs.get("distribution.id", ""));
     yield m.setLastText("distributionVersion", this._prefs.get("distribution.version", ""));
     yield m.setLastText("hotfixVersion", this._prefs.get("extensions.hotfix.lastVersion", ""));
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -90,17 +90,16 @@ user_pref("browser.safebrowsing.provider
 user_pref("browser.safebrowsing.provider.mozilla.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
 user_pref("browser.safebrowsing.provider.mozilla.updateURL", "http://%(server)s/safebrowsing-dummy/update");
 user_pref("privacy.trackingprotection.introURL", "http://%(server)s/trackingprotection/tour");
 // Point update checks to the local testing server for fast failures
 user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL");
 user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
 user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
 user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
-user_pref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
 // Turn off extension updates so they don't bother tests
 user_pref("extensions.update.enabled", false);
 // Make sure opening about:addons won't hit the network
 user_pref("extensions.webservice.discoverURL", "http://%(server)s/extensions-dummy/discoveryURL");
 // Make sure AddonRepository won't hit the network
 user_pref("extensions.getAddons.maxResults", 0);
 user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/repositoryGetURL");
 user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL");
--- a/testing/talos/talos/config.py
+++ b/testing/talos/talos/config.py
@@ -135,18 +135,16 @@ DEFAULTS = dict(
         'extensions.getAddons.search.browseURL':
             'http://127.0.0.1/extensions-dummy/repositoryBrowseURL',
         'extensions.getAddons.search.url':
             'http://127.0.0.1/extensions-dummy/repositorySearchURL',
         'plugins.update.url':
             'http://127.0.0.1/plugins-dummy/updateCheckURL',
         'media.gmp-manager.url':
             'http://127.0.0.1/gmpmanager-dummy/update.xml',
-        'extensions.systemAddon.update.url':
-            'http://127.0.0.1/dummy-system-addons.xml',
         'media.navigator.enabled': True,
         'media.peerconnection.enabled': True,
         'media.navigator.permission.disabled': True,
         'media.capturestream_hints.enabled': True,
         'browser.contentHandlers.types.0.uri': 'http://127.0.0.1/rss?url=%s',
         'browser.contentHandlers.types.1.uri': 'http://127.0.0.1/rss?url=%s',
         'browser.contentHandlers.types.2.uri': 'http://127.0.0.1/rss?url=%s',
         'browser.contentHandlers.types.3.uri': 'http://127.0.0.1/rss?url=%s',
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -1500,17 +1500,16 @@ try {
 
 // We need to avoid hitting the network with certain components.
 try {
   if (runningInParent) {
     let prefs = Components.classes["@mozilla.org/preferences-service;1"]
       .getService(Components.interfaces.nsIPrefBranch);
 
     prefs.setCharPref("media.gmp-manager.url.override", "http://%(server)s/dummy-gmp-manager.xml");
-    prefs.setCharPref("extensions.systemAddon.update.url", "http://%(server)s/dummy-system-addons.xml");
     prefs.setCharPref("browser.selfsupport.url", "https://%(server)s/selfsupport-dummy/");
     prefs.setCharPref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy");
     prefs.setCharPref("browser.search.geoip.url", "https://%(server)s/geoip-dummy");
   }
 } catch (e) { }
 
 // Make tests run consistently on DevEdition (which has a lightweight theme
 // selected by default).
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -75,18 +75,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionRecorder",
                                   "resource://gre/modules/SessionRecorder.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
                                   "resource://gre/modules/TelemetryArchive.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
                                   "resource://gre/modules/TelemetrySession.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryReportingPolicy",
                                   "resource://gre/modules/TelemetryReportingPolicy.jsm");
@@ -407,17 +407,17 @@ var Impl = {
     try {
       arch = Services.sysinfo.get("arch");
     } catch (e) {
       this._log.trace("assemblePing - Unable to get system architecture.", e);
     }
 
     let updateChannel = null;
     try {
-      updateChannel = UpdateUtils.getUpdateChannel(false);
+      updateChannel = UpdateChannel.get(false);
     } catch (e) {
       this._log.trace("assemblePing - Unable to get update channel.", e);
     }
 
     return {
       architecture: arch,
       buildId: Services.appinfo.appBuildID,
       name: Services.appinfo.name,
--- a/toolkit/components/telemetry/TelemetryEnvironment.jsm
+++ b/toolkit/components/telemetry/TelemetryEnvironment.jsm
@@ -27,18 +27,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/ctypes.jsm");
 #ifndef MOZ_WIDGET_GONK
 Cu.import("resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
                                   "resource://gre/modules/LightweightThemeManager.jsm");
 #endif
 XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
                                   "resource://gre/modules/ProfileAge.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 const CHANGE_THROTTLE_INTERVAL_MS = 5 * 60 * 1000;
 
 /**
  * This is a policy object used to override behavior for testing.
  */
 var Policy = {
   now: () => new Date(),
@@ -1002,17 +1002,17 @@ EnvironmentCache.prototype = {
   },
 
   /**
    * Update the cached settings data.
    */
   _updateSettings: function () {
     let updateChannel = null;
     try {
-      updateChannel = UpdateUtils.getUpdateChannel(false);
+      updateChannel = UpdateChannel.get(false);
     } catch (e) {}
 
     this._currentEnvironment.settings = {
 #ifndef MOZ_WIDGET_GONK
       addonCompatibilityCheckEnabled: AddonManager.checkCompatibility,
 #endif
       blocklistEnabled: Preferences.get(PREF_BLOCKLIST_ENABLED, true),
 #ifndef MOZ_WIDGET_ANDROID
--- a/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
+++ b/toolkit/components/telemetry/TelemetryReportingPolicy.jsm
@@ -14,18 +14,18 @@ Cu.import("resource://gre/modules/Log.js
 Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/Timer.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://services-common/observers.js", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
                                   "resource://gre/modules/TelemetrySend.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 const LOGGER_NAME = "Toolkit.Telemetry";
 const LOGGER_PREFIX = "TelemetryReportingPolicy::";
 
 // Oldest year to allow in date preferences. The FHR infobar was implemented in
 // 2012 and no dates older than that should be encountered.
 const OLDEST_ALLOWED_ACCEPTANCE_YEAR = 2012;
 
@@ -246,17 +246,17 @@ var TelemetryReportingPolicyImpl = {
    */
   get minimumPolicyVersion() {
     const minPolicyVersion = Preferences.get(PREF_MINIMUM_POLICY_VERSION, 1);
 
     // First check if the current channel has a specific minimum policy version. If not,
     // use the general minimum policy version.
     let channel = "";
     try {
-      channel = UpdateUtils.getUpdateChannel(false);
+      channel = UpdateChannel.get(false);
     } catch(e) {
       this._log.error("minimumPolicyVersion - Unable to retrieve the current channel.");
       return minPolicyVersion;
     }
     const channelPref = PREF_MINIMUM_POLICY_VERSION + ".channel-" + channel;
     return Preferences.get(channelPref, minPolicyVersion);
   },
 
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -138,16 +138,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
                                   "resource://gre/modules/TelemetryStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
                                   "resource://gre/modules/TelemetryLog.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
                                   "resource://gre/modules/ThirdPartyCookieProbe.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UITelemetry",
                                   "resource://gre/modules/UITelemetry.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
                                   "resource://gre/modules/TelemetryEnvironment.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 
 function generateUUID() {
   let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   // strip {}
--- a/toolkit/components/urlformatter/nsURLFormatter.js
+++ b/toolkit/components/urlformatter/nsURLFormatter.js
@@ -20,18 +20,18 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const PREF_APP_DISTRIBUTION           = "distribution.id";
 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 function nsURLFormatterService() {
   XPCOMUtils.defineLazyGetter(this, "appInfo", function UFS_appInfo() {
     return Cc["@mozilla.org/xre/app-info;1"].
            getService(Ci.nsIXULAppInfo).
            QueryInterface(Ci.nsIXULRuntime);
   });
 
@@ -106,17 +106,17 @@ nsURLFormatterService.prototype = {
     APPBUILDID:       function() this.appInfo.appBuildID,
     PLATFORMVERSION:  function() this.appInfo.platformVersion,
     PLATFORMBUILDID:  function() this.appInfo.platformBuildID,
     APP:              function() this.appInfo.name.toLowerCase().replace(/ /, ""),
     OS:               function() this.appInfo.OS,
     XPCOMABI:         function() this.ABI,
     BUILD_TARGET:     function() this.appInfo.OS + "_" + this.ABI,
     OS_VERSION:       function() this.OSVersion,
-    CHANNEL:          function() UpdateUtils.UpdateChannel,
+    CHANNEL:          function() UpdateChannel.get(),
     MOZILLA_API_KEY:   function() "@MOZ_MOZILLA_API_KEY@",
     GOOGLE_API_KEY:   function() "@MOZ_GOOGLE_API_KEY@",
     GOOGLE_OAUTH_API_CLIENTID:function() "@MOZ_GOOGLE_OAUTH_API_CLIENTID@",
     GOOGLE_OAUTH_API_KEY:     function() "@MOZ_GOOGLE_OAUTH_API_KEY@",
     BING_API_CLIENTID:function() "@MOZ_BING_API_CLIENTID@",
     BING_API_KEY:     function() "@MOZ_BING_API_KEY@",
     DISTRIBUTION:     function() this.distribution.id,
     DISTRIBUTION_VERSION: function() this.distribution.version
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/unit/test_updateChannelModule.js
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/Preferences.jsm");
+Components.utils.import("resource://gre/modules/UpdateChannel.jsm");
+
+const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
+const TEST_CHANNEL            = "TestChannel";
+const PREF_PARTNER_A          = "app.partner.test_partner_a";
+const TEST_PARTNER_A          = "TestPartnerA";
+const PREF_PARTNER_B          = "app.partner.test_partner_b";
+const TEST_PARTNER_B          = "TestPartnerB";
+
+function test_get() {
+  let defaultPrefs = new Preferences({ defaultBranch: true });
+  let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
+
+  do_check_eq(UpdateChannel.get(), currentChannel);
+  do_check_eq(UpdateChannel.get(false), currentChannel);
+
+  defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
+  do_check_eq(UpdateChannel.get(), TEST_CHANNEL);
+  do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
+
+  defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
+  defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
+  do_check_eq(UpdateChannel.get(),
+              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
+  do_check_eq(UpdateChannel.get(false), TEST_CHANNEL);
+}
+
+function run_test() {
+  test_get();
+}
--- a/toolkit/content/tests/unit/xpcshell.ini
+++ b/toolkit/content/tests/unit/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 head = 
 tail = 
 skip-if = toolkit == 'gonk'
 
 [test_contentAreaUtils.js]
+[test_updateChannelModule.js]
--- a/toolkit/modules/GMPInstallManager.jsm
+++ b/toolkit/modules/GMPInstallManager.jsm
@@ -3,16 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} =
   Components;
+// Chunk size for the incremental downloader
+const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
+// Incremental downloader interval
+const DOWNLOAD_INTERVAL  = 0;
 // 1 day default
 const DEFAULT_SECONDS_BETWEEN_CHECKS = 60 * 60 * 24;
 
 var GMPInstallFailureReason = {
   GMP_INVALID: 1,
   GMP_HIDDEN: 2,
   GMP_DISABLED: 3,
   GMP_UPDATE_DISABLED: 4,
@@ -21,38 +25,183 @@ var GMPInstallFailureReason = {
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://gre/modules/GMPUtils.jsm");
-Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
 
 this.EXPORTED_SYMBOLS = ["GMPInstallManager", "GMPExtractor", "GMPDownloader",
                          "GMPAddon"];
 
+var gLocale = null;
+
 // Shared code for suppressing bad cert dialogs
 XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() {
   let temp = { };
   Cu.import("resource://gre/modules/CertUtils.jsm", temp);
   return temp;
 });
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
+
+/**
+ * Number of milliseconds after which we need to cancel `checkForAddons`.
+ *
+ * Bug 1087674 suggests that the XHR we use in `checkForAddons` may
+ * never terminate in presence of network nuisances (e.g. strange
+ * antivirus behavior). This timeout is a defensive measure to ensure
+ * that we fail cleanly in such case.
+ */
+const CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS = 20000;
 
 function getScopedLogger(prefix) {
   // `PARENT_LOGGER_ID.` being passed here effectively links this logger
   // to the parentLogger.
   return Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP", prefix + " ");
 }
 
+// This is copied directly from nsUpdateService.js
+// It is used for calculating the URL string w/ var replacement.
+// TODO: refactor this out somewhere else
+XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
+  let osVersion;
+  try {
+    osVersion = Services.sysinfo.getProperty("name") + " " +
+                Services.sysinfo.getProperty("version");
+  }
+  catch (e) {
+    LOG("gOSVersion - OS Version unknown: updates are not possible.");
+  }
+
+  if (osVersion) {
+    if (AppConstants.platform == "win") {
+      const BYTE = ctypes.uint8_t;
+      const WORD = ctypes.uint16_t;
+      const DWORD = ctypes.uint32_t;
+      const WCHAR = ctypes.char16_t;
+      const BOOL = ctypes.int;
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+      const SZCSDVERSIONLENGTH = 128;
+      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+          [
+          {dwOSVersionInfoSize: DWORD},
+          {dwMajorVersion: DWORD},
+          {dwMinorVersion: DWORD},
+          {dwBuildNumber: DWORD},
+          {dwPlatformId: DWORD},
+          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+          {wServicePackMajor: WORD},
+          {wServicePackMinor: WORD},
+          {wSuiteMask: WORD},
+          {wProductType: BYTE},
+          {wReserved: BYTE}
+          ]);
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+          [
+          {wProcessorArchitecture: WORD},
+          {wReserved: WORD},
+          {dwPageSize: DWORD},
+          {lpMinimumApplicationAddress: ctypes.voidptr_t},
+          {lpMaximumApplicationAddress: ctypes.voidptr_t},
+          {dwActiveProcessorMask: DWORD.ptr},
+          {dwNumberOfProcessors: DWORD},
+          {dwProcessorType: DWORD},
+          {dwAllocationGranularity: DWORD},
+          {wProcessorLevel: WORD},
+          {wProcessorRevision: WORD}
+          ]);
+
+      let kernel32 = false;
+      try {
+        kernel32 = ctypes.open("Kernel32");
+      } catch (e) {
+        LOG("gOSVersion - Unable to open kernel32! " + e);
+        osVersion += ".unknown (unknown)";
+      }
+
+      if(kernel32) {
+        try {
+          // Get Service pack info
+          try {
+            let GetVersionEx = kernel32.declare("GetVersionExW",
+                                                ctypes.default_abi,
+                                                BOOL,
+                                                OSVERSIONINFOEXW.ptr);
+            let winVer = OSVERSIONINFOEXW();
+            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+            if(0 !== GetVersionEx(winVer.address())) {
+              osVersion += "." + winVer.wServicePackMajor
+                        +  "." + winVer.wServicePackMinor;
+            } else {
+              LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
+              osVersion += ".unknown";
+            }
+          } catch (e) {
+            LOG("gOSVersion - error getting service pack information. Exception: " + e);
+            osVersion += ".unknown";
+          }
+
+          // Get processor architecture
+          let arch = "unknown";
+          try {
+            let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+                                                       ctypes.default_abi,
+                                                       ctypes.void_t,
+                                                       SYSTEM_INFO.ptr);
+            let sysInfo = SYSTEM_INFO();
+            // Default to unknown
+            sysInfo.wProcessorArchitecture = 0xffff;
+
+            GetNativeSystemInfo(sysInfo.address());
+            switch(sysInfo.wProcessorArchitecture) {
+              case 9:
+                arch = "x64";
+                break;
+              case 6:
+                arch = "IA64";
+                break;
+              case 0:
+                arch = "x86";
+                break;
+            }
+          } catch (e) {
+            LOG("gOSVersion - error getting processor architecture.  Exception: " + e);
+          } finally {
+            osVersion += " (" + arch + ")";
+          }
+        } finally {
+          kernel32.close();
+        }
+      }
+    }
+
+    try {
+      osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+    }
+    catch (e) {
+      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
+    }
+    osVersion = encodeURIComponent(osVersion);
+  }
+  return osVersion;
+});
+
 /**
  * Provides an easy API for downloading and installing GMP Addons
  */
 function GMPInstallManager() {
 }
 /**
  * Temp file name used for downloading
  */
@@ -67,18 +216,34 @@ GMPInstallManager.prototype = {
     let url = GMPPrefs.get(GMPPrefs.KEY_URL_OVERRIDE);
     if (url) {
       log.info("Using override url: " + url);
     } else {
       url = GMPPrefs.get(GMPPrefs.KEY_URL);
       log.info("Using url: " + url);
     }
 
-    url = UpdateUtils.formatUpdateURL(url);
-
+    url =
+      url.replace(/%PRODUCT%/g, Services.appinfo.name)
+         .replace(/%VERSION%/g, Services.appinfo.version)
+         .replace(/%BUILD_ID%/g, Services.appinfo.appBuildID)
+         .replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + GMPUtils.ABI())
+         .replace(/%OS_VERSION%/g, gOSVersion);
+    if (/%LOCALE%/.test(url)) {
+      // TODO: Get the real local, does it actually matter for GMP plugins?
+      url = url.replace(/%LOCALE%/g, "en-US");
+    }
+    url =
+      url.replace(/%CHANNEL%/g, UpdateChannel.get())
+         .replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion)
+         .replace(/%DISTRIBUTION%/g,
+                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION))
+         .replace(/%DISTRIBUTION_VERSION%/g,
+                  GMPPrefs.get(GMPPrefs.KEY_APP_DISTRIBUTION_VERSION))
+         .replace(/\+/g, "%2B");
     log.info("Using url (with replacement): " + url);
     return url;
   },
   /**
    * Performs an addon check.
    * @return a promise which will be resolved or rejected.
    *         The promise is resolved with an array of GMPAddons
    *         The promise is rejected with an object with properties:
@@ -90,37 +255,48 @@ GMPInstallManager.prototype = {
     let log = getScopedLogger("GMPInstallManager.checkForAddons");
     if (this._deferred) {
         log.error("checkForAddons already called");
         return Promise.reject({type: "alreadycalled"});
     }
     this._deferred = Promise.defer();
     let url = this._getURL();
 
-    let allowNonBuiltIn = true;
-    let certs = null;
-    if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE)) {
-      allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN, true);
-      if (GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
-        certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
-      }
+    this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+                    createInstance(Ci.nsISupports);
+    // This is here to let unit test code override XHR
+    if (this._request.wrappedJSObject) {
+      this._request = this._request.wrappedJSObject;
     }
+    this._request.open("GET", url, true);
+    let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true);
+    this._request.channel.notificationCallbacks =
+      new gCertUtils.BadCertHandler(allowNonBuiltIn);
+    // Prevent the request from reading from the cache.
+    this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
+    // Prevent the request from writing to the cache.
+    this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
 
-    ProductAddonChecker.getProductAddonList(url, allowNonBuiltIn, certs).then((addons) => {
-      if (!addons) {
-        this._deferred.resolve([]);
-      }
-      else {
-        this._deferred.resolve([for (a of addons) new GMPAddon(a)]);
-      }
-      delete this._deferred;
-    }, (ex) => {
-      this._deferred.reject(ex);
-      delete this._deferred;
-    });
+    this._request.overrideMimeType("text/xml");
+    // The Cache-Control header is only interpreted by proxies and the
+    // final destination. It does not help if a resource is already
+    // cached locally.
+    this._request.setRequestHeader("Cache-Control", "no-cache");
+    // HTTP/1.0 servers might not implement Cache-Control and
+    // might only implement Pragma: no-cache
+    this._request.setRequestHeader("Pragma", "no-cache");
+
+    this._request.timeout = CHECK_FOR_ADDONS_TIMEOUT_DELAY_MS;
+    this._request.addEventListener("error", event => this.onFailXML("onErrorXML", event), false);
+    this._request.addEventListener("abort", event => this.onFailXML("onAbortXML", event), false);
+    this._request.addEventListener("timeout", event => this.onFailXML("onTimeoutXML", event), false);
+    this._request.addEventListener("load", event => this.onLoadXML(event), false);
+
+    log.info("sending request to: " + url);
+    this._request.send(null);
 
     return this._deferred.promise;
   },
   /**
    * Installs the specified addon and calls a callback when done.
    * @param gmpAddon The GMPAddon object to install
    * @return a promise which will be resolved or rejected
    *         The promise will resolve with an array of paths that were extracted
@@ -312,33 +488,192 @@ GMPInstallManager.prototype = {
     log.info("Done cleanup");
   },
 
   /**
    * If set to true, specifies to leave the temporary downloaded zip file.
    * This is useful for tests.
    */
   overrideLeaveDownloadedZip: false,
+
+  /**
+   * The XMLHttpRequest succeeded and the document was loaded.
+   * @param event The nsIDOMEvent for the load
+  */
+  onLoadXML: function(event) {
+    let log = getScopedLogger("GMPInstallManager.onLoadXML");
+    try {
+      log.info("request completed downloading document");
+      let certs = null;
+      if (!Services.prefs.prefHasUserValue(GMPPrefs.KEY_URL_OVERRIDE) &&
+          GMPPrefs.get(GMPPrefs.KEY_CERT_CHECKATTRS, true)) {
+        certs = gCertUtils.readCertPrefs(GMPPrefs.KEY_CERTS_BRANCH);
+      }
+
+      let allowNonBuiltIn = !GMPPrefs.get(GMPPrefs.KEY_CERT_REQUIREBUILTIN,
+                                          true);
+      log.info("allowNonBuiltIn: " + allowNonBuiltIn);
+
+      gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);
+
+      this.parseResponseXML();
+    } catch (ex) {
+      log.error("could not load xml: " + ex);
+      this._deferred.reject({
+        target: event.target,
+        status: this._getChannelStatus(event.target),
+        message: "" + ex,
+      });
+      delete this._deferred;
+    }
+  },
+
+  /**
+   * Returns the status code for the XMLHttpRequest
+   */
+  _getChannelStatus: function(request) {
+    let log = getScopedLogger("GMPInstallManager._getChannelStatus");
+    let status = null;
+    try {
+      status = request.status;
+      log.info("request.status is: " + request.status);
+    }
+    catch (e) {
+    }
+
+    if (status == null) {
+      status = request.channel.QueryInterface(Ci.nsIRequest).status;
+    }
+    return status;
+  },
+
+  /**
+   * There was an error of some kind during the XMLHttpRequest.  This
+   * error may have been caused by external factors (e.g. network
+   * issues) or internally (by a timeout).
+   *
+   * @param event The nsIDOMEvent for the error
+   */
+  onFailXML: function(failure, event) {
+    let log = getScopedLogger("GMPInstallManager.onFailXML " + failure);
+    let request = event.target;
+    let status = this._getChannelStatus(request);
+    let message = "request.status: " + status +  " (" + event.type + ")";
+    log.warn(message);
+    this._deferred.reject({
+      target: request,
+      status: status,
+      message: message
+    });
+    delete this._deferred;
+  },
+
+  /**
+   * Returns an array of GMPAddon objects discovered by the update check.
+   * Or returns an empty array if there were any problems with parsing.
+   * If there's an error, it will be logged if logging is enabled.
+   */
+  parseResponseXML: function() {
+    try {
+      let log = getScopedLogger("GMPInstallManager.parseResponseXML");
+      let updatesElement = this._request.responseXML.documentElement;
+      if (!updatesElement) {
+        let message = "empty updates document";
+        log.warn(message);
+        this._deferred.reject({
+          target: this._request,
+          message: message
+        });
+        delete this._deferred;
+        return;
+      }
+
+      if (updatesElement.nodeName != "updates") {
+        let message = "got node name: " + updatesElement.nodeName +
+          ", expected: updates";
+        log.warn(message);
+        this._deferred.reject({
+          target: this._request,
+          message: message
+        });
+        delete this._deferred;
+        return;
+      }
+
+      const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
+      let gmpResults = [];
+      for (let i = 0; i < updatesElement.childNodes.length; ++i) {
+        let updatesChildElement = updatesElement.childNodes.item(i);
+        if (updatesChildElement.nodeType != ELEMENT_NODE) {
+          continue;
+        }
+        if (updatesChildElement.localName == "addons") {
+          gmpResults = GMPAddon.parseGMPAddonsNode(updatesChildElement);
+        }
+      }
+      this._deferred.resolve(gmpResults);
+      delete this._deferred;
+    } catch (e) {
+      this._deferred.reject({
+        target: this._request,
+        message: e
+      });
+      delete this._deferred;
+    }
+  },
 };
 
 /**
  * Used to construct a single GMP addon
  * GMPAddon objects are returns from GMPInstallManager.checkForAddons
  * GMPAddon objects can also be used in calls to GMPInstallManager.installAddon
  *
- * @param addon The ProductAddonChecker `addon` object
+ * @param gmpAddon The AUS response XML's DOM element `addon`
  */
-function GMPAddon(addon) {
+function GMPAddon(gmpAddon) {
   let log = getScopedLogger("GMPAddon.constructor");
-  for (let name of Object.keys(addon)) {
-    this[name] = addon[name];
-  }
+  gmpAddon.QueryInterface(Ci.nsIDOMElement);
+  ["id", "URL", "hashFunction",
+   "hashValue", "version", "size"].forEach(name => {
+    if (gmpAddon.hasAttribute(name)) {
+      this[name] = gmpAddon.getAttribute(name);
+    }
+  });
+  this.size = Number(this.size) || undefined;
   log.info ("Created new addon: " + this.toString());
 }
+/**
+ * Parses an XML GMP addons node from AUS into an array
+ * @param addonsElement An nsIDOMElement compatible node with XML from AUS
+ * @return An array of GMPAddon results
+ */
+GMPAddon.parseGMPAddonsNode = function(addonsElement) {
+  let log = getScopedLogger("GMPAddon.parseGMPAddonsNode");
+  let gmpResults = [];
+  if (addonsElement.localName !== "addons") {
+    return;
+  }
 
+  addonsElement.QueryInterface(Ci.nsIDOMElement);
+  let addonCount = addonsElement.childNodes.length;
+  for (let i = 0; i < addonCount; ++i) {
+    let addonElement = addonsElement.childNodes.item(i);
+    if (addonElement.localName !== "addon") {
+      continue;
+    }
+    addonElement.QueryInterface(Ci.nsIDOMElement);
+    try {
+      gmpResults.push(new GMPAddon(addonElement));
+    } catch (e) {
+      log.warn("invalid addon: " + e);
+      continue;
+    }
+  }
+  return gmpResults;
+};
 GMPAddon.prototype = {
   /**
    * Returns a string representation of the addon
    */
   toString: function() {
     return this.id + " (" +
            "isValid: " + this.isValid +
            ", isInstalled: " + this.isInstalled +
@@ -459,56 +794,192 @@ GMPExtractor.prototype = {
  * Constructs an object which downloads and initiates an install of
  * the specified GMPAddon object.
  * @param gmpAddon The addon to install.
  */
 function GMPDownloader(gmpAddon)
 {
   this._gmpAddon = gmpAddon;
 }
-
+/**
+ * Computes the file hash of fileToHash with the specified hash function
+ * @param hashFunctionName A hash function name such as sha512
+ * @param fileToHash An nsIFile to hash
+ * @return a promise which resolve to a digest in binary hex format
+ */
+GMPDownloader.computeHash = function(hashFunctionName, fileToHash) {
+  let log = getScopedLogger("GMPDownloader.computeHash");
+  let digest;
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
+  fileStream.init(fileToHash, FileUtils.MODE_RDONLY,
+                  FileUtils.PERMS_FILE, 0);
+  try {
+    let hash = Cc["@mozilla.org/security/hash;1"].
+               createInstance(Ci.nsICryptoHash);
+    let hashFunction =
+      Ci.nsICryptoHash[hashFunctionName.toUpperCase()];
+    if (!hashFunction) {
+      log.error("could not get hash function");
+      return Promise.reject();
+    }
+    hash.init(hashFunction);
+    hash.updateFromStream(fileStream, -1);
+    digest = binaryToHex(hash.finish(false));
+  } catch (e) {
+    log.warn("failed to compute hash: " + e);
+    digest = "";
+  }
+  fileStream.close();
+  return Promise.resolve(digest);
+},
 GMPDownloader.prototype = {
   /**
    * Starts the download process for an addon.
    * @return a promise which will be resolved or rejected
    *         See GMPInstallManager.installAddon for resolve/rejected info
    */
   start: function() {
-    let log = getScopedLogger("GMPDownloader");
-    let gmpAddon = this._gmpAddon;
-
-    if (!gmpAddon.isValid) {
+    let log = getScopedLogger("GMPDownloader.start");
+    this._deferred = Promise.defer();
+    if (!this._gmpAddon.isValid) {
       log.info("gmpAddon is not valid, will not continue");
       return Promise.reject({
         target: this,
         status: status,
         type: "downloaderr"
       });
     }
 
-    return ProductAddonChecker.downloadAddon(gmpAddon).then((zipPath) => {
+    let uri = Services.io.newURI(this._gmpAddon.URL, null, null);
+    this._request = Cc["@mozilla.org/network/incremental-download;1"].
+                    createInstance(Ci.nsIIncrementalDownload);
+    let gmpFile = FileUtils.getFile("TmpD", [this._gmpAddon.id + ".zip"]);
+    if (gmpFile.exists()) {
+      gmpFile.remove(false);
+    }
+
+    log.info("downloading from " + uri.spec + " to " + gmpFile.path);
+    this._request.init(uri, gmpFile, DOWNLOAD_CHUNK_BYTES_SIZE,
+                       DOWNLOAD_INTERVAL);
+    this._request.start(this, null);
+    return this._deferred.promise;
+  },
+  // For nsIRequestObserver
+  onStartRequest: function(request, context) {
+  },
+  // For nsIRequestObserver
+  // Called when the GMP addon zip file is downloaded
+  onStopRequest: function(request, context, status) {
+    let log = getScopedLogger("GMPDownloader.onStopRequest");
+    log.info("onStopRequest called");
+    if (!Components.isSuccessCode(status)) {
+      log.info("status failed: " + status);
+      this._deferred.reject({
+        target: this,
+        status: status,
+        type: "downloaderr"
+      });
+      return;
+    }
+
+    let promise = this._verifyDownload();
+    promise.then(() => {
+      log.info("GMP file is ready to unzip");
+      let destination = this._request.destination;
+
+      let zipPath = destination.path;
+      let gmpAddon = this._gmpAddon;
+      let installToDirPath = Cc["@mozilla.org/file/local;1"].
+                          createInstance(Ci.nsIFile);
       let path = OS.Path.join(OS.Constants.Path.profileDir,
                               gmpAddon.id,
                               gmpAddon.version);
-      log.info("install to directory path: " + path);
-      let gmpInstaller = new GMPExtractor(zipPath, path);
+      installToDirPath.initWithPath(path);
+      log.info("install to directory path: " + installToDirPath.path);
+      let gmpInstaller = new GMPExtractor(zipPath, installToDirPath.path);
       let installPromise = gmpInstaller.install();
-      return installPromise.then(extractedPaths => {
+      installPromise.then(extractedPaths => {
         // Success, set the prefs
         let now = Math.round(Date.now() / 1000);
         GMPPrefs.set(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
         // Reset the trial create pref, so that Gecko knows to do a test
         // run before reporting that the GMP works to content.
         GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_TRIAL_CREATE, gmpAddon.version,
                        gmpAddon.id);
         // Remember our ABI, so that if the profile is migrated to another
         // platform or from 32 -> 64 bit, we notice and don't try to load the
         // unexecutable plugin library.
-        GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, gmpAddon.id);
+        GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), gmpAddon.id);
         // Setting the version pref signals installation completion to consumers,
         // if you need to set other prefs etc. do it before this.
         GMPPrefs.set(GMPPrefs.KEY_PLUGIN_VERSION, gmpAddon.version,
                      gmpAddon.id);
-        return extractedPaths;
+        this._deferred.resolve(extractedPaths);
+      }, err => {
+        this._deferred.reject(err);
+      });
+    }, err => {
+      log.warn("verifyDownload check failed");
+      this._deferred.reject({
+        target: this,
+        status: 200,
+        type: "verifyerr"
       });
     });
   },
+  /**
+   * Verifies that the downloaded zip file's hash matches the GMPAddon hash.
+   * @return a promise which resolves if the download verifies
+   */
+  _verifyDownload: function() {
+    let verifyDownloadDeferred = Promise.defer();
+    let log = getScopedLogger("GMPDownloader._verifyDownload");
+    log.info("_verifyDownload called");
+    if (!this._request) {
+      return Promise.reject();
+    }
+
+    let destination = this._request.destination;
+    log.info("for path: " + destination.path);
+
+    // Ensure that the file size matches the expected file size.
+    if (this._gmpAddon.size !== undefined &&
+        destination.fileSize != this._gmpAddon.size) {
+      log.warn("Downloader:_verifyDownload downloaded size " +
+               destination.fileSize + " != expected size " +
+               this._gmpAddon.size + ".");
+      return Promise.reject();
+    }
+
+    let promise = GMPDownloader.computeHash(this._gmpAddon.hashFunction, destination);
+    promise.then(digest => {
+        let expectedDigest = this._gmpAddon.hashValue.toLowerCase();
+        if (digest !== expectedDigest) {
+          log.warn("hashes do not match! Got: `" +
+                   digest + "`, expected: `" + expectedDigest +  "`");
+          this._deferred.reject();
+          return;
+        }
+
+        log.info("hashes match!");
+        verifyDownloadDeferred.resolve();
+    }, err => {
+        verifyDownloadDeferred.reject();
+    });
+    return verifyDownloadDeferred.promise;
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver])
 };
+
+/**
+ * Convert a string containing binary values to hex.
+ */
+function binaryToHex(input) {
+  let result = "";
+  for (let i = 0; i < input.length; ++i) {
+    let hex = input.charCodeAt(i).toString(16);
+    if (hex.length == 1)
+      hex = "0" + hex;
+    result += hex;
+  }
+  return result;
+}
--- a/toolkit/modules/GMPUtils.jsm
+++ b/toolkit/modules/GMPUtils.jsm
@@ -121,16 +121,37 @@ this.GMPUtils = {
     }
     this.reportedKeys.push(key);
 
     let hist = Services.telemetry.getHistogramById(key);
     if (hist) {
       hist.add(value);
     }
   },
+
+  ABI: function() {
+    // This is copied directly from nsUpdateService.js
+    let abi = null;
+    try {
+      abi = Services.appinfo.XPCOMABI;
+    }
+    catch (e) {
+      return "unknown";
+    }
+    if (AppConstants.platform == "macosx") {
+      // Mac universal build should report a different ABI than either macppc
+      // or mactel.
+      let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+                     getService(Ci.nsIMacUtils);
+
+      if (macutils.isUniversalBinary)
+        abi += "-u-" + macutils.architecturesInBinary;
+    }
+    return abi;
+  }
 };
 
 /**
  * Manages preferences for GMP addons
  */
 this.GMPPrefs = {
   KEY_EME_ENABLED:              "media.eme.enabled",
   KEY_PLUGIN_ENABLED:           "media.{0}.enabled",
--- a/toolkit/modules/Troubleshoot.jsm
+++ b/toolkit/modules/Troubleshoot.jsm
@@ -176,17 +176,17 @@ var dataProviders = {
       buildID: Services.appinfo.appBuildID,
       userAgent: Cc["@mozilla.org/network/protocol;1?name=http"].
                  getService(Ci.nsIHttpProtocolHandler).
                  userAgent,
       safeMode: Services.appinfo.inSafeMode,
     };
 
     if (AppConstants.MOZ_UPDATER)
-      data.updateChannel = Cu.import("resource://gre/modules/UpdateUtils.jsm", {}).UpdateUtils.UpdateChannel;
+      data.updateChannel = Cu.import("resource://gre/modules/UpdateChannel.jsm", {}).UpdateChannel.get();
 
     try {
       data.vendor = Services.prefs.getCharPref("app.support.vendor");
     }
     catch (e) {}
     let urlFormatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
                        getService(Ci.nsIURLFormatter);
     try {
new file mode 100644
--- /dev/null
+++ b/toolkit/modules/UpdateChannel.jsm
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+this.EXPORTED_SYMBOLS = ["UpdateChannel"];
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+this.UpdateChannel = {
+  /**
+   * Read the update channel from defaults only.  We do this to ensure that
+   * the channel is tightly coupled with the application and does not apply
+   * to other instances of the application that may use the same profile.
+   *
+   * @param [optional] aIncludePartners
+   *        Whether or not to include the partner bits. Default: true.
+   */
+  get: function UpdateChannel_get(aIncludePartners = true) {
+    let channel = AppConstants.MOZ_UPDATE_CHANNEL;
+    let defaults = Services.prefs.getDefaultBranch(null);
+    try {
+      channel = defaults.getCharPref("app.update.channel");
+    } catch (e) {
+      // use default value when pref not found
+    }
+
+    if (aIncludePartners) {
+      try {
+        let partners = Services.prefs.getChildList("app.partner.").sort();
+        if (partners.length) {
+          channel += "-cck";
+          partners.forEach(function (prefName) {
+            channel += "-" + Services.prefs.getCharPref(prefName);
+          });
+        }
+      } catch (e) {
+        Cu.reportError(e);
+      }
+    }
+
+    return channel;
+  }
+};
deleted file mode 100644
--- a/toolkit/modules/UpdateUtils.jsm
+++ /dev/null
@@ -1,347 +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/. */
-
-this.EXPORTED_SYMBOLS = ["UpdateUtils"];
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/ctypes.jsm");
-
-const FILE_UPDATE_LOCALE                  = "update.locale";
-const PREF_APP_DISTRIBUTION               = "distribution.id";
-const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
-const PREF_APP_B2G_VERSION                = "b2g.version";
-const PREF_APP_UPDATE_CUSTOM              = "app.update.custom";
-const PREF_APP_UPDATE_IMEI_HASH           = "app.update.imei_hash";
-
-
-this.UpdateUtils = {
-  /**
-   * Read the update channel from defaults only.  We do this to ensure that
-   * the channel is tightly coupled with the application and does not apply
-   * to other instances of the application that may use the same profile.
-   *
-   * @param [optional] aIncludePartners
-   *        Whether or not to include the partner bits. Default: true.
-   */
-  getUpdateChannel(aIncludePartners = true) {
-    let channel = AppConstants.MOZ_UPDATE_CHANNEL;
-    let defaults = Services.prefs.getDefaultBranch(null);
-    try {
-      channel = defaults.getCharPref("app.update.channel");
-    } catch (e) {
-      // use default value when pref not found
-    }
-
-    if (aIncludePartners) {
-      try {
-        let partners = Services.prefs.getChildList("app.partner.").sort();
-        if (partners.length) {
-          channel += "-cck";
-          partners.forEach(function (prefName) {
-            channel += "-" + Services.prefs.getCharPref(prefName);
-          });
-        }
-      } catch (e) {
-        Cu.reportError(e);
-      }
-    }
-
-    return channel;
-  },
-
-  get UpdateChannel() {
-    return this.getUpdateChannel();
-  },
-
-  /**
-   * Formats a URL by replacing %...% values with OS, build and locale specific
-   * values.
-   *
-   * @param  url
-   *         The URL to format.
-   * @return The formatted URL.
-   */
-  formatUpdateURL(url) {
-    url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
-    url = url.replace(/%VERSION%/g, Services.appinfo.version);
-    url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
-    url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI);
-    url = url.replace(/%OS_VERSION%/g, this.OSVersion);
-    if (/%LOCALE%/.test(url)) {
-      url = url.replace(/%LOCALE%/g, this.Locale);
-    }
-    url = url.replace(/%CHANNEL%/g, this.UpdateChannel);
-    url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
-    url = url.replace(/%DISTRIBUTION%/g,
-                      getDistributionPrefValue(PREF_APP_DISTRIBUTION));
-    url = url.replace(/%DISTRIBUTION_VERSION%/g,
-                      getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
-    url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, ""));
-    url = url.replace(/\+/g, "%2B");
-
-    if (AppConstants.platform == "gonk") {
-      let sysLibs = {};
-      Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
-      let productDevice = sysLibs.libcutils.property_get("ro.product.device");
-      let buildType = sysLibs.libcutils.property_get("ro.build.type");
-      url = url.replace(/%PRODUCT_MODEL%/g,
-                        sysLibs.libcutils.property_get("ro.product.model"));
-      if (buildType == "user" || buildType == "userdebug") {
-        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
-      } else {
-        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
-      }
-      url = url.replace(/%B2G_VERSION%/g,
-                        Preferences.get(PREF_APP_B2G_VERSION, null));
-      url = url.replace(/%IMEI%/g,
-                        getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
-    }
-
-    return url;
-  }
-};
-
-/* Get the distribution pref values, from defaults only */
-function getDistributionPrefValue(aPrefName) {
-  var prefValue = "default";
-
-  try {
-    prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
-  } catch (e) {
-    // use default when pref not found
-  }
-
-  return prefValue;
-}
-
-/**
- * Gets the locale from the update.locale file for replacing %LOCALE% in the
- * update url. The update.locale file can be located in the application
- * directory or the GRE directory with preference given to it being located in
- * the application directory.
- */
-XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() {
-  let channel;
-  let locale;
-  for (let res of ['app', 'gre']) {
-    channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
-                                      null,
-                                      null,
-                                      null,      // aLoadingNode
-                                      Services.scriptSecurityManager.getSystemPrincipal(),
-                                      null,      // aTriggeringPrincipal
-                                      Ci.nsILoadInfo.SEC_NORMAL,
-                                      Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
-    try {
-      let inputStream = channel.open();
-      locale = NetUtil.readInputStreamToString(inputStream, inputStream.available());
-    } catch(e) {}
-    if (locale)
-      return locale.trim();
-  }
-
-  Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " +
-                 "application or GRE directories");
-
-  return null;
-});
-
-/* Windows only getter that returns the processor architecture. */
-XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
-  // Get processor architecture
-  let arch = "unknown";
-
-  const WORD = ctypes.uint16_t;
-  const DWORD = ctypes.uint32_t;
-
-  // This structure is described at:
-  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-      [
-      {wProcessorArchitecture: WORD},
-      {wReserved: WORD},
-      {dwPageSize: DWORD},
-      {lpMinimumApplicationAddress: ctypes.voidptr_t},
-      {lpMaximumApplicationAddress: ctypes.voidptr_t},
-      {dwActiveProcessorMask: DWORD.ptr},
-      {dwNumberOfProcessors: DWORD},
-      {dwProcessorType: DWORD},
-      {dwAllocationGranularity: DWORD},
-      {wProcessorLevel: WORD},
-      {wProcessorRevision: WORD}
-      ]);
-
-  let kernel32 = false;
-  try {
-    kernel32 = ctypes.open("Kernel32");
-  } catch (e) {
-    Cu.reportError("Unable to open kernel32! Exception: " + e);
-  }
-
-  if (kernel32) {
-    try {
-      let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
-                                                 ctypes.default_abi,
-                                                 ctypes.void_t,
-                                                 SYSTEM_INFO.ptr);
-      let winSystemInfo = SYSTEM_INFO();
-      // Default to unknown
-      winSystemInfo.wProcessorArchitecture = 0xffff;
-
-      GetNativeSystemInfo(winSystemInfo.address());
-      switch (winSystemInfo.wProcessorArchitecture) {
-        case 9:
-          arch = "x64";
-          break;
-        case 6:
-          arch = "IA64";
-          break;
-        case 0:
-          arch = "x86";
-          break;
-      }
-    } catch (e) {
-      Cu.reportError("Error getting processor architecture. " +
-                     "Exception: " + e);
-    } finally {
-      kernel32.close();
-    }
-  }
-
-  return arch;
-});
-
-XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() {
-  let abi = null;
-  try {
-    abi = Services.appinfo.XPCOMABI;
-  }
-  catch (e) {
-    Cu.reportError("XPCOM ABI unknown");
-  }
-
-  if (AppConstants.platform == "macosx") {
-    // Mac universal build should report a different ABI than either macppc
-    // or mactel.
-    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
-                   getService(Ci.nsIMacUtils);
-
-    if (macutils.isUniversalBinary) {
-      abi += "-u-" + macutils.architecturesInBinary;
-    }
-  } else if (AppConstants.platform == "win") {
-    // Windows build should report the CPU architecture that it's running on.
-    abi += "-" + gWinCPUArch;
-  }
-  return abi;
-});
-
-XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() {
-  let osVersion;
-  try {
-    osVersion = Services.sysinfo.getProperty("name") + " " +
-                Services.sysinfo.getProperty("version");
-  }
-  catch (e) {
-    Cu.reportError("OS Version unknown.");
-  }
-
-  if (osVersion) {
-    if (AppConstants.platform == "win") {
-      const BYTE = ctypes.uint8_t;
-      const WORD = ctypes.uint16_t;
-      const DWORD = ctypes.uint32_t;
-      const WCHAR = ctypes.char16_t;
-      const BOOL = ctypes.int;
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
-      const SZCSDVERSIONLENGTH = 128;
-      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
-          [
-          {dwOSVersionInfoSize: DWORD},
-          {dwMajorVersion: DWORD},
-          {dwMinorVersion: DWORD},
-          {dwBuildNumber: DWORD},
-          {dwPlatformId: DWORD},
-          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
-          {wServicePackMajor: WORD},
-          {wServicePackMinor: WORD},
-          {wSuiteMask: WORD},
-          {wProductType: BYTE},
-          {wReserved: BYTE}
-          ]);
-
-      // This structure is described at:
-      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-          [
-          {wProcessorArchitecture: WORD},
-          {wReserved: WORD},
-          {dwPageSize: DWORD},
-          {lpMinimumApplicationAddress: ctypes.voidptr_t},
-          {lpMaximumApplicationAddress: ctypes.voidptr_t},
-          {dwActiveProcessorMask: DWORD.ptr},
-          {dwNumberOfProcessors: DWORD},
-          {dwProcessorType: DWORD},
-          {dwAllocationGranularity: DWORD},
-          {wProcessorLevel: WORD},
-          {wProcessorRevision: WORD}
-          ]);
-
-      let kernel32 = false;
-      try {
-        kernel32 = ctypes.open("Kernel32");
-      } catch (e) {
-        Cu.reportError("Unable to open kernel32! " + e);
-        osVersion += ".unknown (unknown)";
-      }
-
-      if (kernel32) {
-        try {
-          // Get Service pack info
-          try {
-            let GetVersionEx = kernel32.declare("GetVersionExW",
-                                                ctypes.default_abi,
-                                                BOOL,
-                                                OSVERSIONINFOEXW.ptr);
-            let winVer = OSVERSIONINFOEXW();
-            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
-
-            if(0 !== GetVersionEx(winVer.address())) {
-              osVersion += "." + winVer.wServicePackMajor +
-                           "." + winVer.wServicePackMinor;
-            } else {
-              Cu.reportError("Unknown failure in GetVersionEX (returned 0)");
-              osVersion += ".unknown";
-            }
-          } catch (e) {
-            Cu.reportError("Error getting service pack information. Exception: " + e);
-            osVersion += ".unknown";
-          }
-        } finally {
-          kernel32.close();
-        }
-
-        // Add processor architecture
-        osVersion += " (" + gWinCPUArch + ")";
-      }
-    }
-
-    try {
-      osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
-    }
-    catch (e) {
-      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
-    }
-    osVersion = encodeURIComponent(osVersion);
-  }
-  return osVersion;
-});
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -70,17 +70,17 @@ EXTRA_JS_MODULES += [
     'ShortcutUtils.jsm',
     'Sntp.jsm',
     'SpatialNavigation.jsm',
     'Sqlite.jsm',
     'Task.jsm',
     'TelemetryTimestamps.jsm',
     'Timer.jsm',
     'Troubleshoot.jsm',
-    'UpdateUtils.jsm',
+    'UpdateChannel.jsm',
     'WebChannel.jsm',
     'WindowDraggingUtils.jsm',
     'ZipUtils.jsm',
 ]
 
 EXTRA_PP_JS_MODULES += [
     'AppConstants.jsm',
     'SessionRecorder.jsm',
--- a/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
+++ b/toolkit/modules/tests/xpcshell/test_GMPInstallManager.js
@@ -6,19 +6,16 @@ const URL_HOST = "http://localhost";
 
 var GMPScope = Cu.import("resource://gre/modules/GMPInstallManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm")
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-
-let { computeHash } = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
 
 do_get_profile();
 
 function run_test() {Cu.import("resource://gre/modules/Preferences.jsm")
   Preferences.set("media.gmp.log.dump", true);
   Preferences.set("media.gmp.log.level", 0);
   run_next_test();
 }
@@ -428,17 +425,17 @@ function* test_checkForAddons_installAdd
   let zipFileName = "test_" + id + "_GMP.zip";
 
   let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName;
   do_print("zipURL: " + zipURL);
 
   let data = "e~=0.5772156649";
   let zipFile = createNewZipFile(zipFileName, data);
   let hashFunc = "sha256";
-  let expectedDigest = yield computeHash(hashFunc, zipFile.path);
+  let expectedDigest = yield GMPDownloader.computeHash(hashFunc, zipFile);
   let fileSize = zipFile.fileSize;
   if (wantInstallReject) {
     fileSize = 1;
   }
 
   let responseXML =
     "<?xml version=\"1.0\"?>" +
     "<updates>" +
@@ -454,16 +451,17 @@ function* test_checkForAddons_installAdd
 
   overrideXHR(200, responseXML);
   let installManager = new GMPInstallManager();
   let gmpAddons = yield installManager.checkForAddons();
   do_check_eq(gmpAddons.length, 1);
   let gmpAddon = gmpAddons[0];
   do_check_false(gmpAddon.isInstalled);
 
+  GMPInstallManager.overrideLeaveDownloadedZip = true;
   try {
     let extractedPaths = yield installManager.installAddon(gmpAddon);
     if (wantInstallReject) {
       do_check_true(false); // installAddon() should have thrown.
     }
     do_check_eq(extractedPaths.length, 1);
     let extractedPath = extractedPaths[0];
 
@@ -471,35 +469,50 @@ function* test_checkForAddons_installAdd
 
     let extractedFile = Cc["@mozilla.org/file/local;1"].
                         createInstance(Ci.nsIFile);
     extractedFile.initWithPath(extractedPath);
     do_check_true(extractedFile.exists());
     let readData = readStringFromFile(extractedFile);
     do_check_eq(readData, data);
 
+    // Check that the downloaded zip matches the offered zip exactly
+    let downloadedGMPFile = FileUtils.getFile("TmpD",
+      [gmpAddon.id + ".zip"]);
+    do_check_true(downloadedGMPFile.exists());
+    let downloadedBytes = getBinaryFileData(downloadedGMPFile);
+    let sourceBytes = getBinaryFileData(zipFile);
+    do_check_true(compareBinaryData(downloadedBytes, sourceBytes));
+
     // Make sure the prefs are set correctly
     do_check_true(!!GMPScope.GMPPrefs.get(
       GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id));
     do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "",
                                       gmpAddon.id),
                 "1.1");
     do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, "",
                                       gmpAddon.id),
-                UpdateUtils.ABI);
+                GMPScope.GMPUtils.ABI());
     // Make sure it reports as being installed
     do_check_true(gmpAddon.isInstalled);
 
     // Cleanup
     extractedFile.parent.remove(true);
     zipFile.remove(false);
     httpServer.stop(function() {});
+    do_print("Removing downloaded GMP file: " + downloadedGMPFile.path);
+    downloadedGMPFile.remove(false);
     installManager.uninit();
   } catch(ex) {
     zipFile.remove(false);
+    let downloadedGMPFile = FileUtils.getFile("TmpD",
+      [gmpAddon.id + ".zip"]);
+    do_print("Removing downloaded GMP file from exception handler: " +
+             downloadedGMPFile.path);
+    downloadedGMPFile.remove(false);
     if (!wantInstallReject) {
       do_throw("install update should not reject");
     }
   }
 }
 
 add_task(test_checkForAddons_installAddon.bind(null, "1", true, false));
 add_task(test_checkForAddons_installAddon.bind(null, "2", false, false));
@@ -781,16 +794,55 @@ function overrideXHR(status, response, o
   registrar.registerFactory(overrideXHR.myxhr.classID,
                             overrideXHR.myxhr.classDescription,
                             overrideXHR.myxhr.contractID,
                             overrideXHR.myxhr);
   return overrideXHR.myxhr;
 }
 
 /**
+ * Compares binary data of 2 arrays and returns true if they are the same
+ *
+ * @param arr1 The first array to compare
+ * @param arr2 The second array to compare
+*/
+function compareBinaryData(arr1, arr2) {
+  do_check_eq(arr1.length, arr2.length);
+  for (let i = 0; i < arr1.length; i++) {
+    if (arr1[i] != arr2[i]) {
+      do_print("Data differs at index " + i +
+               ", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
+      return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * Reads a file's data and returns it
+ *
+ * @param file The file to read the data from
+ * @return array of bytes for the data in the file.
+*/
+function getBinaryFileData(file) {
+  let fileStream = Cc["@mozilla.org/network/file-input-stream;1"].
+                   createInstance(Ci.nsIFileInputStream);
+  // Open as RD_ONLY with default permissions.
+  fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+
+  // Check the returned size versus the expected size.
+  let stream = Cc["@mozilla.org/binaryinputstream;1"].
+               createInstance(Ci.nsIBinaryInputStream);
+  stream.setInputStream(fileStream);
+  let bytes = stream.readByteArray(stream.available());
+  fileStream.close();
+  return bytes;
+}
+
+/**
  * Creates a new zip file containing a file with the specified data
  * @param zipName The name of the zip file
  * @param data The data to go inside the zip for the filename entry1.info
  */
 function createNewZipFile(zipName, data) {
    // Create a zip file which will be used for extracting
     let stream = Cc["@mozilla.org/io/string-input-stream;1"].
                  createInstance(Ci.nsIStringInputStream);
deleted file mode 100644
--- a/toolkit/modules/tests/xpcshell/test_UpdateUtils_updatechannel.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* 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/. */
-
-const { utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-
-const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
-const TEST_CHANNEL            = "TestChannel";
-const PREF_PARTNER_A          = "app.partner.test_partner_a";
-const TEST_PARTNER_A          = "TestPartnerA";
-const PREF_PARTNER_B          = "app.partner.test_partner_b";
-const TEST_PARTNER_B          = "TestPartnerB";
-
-add_task(function* test_updatechannel() {
-  let defaultPrefs = new Preferences({ defaultBranch: true });
-  let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
-
-  do_check_eq(UpdateUtils.UpdateChannel, currentChannel);
-  do_check_eq(UpdateUtils.getUpdateChannel(true), currentChannel);
-  do_check_eq(UpdateUtils.getUpdateChannel(false), currentChannel);
-
-  defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
-  do_check_eq(UpdateUtils.UpdateChannel, TEST_CHANNEL);
-  do_check_eq(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL);
-  do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
-
-  defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
-  defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
-  do_check_eq(UpdateUtils.UpdateChannel,
-              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
-  do_check_eq(UpdateUtils.getUpdateChannel(true),
-              TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
-  do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
-});
deleted file mode 100644
--- a/toolkit/modules/tests/xpcshell/test_UpdateUtils_url.js
+++ /dev/null
@@ -1,295 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* 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/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://testing-common/AppInfo.jsm");
-Cu.import("resource://gre/modules/ctypes.jsm");
-
-const PREF_APP_UPDATE_CHANNEL     = "app.update.channel";
-const PREF_APP_PARTNER_BRANCH     = "app.partner.";
-const PREF_DISTRIBUTION_ID        = "distribution.id";
-const PREF_DISTRIBUTION_VERSION   = "distribution.version";
-
-const URL_PREFIX = "http://localhost/";
-
-const MSG_SHOULD_EQUAL = " should equal the expected value";
-
-// For INSTALL_LOCALE
-load("../../../mozapps/update/tests/data/xpcshellConstantsPP.js");
-
-updateAppInfo();
-const gAppInfo = getAppInfo();
-const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null);
-
-function setUpdateChannel(aChannel) {
-  gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel);
-}
-
-function getServicePack() {
-  // NOTE: This function is a helper function and not a test.  Thus,
-  // it uses throw() instead of do_throw().  Any tests that use this function
-  // should catch exceptions thrown in this function and deal with them
-  // appropriately (usually by calling do_throw).
-  const BYTE = ctypes.uint8_t;
-  const WORD = ctypes.uint16_t;
-  const DWORD = ctypes.uint32_t;
-  const WCHAR = ctypes.char16_t;
-  const BOOL = ctypes.int;
-
-  // This structure is described at:
-  // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
-  const SZCSDVERSIONLENGTH = 128;
-  const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
-      [
-      {dwOSVersionInfoSize: DWORD},
-      {dwMajorVersion: DWORD},
-      {dwMinorVersion: DWORD},
-      {dwBuildNumber: DWORD},
-      {dwPlatformId: DWORD},
-      {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
-      {wServicePackMajor: WORD},
-      {wServicePackMinor: WORD},
-      {wSuiteMask: WORD},
-      {wProductType: BYTE},
-      {wReserved: BYTE}
-      ]);
-
-  let kernel32 = ctypes.open("kernel32");
-  try {
-    let GetVersionEx = kernel32.declare("GetVersionExW",
-                                        ctypes.default_abi,
-                                        BOOL,
-                                        OSVERSIONINFOEXW.ptr);
-    let winVer = OSVERSIONINFOEXW();
-    winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
-
-    if (0 === GetVersionEx(winVer.address())) {
-      // Using "throw" instead of "do_throw" (see NOTE above)
-      throw("Failure in GetVersionEx (returned 0)");
-    }
-
-    return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
-  } finally {
-    kernel32.close();
-  }
-}
-
-function getProcArchitecture() {
-  // NOTE: This function is a helper function and not a test.  Thus,
-  // it uses throw() instead of do_throw().  Any tests that use this function
-  // should catch exceptions thrown in this function and deal with them
-  // appropriately (usually by calling do_throw).
-  const WORD = ctypes.uint16_t;
-  const DWORD = ctypes.uint32_t;
-
-  // This structure is described at:
-  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
-  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
-      [
-      {wProcessorArchitecture: WORD},
-      {wReserved: WORD},
-      {dwPageSize: DWORD},
-      {lpMinimumApplicationAddress: ctypes.voidptr_t},
-      {lpMaximumApplicationAddress: ctypes.voidptr_t},
-      {dwActiveProcessorMask: DWORD.ptr},
-      {dwNumberOfProcessors: DWORD},
-      {dwProcessorType: DWORD},
-      {dwAllocationGranularity: DWORD},
-      {wProcessorLevel: WORD},
-      {wProcessorRevision: WORD}
-      ]);
-
-  let kernel32 = ctypes.open("kernel32");
-  try {
-    let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
-                                               ctypes.default_abi,
-                                               ctypes.void_t,
-                                               SYSTEM_INFO.ptr);
-    let sysInfo = SYSTEM_INFO();
-    // Default to unknown
-    sysInfo.wProcessorArchitecture = 0xffff;
-
-    GetNativeSystemInfo(sysInfo.address());
-    switch(sysInfo.wProcessorArchitecture) {
-      case 9:
-        return "x64";
-      case 6:
-        return "IA64";
-      case 0:
-        return "x86";
-      default:
-        // Using "throw" instead of "do_throw" (see NOTE above)
-        throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
-    }
-  } finally {
-    kernel32.close();
-  }
-}
-
-// Helper function for formatting a url and getting the result we're
-// interested in
-function getResult(url) {
-  url = UpdateUtils.formatUpdateURL(url);
-  return url.substr(URL_PREFIX.length).split("/")[0];
-}
-
-// url constructed with %PRODUCT%
-add_task(function* test_product() {
-  let url = URL_PREFIX + "%PRODUCT%/";
-  Assert.equal(getResult(url), gAppInfo.name,
-               "the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %VERSION%
-add_task(function* test_version() {
-  let url = URL_PREFIX + "%VERSION%/";
-  Assert.equal(getResult(url), gAppInfo.version,
-               "the url param for %VERSION%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %BUILD_ID%
-add_task(function* test_build_id() {
-  let url = URL_PREFIX + "%BUILD_ID%/";
-  Assert.equal(getResult(url), gAppInfo.appBuildID,
-               "the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %BUILD_TARGET%
-// XXX TODO - it might be nice if we tested the actual ABI
-add_task(function* test_build_target() {
-  let url = URL_PREFIX + "%BUILD_TARGET%/";
-
-  let abi;
-  try {
-    abi = gAppInfo.XPCOMABI;
-  } catch (e) {
-    do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
-  }
-
-  if (AppConstants.platform == "macosx") {
-    // Mac universal build should report a different ABI than either macppc
-    // or mactel. This is necessary since nsUpdateService.js will set the ABI to
-    // Universal-gcc3 for Mac universal builds.
-    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
-                   getService(Ci.nsIMacUtils);
-
-    if (macutils.isUniversalBinary) {
-      abi += "-u-" + macutils.architecturesInBinary;
-    }
-  } else if (IS_WIN) {
-    // Windows build should report the CPU architecture that it's running on.
-    abi += "-" + getProcArchitecture();
-  }
-
-  Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
-               "the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %LOCALE%
-// Bug 488936 added the update.locale file that stores the update locale
-add_task(function* test_locale() {
-  // The code that gets the locale accesses the profile which is only available
-  // after calling do_get_profile in xpcshell tests. This prevents an error from
-  // being logged.
-  do_get_profile();
-
-  let url = URL_PREFIX + "%LOCALE%/";
-  Assert.equal(getResult(url), INSTALL_LOCALE,
-               "the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %CHANNEL%
-add_task(function* test_channel() {
-  let url = URL_PREFIX + "%CHANNEL%/";
-  setUpdateChannel("test_channel");
-  Assert.equal(getResult(url), "test_channel",
-               "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %CHANNEL% with distribution partners
-add_task(function* test_channel_distribution() {
-  let url = URL_PREFIX + "%CHANNEL%/";
-  gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
-                                 "test_partner1");
-  gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
-                                 "test_partner2");
-  Assert.equal(getResult(url),
-               "test_channel-cck-test_partner1-test_partner2",
-               "the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %PLATFORM_VERSION%
-add_task(function* test_platform_version() {
-  let url = URL_PREFIX + "%PLATFORM_VERSION%/";
-  Assert.equal(getResult(url), gAppInfo.platformVersion,
-               "the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %OS_VERSION%
-add_task(function* test_os_version() {
-  let url = URL_PREFIX + "%OS_VERSION%/";
-  let osVersion;
-  let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
-  osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
-
-  if (IS_WIN) {
-    try {
-      let servicePack = getServicePack();
-      osVersion += "." + servicePack;
-    } catch (e) {
-      do_throw("Failure obtaining service pack: " + e);
-    }
-
-    if ("5.0" === sysInfo.getProperty("version")) { // Win2K
-      osVersion += " (unknown)";
-    } else {
-      try {
-        osVersion += " (" + getProcArchitecture() + ")";
-      } catch (e) {
-        do_throw("Failed to obtain processor architecture: " + e);
-      }
-    }
-  }
-
-  if (osVersion) {
-    try {
-      osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
-    } catch (e) {
-      // Not all platforms have a secondary widget library, so an error is
-      // nothing to worry about.
-    }
-    osVersion = encodeURIComponent(osVersion);
-  }
-
-  Assert.equal(getResult(url), osVersion,
-               "the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %DISTRIBUTION%
-add_task(function* test_distribution() {
-  let url = URL_PREFIX + "%DISTRIBUTION%/";
-  gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
-  Assert.equal(getResult(url), "test_distro",
-               "the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
-});
-
-// url constructed with %DISTRIBUTION_VERSION%
-add_task(function* test_distribution_version() {
-  let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
-  gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
-  Assert.equal(getResult(url), "test_distro_version",
-               "the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
-});
-
-add_task(function* test_custom() {
-  Services.prefs.setCharPref("app.update.custom", "custom");
-  let url = URL_PREFIX + "%CUSTOM%/";
-  Assert.equal(getResult(url), "custom",
-               "the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
-});
--- a/toolkit/modules/tests/xpcshell/xpcshell.ini
+++ b/toolkit/modules/tests/xpcshell/xpcshell.ini
@@ -52,14 +52,12 @@ skip-if = toolkit == 'android'
 [test_sqlite_shutdown.js]
 skip-if = toolkit == 'android'
 [test_task.js]
 skip-if = toolkit == 'android'
 [test_TelemetryTimestamps.js]
 skip-if = toolkit == 'android'
 [test_timer.js]
 skip-if = toolkit == 'android'
-[test_UpdateUtils_url.js]
-[test_UpdateUtils_updatechannel.js]
 [test_web_channel.js]
 [test_web_channel_broker.js]
 [test_ZipUtils.js]
 skip-if = toolkit == 'android'
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -854,23 +854,16 @@ var AddonManagerInternal = {
       AddonManager.shutdown.addBlocker(name, AMProviderShutdown);
     }
 
     this.pendingProviders.delete(aProvider);
     this.providers.add(aProvider);
     logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
   },
 
-  _getProviderByName(aName) {
-    for (let provider of this.providers) {
-      if (providerName(provider) == aName)
-        return provider;
-    }
-  },
-
   /**
    * Initializes the AddonManager, loading any known providers and initializing
    * them.
    */
   startup: function AMI_startup() {
     try {
       if (gStarted)
         return;
@@ -1466,19 +1459,19 @@ var AddonManagerInternal = {
   backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     let buPromise = Task.spawn(function* backgroundUpdateTask() {
       let hotfixID = this.hotfixID;
 
-      let appUpdateEnabled = Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
-                             Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
-      let checkHotfix = hotfixID && appUpdateEnabled;
+      let checkHotfix = hotfixID &&
+                        Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
+                        Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
 
       logger.debug("Background update check beginning");
 
       Services.obs.notifyObservers(null, "addons-background-update-start", null);
 
       if (this.updateEnabled) {
         let scope = {};
         Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
@@ -1615,25 +1608,16 @@ var AddonManagerInternal = {
               }
             });
 
             aInstall.install();
           }
         }
       }
 
-      if (appUpdateEnabled) {
-        try {
-          yield AddonManagerInternal._getProviderByName("XPIProvider").updateSystemAddons();
-        }
-        catch (e) {
-          logger.warn("Failed to update system addons", e);
-        }
-      }
-
       logger.debug("Background update check complete");
       Services.obs.notifyObservers(null,
                                    "addons-background-update-complete",
                                    null);
     }.bind(this));
     // Fork the promise chain so we can log the error and let our caller see it too.
     buPromise.then(null, e => logger.warn("Error in background update", e));
     return buPromise;
--- a/toolkit/mozapps/extensions/internal/GMPProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/GMPProvider.jsm
@@ -14,17 +14,16 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/GMPUtils.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(
   this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(
   this, "setTimeout", "resource://gre/modules/Timer.jsm");
 
 const URI_EXTENSION_STRINGS  = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME       = "type.%ID%.name";
@@ -486,18 +485,18 @@ GMPWrapper.prototype = {
   },
 
   validate: function() {
     if (!this.isInstalled) {
       // Not installed -> Valid.
       return { installed: false, valid: true };
     }
 
-    let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, this._plugin.id);
-    if (abi != UpdateUtils.ABI) {
+    let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, GMPUtils.ABI(), this._plugin.id);
+    if (abi != GMPUtils.ABI()) {
       // ABI doesn't match. Possibly this is a profile migrated across platforms
       // or from 32 -> 64 bit.
       return {
         installed: true,
         mismatchedABI: true,
         valid: false
       };
     }
deleted file mode 100644
--- a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm
+++ /dev/null
@@ -1,322 +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";
-
-const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
-
-this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
-
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/CertUtils.jsm");
-Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-
-let logger = Log.repository.getLogger("addons.productaddons");
-
-/**
- * Number of milliseconds after which we need to cancel `downloadXML`.
- *
- * Bug 1087674 suggests that the XHR we use in `downloadXML` may
- * never terminate in presence of network nuisances (e.g. strange
- * antivirus behavior). This timeout is a defensive measure to ensure
- * that we fail cleanly in such case.
- */
-const TIMEOUT_DELAY_MS = 20000;
-// Chunk size for the incremental downloader
-const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
-// Incremental downloader interval
-const DOWNLOAD_INTERVAL  = 0;
-// How much of a file to read into memory at a time for hashing
-const HASH_CHUNK_SIZE = 8192;
-
-/**
- * Gets the status of an XMLHttpRequest either directly or from its underlying
- * channel.
- *
- * @param  request
- *         The XMLHttpRequest.
- * @return an integer status value.
- */
-function getRequestStatus(request) {
-  let status = null;
-  try {
-    status = request.status;
-  }
-  catch (e) {
-  }
-
-  if (status != null) {
-    return status;
-  }
-
-  return request.channel.QueryInterface(Ci.nsIRequest).status;
-}
-
-/**
- * Downloads an XML document from a URL optionally testing the SSL certificate
- * for certain attributes.
- *
- * @param  url
- *         The url to download from.
- * @param  allowNonBuiltIn
- *         Whether to trust SSL certificates without a built-in CA issuer.
- * @param  allowedCerts
- *         The list of certificate attributes to match the SSL certificate
- *         against or null to skip checks.
- * @return a promise that resolves to the DOM document downloaded or rejects
- *         with a JS exception in case of error.
- */
-function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
-  return new Promise((resolve, reject) => {
-    let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
-                  createInstance(Ci.nsISupports);
-    // This is here to let unit test code override XHR
-    if (request.wrappedJSObject) {
-      request = request.wrappedJSObject;
-    }
-    request.open("GET", url, true);
-    request.channel.notificationCallbacks = new BadCertHandler(allowNonBuiltIn);
-    // Prevent the request from reading from the cache.
-    request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
-    // Prevent the request from writing to the cache.
-    request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
-    request.timeout = TIMEOUT_DELAY_MS;
-
-    request.overrideMimeType("text/xml");
-    // The Cache-Control header is only interpreted by proxies and the
-    // final destination. It does not help if a resource is already
-    // cached locally.
-    request.setRequestHeader("Cache-Control", "no-cache");
-    // HTTP/1.0 servers might not implement Cache-Control and
-    // might only implement Pragma: no-cache
-    request.setRequestHeader("Pragma", "no-cache");
-
-    let fail = (event) => {
-      let request = event.target;
-      let status = getRequestStatus(request);
-      let message = "Failed downloading XML, status: " + status +  ", reason: " + event.type;
-      logger.warn(message);
-      let ex = new Error(message);
-      ex.status = status;
-      reject(ex);
-    };
-
-    let success = (event) => {
-      logger.info("Completed downloading document");
-      let request = event.target;
-
-      try {
-        checkCert(request.channel, allowNonBuiltIn, allowedCerts);
-      } catch (ex) {
-        logger.error("Request failed certificate checks: " + ex);
-        ex.status = getRequestStatus(request);
-        reject(ex);
-        return;
-      }
-
-      resolve(request.responseXML);
-    };
-
-    request.addEventListener("error", fail, false);
-    request.addEventListener("abort", fail, false);
-    request.addEventListener("timeout", fail, false);
-    request.addEventListener("load", success, false);
-
-    logger.info("sending request to: " + url);
-    request.send(null);
-  });
-}
-
-/**
- * Parses a list of add-ons from a DOM document.
- *
- * @param  document
- *         The DOM document to parse.
- * @return null if there is no <addons> element otherwise an array of the addons
- *         listed.
- */
-function parseXML(document) {
-  // Check that the root element is correct
-  if (document.documentElement.localName != "updates") {
-    throw new Error("got node name: " + document.documentElement.localName +
-                    ", expected: updates");
-  }
-
-  // Check if there are any addons elements in the updates element
-  let addons = document.querySelector("updates:root > addons");
-  if (!addons) {
-    return null;
-  }
-
-  let results = [];
-  let addonList = document.querySelectorAll("updates:root > addons > addon");
-  for (let addonElement of addonList) {
-    let addon = {};
-
-    for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) {
-      if (addonElement.hasAttribute(name)) {
-        addon[name] = addonElement.getAttribute(name);
-      }
-    }
-    addon.size = Number(addon.size) || undefined;
-
-    results.push(addon);
-  }
-
-  return results;
-}
-
-/**
- * Downloads file from a URL using the incremental file downloader.
- *
- * @param  url
- *         The url to download from.
- * @return a promise that resolves to the path of a temporary file or rejects
- *         with a JS exception in case of error.
- */
-function downloadFile(url) {
-  return new Promise((resolve, reject) => {
-    let observer = {
-      onStartRequest: function() {},
-
-      onStopRequest: function(request, context, status) {
-        if (!Components.isSuccessCode(status)) {
-          logger.warn("File download failed: 0x" + status.toString(16));
-          tmpFile.remove(true);
-          reject(Components.Exception("File download failed", status));
-          return;
-        }
-
-        resolve(tmpFile.path);
-      }
-    };
-
-    let uri = NetUtil.newURI(url);
-    let request = Cc["@mozilla.org/network/incremental-download;1"].
-                  createInstance(Ci.nsIIncrementalDownload);
-    let tmpFile = FileUtils.getFile("TmpD", ["tmpaddon"]);
-    tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
-
-    logger.info("Downloading from " + uri.spec + " to " + tmpFile.path);
-    request.init(uri, tmpFile, DOWNLOAD_CHUNK_BYTES_SIZE, DOWNLOAD_INTERVAL);
-    request.start(observer, null);
-  });
-}
-
-/**
- * Convert a string containing binary values to hex.
- */
-function binaryToHex(input) {
-  let result = "";
-  for (let i = 0; i < input.length; ++i) {
-    let hex = input.charCodeAt(i).toString(16);
-    if (hex.length == 1) {
-      hex = "0" + hex;
-    }
-    result += hex;
-  }
-  return result;
-}
-
-/**
- * Calculates the hash of a file.
- *
- * @param  hashFunction
- *         The type of hash function to use, must be supported by nsICryptoHash.
- * @param  path
- *         The path of the file to hash.
- * @return a promise that resolves to hash of the file or rejects with a JS
- *         exception in case of error.
- */
-let computeHash = Task.async(function*(hashFunction, path) {
-  let file = yield OS.File.open(path, { existing: true, read: true });
-  try {
-    let hasher = Cc["@mozilla.org/security/hash;1"].
-                 createInstance(Ci.nsICryptoHash);
-    hasher.initWithString(hashFunction);
-
-    let bytes;
-    do {
-      bytes = yield file.read(HASH_CHUNK_SIZE);
-      hasher.update(bytes, bytes.length);
-    } while (bytes.length == HASH_CHUNK_SIZE);
-
-    return binaryToHex(hasher.finish(false));
-  }
-  finally {
-    yield file.close();
-  }
-});
-
-/**
- * Verifies that a downloaded file matches what was expected.
- *
- * @param  properties
- *         The properties to check, `size` and `hashFunction` with `hashValue`
- *         are supported. Any properties missing won't be checked.
- * @param  path
- *         The path of the file to check.
- * @return a promise that resolves if the file matched or rejects with a JS
- *         exception in case of error.
- */
-let verifyFile = Task.async(function*(properties, path) {
-  if (properties.size !== undefined) {
-    let stat = yield OS.File.stat(path);
-    if (stat.size != properties.size) {
-      throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
-    }
-  }
-
-  if (properties.hashFunction !== undefined) {
-    let expectedDigest = properties.hashValue.toLowerCase();
-    let digest = yield computeHash(properties.hashFunction, path);
-    if (digest != expectedDigest) {
-      throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest +  "`.");
-    }
-  }
-});
-
-const ProductAddonChecker = {
-  /**
-   * Downloads a list of add-ons from a URL optionally testing the SSL
-   * certificate for certain attributes.
-   *
-   * @param  url
-   *         The url to download from.
-   * @param  allowNonBuiltIn
-   *         Whether to trust SSL certificates without a built-in CA issuer.
-   * @param  allowedCerts
-   *         The list of certificate attributes to match the SSL certificate
-   *         against or null to skip checks.
-   * @return a promise that resolves to the list of add-ons or rejects with a JS
-   *         exception in case of error.
-   */
-  getProductAddonList: function(url, allowNonBuiltIn = false, allowedCerts = null) {
-    return downloadXML(url, allowNonBuiltIn, allowedCerts).then(parseXML);
-  },
-
-  /**
-   * Downloads an add-on to a local file and checks that it matches the expected
-   * file. The caller is responsible for deleting the temporary file returned.
-   *
-   * @param  addon
-   *         The addon to download.
-   * @return a promise that resolves to the temporary file downloaded or rejects
-   *         with a JS exception in case of error.
-   */
-  downloadAddon: Task.async(function*(addon) {
-    let path = yield downloadFile(addon.URL);
-    try {
-      yield verifyFile(addon, path);
-      return path;
-    }
-    catch (e) {
-      yield OS.File.remove(path);
-      throw e;
-    }
-  })
-}
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -37,20 +37,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserToolboxProcess",
                                   "resource:///modules/devtools/client/framework/ToolboxProcess.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
                                   "resource://gre/modules/devtools/shared/Console.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ProductAddonChecker",
-                                  "resource://gre/modules/addons/ProductAddonChecker.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "Blocklist",
                                    "@mozilla.org/extensions/blocklist;1",
                                    Ci.nsIBlocklistService);
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "ChromeRegistry",
                                    "@mozilla.org/chrome/chrome-registry;1",
                                    "nsIChromeRegistry");
@@ -98,17 +94,16 @@ const PREF_XPI_PERMISSIONS_BRANCH     = 
 const PREF_XPI_UNPACK                 = "extensions.alwaysUnpack";
 const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
 const PREF_INSTALL_REQUIRESECUREORIGIN = "extensions.install.requireSecureOrigin";
 const PREF_INSTALL_DISTRO_ADDONS      = "extensions.installDistroAddons";
 const PREF_BRANCH_INSTALLED_ADDON     = "extensions.installedDistroAddon.";
 const PREF_SHOWN_SELECTION_UI         = "extensions.shownSelectionUI";
 const PREF_INTERPOSITION_ENABLED      = "extensions.interposition.enabled";
 const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
-const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
 
 const PREF_EM_MIN_COMPAT_APP_VERSION      = "extensions.minCompatibleAppVersion";
 const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
 
 const PREF_CHECKCOMAT_THEMEOVERRIDE   = "extensions.checkCompatibility.temporaryThemeOverride_minAppVersion";
 
 const PREF_EM_HOTFIX_ID               = "extensions.hotfix.id";
 const PREF_EM_CERT_CHECKATTRIBUTES    = "extensions.hotfix.cert.checkAttributes";
@@ -310,35 +305,16 @@ LAZY_OBJECTS.forEach(name => {
       let objs = loadLazyObjects();
       return objs[name];
     },
     configurable: true
   });
 });
 
 
-// Behaves like Promise.all except waits for all promises to resolve/reject
-// before resolving/rejecting itself
-function waitForAllPromises(promises) {
-  return new Promise((resolve, reject) => {
-    let shouldReject = false;
-    let rejectValue = null;
-
-    let newPromises = [
-      for (p of promises)
-        p.catch(value => {
-          shouldReject = true;
-          rejectValue = value;
-        })
-    ]
-    Promise.all(newPromises)
-           .then((results) => shouldReject ? reject(rejectValue) : resolve(results));
-  });
-}
-
 function findMatchingStaticBlocklistItem(aAddon) {
   for (let item of STATIC_BLOCKLIST_PATTERNS) {
     if ("creator" in item && typeof item.creator == "string") {
       if ((aAddon.defaultLocale && aAddon.defaultLocale.creator == item.creator) ||
           (aAddon.selectedLocale && aAddon.selectedLocale.creator == item.creator)) {
         return item;
       }
     }
@@ -2778,101 +2754,16 @@ this.XPIProvider = {
              getService(Ci.nsIWindowWatcher);
     ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, variant);
 
     // Ensure any changes to the add-ons list are flushed to disk
     Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS,
                                !XPIDatabase.writeAddonsList());
   },
 
-  updateSystemAddons: Task.async(function XPI_updateSystemAddons() {
-    // Download the list of system add-ons
-    let url = Preferences.get(PREF_SYSTEM_ADDON_UPDATE_URL, null);
-    if (!url)
-      return;
-
-    url = UpdateUtils.formatUpdateURL(url);
-
-    logger.info(`Starting system add-on update check from ${url}.`);
-    let addonList = yield ProductAddonChecker.getProductAddonList(url);
-
-    // If there was no list then do nothing.
-    if (!addonList) {
-      logger.info("No system add-ons list was returned.");
-      return;
-    }
-
-    addonList = [for (spec of addonList) { spec, path: null, addon: null }];
-
-    // Bug 1204159: If this matches the current set in the profile or app locations
-    // then just switch to those
-
-    let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
-
-    // Download all the add-ons
-    // Bug 1204158: If we already have some of these locally then just use those
-    let downloadAddon = Task.async(function*(item) {
-      try {
-        item.path = yield ProductAddonChecker.downloadAddon(item.spec);
-        item.addon = yield loadManifestFromFile(nsIFile(item.path), systemAddonLocation);
-      }
-      catch (e) {
-        logger.error(`Failed to download system add-on ${item.spec.id}`, e);
-      }
-    });
-    yield Promise.all([for (item of addonList) downloadAddon(item)]);
-
-    // The download promises all resolve regardless, now check if they all
-    // succeeded
-    let validateAddon = (item) => {
-      if (item.spec.id != item.addon.id) {
-        logger.warn(`Downloaded system add-on expected to be ${item.spec.id} but was ${item.addon.id}.`);
-        return false;
-      }
-
-      if (item.spec.version != item.addon.version) {
-        logger.warn(`Expected system add-on ${item.spec.id} to be version ${item.version} but was ${item.addon.version}.`);
-        return false;
-      }
-
-      if (!systemAddonLocation.isValidAddon(item.addon))
-        return false;
-
-      return true;
-    }
-
-    try {
-      if (!addonList.every(item => item.path && item.addon && validateAddon(item))) {
-        throw new Error("Rejecting updated system add-on set that either could not " +
-                        "be downloaded or contained unusable add-ons.");
-      }
-
-      // Install into the install location
-      logger.info("Installing new system add-on set");
-      yield systemAddonLocation.installAddonSet([for (item of addonList) item.addon]);
-
-      // Bug 1204156: Switch to the new system add-ons without requiring a restart
-    }
-    finally {
-      // Delete the temporary files
-      logger.info("Deleting temporary files");
-      for (let item of addonList) {
-        // If this item downloaded delete the temporary file.
-        if (item.path) {
-          try {
-            yield OS.File.remove(item.path);
-          }
-          catch (e) {
-            logger.warn(`Failed to remove temporary file ${item.path}.`, e);
-          }
-        }
-      }
-    }
-  }),
-
   /**
    * Verifies that all installed add-ons are still correctly signed.
    */
   verifySignatures: function XPI_verifySignatures() {
     XPIDatabase.getAddonList(a => true, (addons) => {
       Task.spawn(function*() {
         let changes = {
           enabled: [],
@@ -7460,120 +7351,50 @@ Object.assign(SystemAddonInstallLocation
 
   /**
    * Tests whether updated system add-ons are expected.
    */
   isActive: function() {
     return this._directory != null;
   },
 
-  isValidAddon: function(aAddon) {
-    if (aAddon.appDisabled) {
-      logger.warn(`System add-on ${aAddon.id} isn't compatible with the application.`);
-      return false;
-    }
-
-    if (aAddon.unpack) {
-      logger.warn(`System add-on ${aAddon.id} isn't a packed add-on.`);
-      return false;
-    }
-
-    if (!aAddon.bootstrap) {
-      logger.warn(`System add-on ${aAddon.id} isn't restartless.`);
-      return false;
-    }
-
-    return true;
-  },
-
   /**
    * Tests whether the loaded add-on information matches what is expected.
    */
   isValid: function(aAddons) {
     for (let id of Object.keys(this._addonSet.addons)) {
       if (!aAddons.has(id)) {
-        logger.warn(`Expected add-on ${id} is missing from the system add-on location.`);
+        logger.warn("Expected add-on " + id + " is missing from the system add-on location.");
         return false;
       }
 
       let addon = aAddons.get(id);
-      if (addon.version != this._addonSet.addons[id].version) {
-        logger.warn(`Expected system add-on ${id} to be version ${this._addonSet.addons[id].version} but was ${addon.version}.`);
+      if (addon.appDisabled) {
+        logger.warn("System add-on " + id + " isn't compatible with the application.");
+        return false;
+      }
+
+      if (addon.unpack) {
+        logger.warn("System add-on " + id + " isn't a packed add-on.");
         return false;
       }
 
-      if (!this.isValidAddon(addon))
+      if (!addon.bootstrap) {
+        logger.warn("System add-on " + id + " isn't restartless.");
         return false;
+      }
+
+      if (addon.version != this._addonSet.addons[id].version) {
+        logger.warn("System add-on " + id + " wasn't the correct version.");
+        return false;
+      }
     }
 
     return true;
   },
-
-  /**
-   * Installs a new set of system add-ons into the location and updates the
-   * add-on set in prefs. We wait to switch state until a restart.
-   */
-  installAddonSet: Task.async(function(aAddons) {
-    // Make sure the base dir exists
-    yield OS.File.makeDir(this._baseDir.path, { ignoreExisting: true });
-
-    let newDir = this._baseDir.clone();
-
-    let uuidGen = Cc["@mozilla.org/uuid-generator;1"].
-                  getService(Ci.nsIUUIDGenerator);
-    newDir.append("blank");
-
-    while (true) {
-      newDir.leafName = uuidGen.generateUUID().toString();
-
-      try {
-        yield OS.File.makeDir(newDir.path, { ignoreExisting: false });
-        break;
-      }
-      catch (e) {
-        // Directory already exists, pick another
-      }
-    }
-
-    let copyAddon = Task.async(function*(addon) {
-      let target = OS.Path.join(newDir.path, addon.id + ".xpi");
-      logger.info(`Copying ${addon.id} from ${addon._sourceBundle.path} to ${target}.`);
-      try {
-        yield OS.File.copy(addon._sourceBundle.path, target);
-      }
-      catch (e) {
-        logger.error(`Failed to copy ${addon.id} from ${addon._sourceBundle.path} to ${target}.`, e);
-        throw e;
-      }
-      addon._sourceBundle = new nsIFile(target);
-    });
-
-    try {
-      yield waitForAllPromises([for (addon of aAddons) copyAddon(addon)]);
-    }
-    catch (e) {
-      try {
-        yield OS.File.removeDir(newDir.path, { ignorePermissions: true });
-      }
-      catch (e) {
-        logger.warn(`Failed to remove new system add-on directory ${newDir.path}.`, e);
-      }
-      throw e;
-    }
-
-    // All add-ons in position, create the new state and store it in prefs
-    let state = { schema: 1, directory: newDir.leafName, addons: {} };
-    for (let addon of aAddons) {
-      state.addons[addon.id] = {
-        version: addon.version
-      }
-    }
-
-    this._saveAddonSet(state);
-  }),
 });
 
 #ifdef XP_WIN
 /**
  * An object that identifies a registry install location for add-ons. The location
  * consists of a registry key which contains string values mapping ID to the
  * path where an add-on is installed
  *
--- a/toolkit/mozapps/extensions/internal/moz.build
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -7,17 +7,16 @@
 EXTRA_JS_MODULES.addons += [
     'AddonLogging.jsm',
     'AddonRepository.jsm',
     'AddonRepository_SQLiteMigrator.jsm',
     'AddonUpdateChecker.jsm',
     'Content.js',
     'GMPProvider.jsm',
     'LightweightThemeImageOptimizer.jsm',
-    'ProductAddonChecker.jsm',
     'SpellCheckDictionaryBootstrap.js',
     'WebExtensionBootstrap.js',
 ]
 
 # Don't ship unused providers on Android
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     EXTRA_JS_MODULES.addons += [
         'PluginProvider.jsm',
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -18,18 +18,18 @@ try {
   // process. We're used in the child process (for now), so guard against
   // this.
   Components.utils.import("resource://gre/modules/AddonManager.jsm");
 } catch (e) {
 }
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 const KEY_PROFILEDIR                  = "ProfD";
 const KEY_APPDIR                      = "XCurProcD";
@@ -554,17 +554,17 @@ Blocklist.prototype = {
     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
     // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't).
     if (gApp.version)
       dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
-    dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel);
+    dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
     dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
     dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
     dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-Not an xml file!
\ No newline at end of file
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<foobar></barfoo>
\ No newline at end of file
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/bad2.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<test></test>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/empty.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<updates>
-  <addons></addons>
-</updates>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/good.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<updates>
-  <addons>
-    <addon id="test1" URL="http://example.com/test1.xpi"/>
-    <addon id="test2" URL="http://example.com/test2.xpi" hashFunction="md5" hashValue="djhfgsjdhf"/>
-    <addon id="test3" URL="http://example.com/test3.xpi" version="1.0" size="45"/>
-    <addon id="test4"/>
-    <addon URL="http://example.com/test5.xpi"/>
-  </addons>
-</updates>
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/data/productaddons/missing.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<updates></updates>
deleted file mode 100644
index 51b00475a9641ea9d608874a3ab7679da3a4374b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1.xpi
rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system2_1.xpi
rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/bootstrap.js
@@ -0,0 +1,18 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const ID = "system1@tests.mozilla.org";
+const VERSION = "1.0";
+
+function install(data, reason) {
+}
+
+function startup(data, reason) {
+  Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
+}
+
+function shutdown(data, reason) {
+  Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
+}
+
+function uninstall(data, reason) {
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/install.rdf
@@ -0,0 +1,23 @@
+<?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>system1@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>System Add-on 1</em:name>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>5</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/bootstrap.js
@@ -0,0 +1,18 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const ID = "system2@tests.mozilla.org";
+const VERSION = "1.0";
+
+function install(data, reason) {
+}
+
+function startup(data, reason) {
+  Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
+}
+
+function shutdown(data, reason) {
+  Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
+}
+
+function uninstall(data, reason) {
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/install.rdf
@@ -0,0 +1,23 @@
+<?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>system2@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>System Add-on 2</em:name>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>5</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_2.xpi
rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system3_1.xpi
rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/bootstrap.js
@@ -0,0 +1,18 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const ID = "system1@tests.mozilla.org";
+const VERSION = "2.0";
+
+function install(data, reason) {
+}
+
+function startup(data, reason) {
+  Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
+}
+
+function shutdown(data, reason) {
+  Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
+}
+
+function uninstall(data, reason) {
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/install.rdf
@@ -0,0 +1,23 @@
+<?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>system1@tests.mozilla.org</em:id>
+    <em:version>2.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>System Add-on 1</em:name>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>5</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/bootstrap.js
@@ -0,0 +1,18 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const ID = "system3@tests.mozilla.org";
+const VERSION = "1.0";
+
+function install(data, reason) {
+}
+
+function startup(data, reason) {
+  Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
+}
+
+function shutdown(data, reason) {
+  Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
+}
+
+function uninstall(data, reason) {
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/install.rdf
@@ -0,0 +1,23 @@
+<?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>system3@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <!-- Front End MetaData -->
+    <em:name>System Add-on 3</em:name>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>5</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
rename from toolkit/mozapps/extensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi
rename to toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app3/features/system1@tests.mozilla.org.xpi
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..60bcde626ffd9307c052b3cecc45812f716c5c0d
GIT binary patch
literal 4825
zc$|$`Wl$VQw;kMJ0t5-}FgOgs5_GUZCqRM|f;$ZE?g4@f?gR)PJm?_7EjR=Z!4o75
zL4v&OR=wKy?c1&Tx~qTOuI|%y&%J%C&ec-Iz$6C%0Js3S|05-!9rx-TApl?s1OWcn
zRaelF<yFy8<ac!TwRLbX<Ab}It-i1p%eh*3+%M4fn!-{b3_^YOnjXRdO??>-VIJay
zDLTtiF+pbE)j?BjU@S!ty(#AK@VR0`W71BxJJ$B>)d1;^{I$c*n){aBK#kT3airUk
z8(r{6mxtJt_%npdg;=oQ24zA@G}173D$HIOh6`1z03GI-h6c9Qy$-r%l1&?c^ok}J
zvwSEtSoWm=;oIZrv?@%S`)PC-0}WP7pci_Nycw>*8tx$8`+NWhQ!aha`gqS~xESj0
z#?+?oyGWMnst0gmE>Xn2;nJO-+ZA+SSwl5S!8QczTRfV*nzsd+Klm&elDUWh{9XZM
zb3Cq8@wV>d@ys2$?0nAHr<ROo_oGB%McKR>=)hmx5V7cp_1h?JFT$yQ<IAW!62B7Z
z>Z%wGKr(`Y18*o=B_<|j>thJ_pw<Bu^bRd#qg{-61C_X;d8;-)9I>b7^3&zs`hgyx
zluRb1V2;gK!cXtB<XOfdS%)iXaPF50ZN`ravAf11NU$!#l{0wPuSTtGHDL8AuErNA
zJ*HP;5G&4`?srHeAUcOo4t*wt?!F8?rJJ!{<75TmRK9mjBd6jM=)8Uv)0%kKB);HF
zC^adbWld~r$h&_IA1Z;K=Ns}zBwj;|%AR<*U$>3|eOJJA6c~A}L<W!F$0s-Ibujv5
zEZ5E4E!k;AwEIHx`N40(ymx&nQa`1Xft&ojsd(CaMk46ONgK|mkSsxEwi8JkEWuSy
zOYnW@e1$4u{jG7!_iubNyk~*twz1IRs?t>4d~WacR?F{C790fgi|d5x3`=cytW6YE
z9f)NSRAiB1<R)jh`j-B_hV(HD{3D{qve)b8wVxt+x+Wdf8%>z}4hQLLgHA>`gY0Ks
zTwQIwdg$!hj5g(*zjsoeGmEr?*$$?<KNdmEYHPn(_BOsy%W!jA`}GrOXo@Voi4aPs
zQ)_Xq;VEp}<ayX;ApJaaQPD(#e<ZD)@7LRQv=ZWg&L>xDw>b$pvp<Tw?xgySzb34A
zIaS1i>C9lxpJy#V*$cG^{G=Y9aRlp9Bl8MIVEk?rmiz5buIklkPI1b2#IyNu2XP?-
zTDCQE&1_?89SPrT19IDr+=jg2=Y<;&bdB_qs8duAb)lQdY7E~8IMt7=?TaWlR(L%H
zEB6`2d?XN-&b3J)f}Jtl<v#ql8Z3wY<&*Xz%GLbXitHBox;lp&kZM>5w3>uitM)s)
zP)X_`oeS~Ss*!wlfm4ctlnRMp^XN<5XhKvibwmz05}B*`$xzZje2SDm>T+Z`ZXz#w
z^rTz2aiU6K7vt;mQ{&AwuBS{(;=0jqsbljq%9iXBX%2QqkLQV=UOP-zR$d9+&{bla
zTKLFBm#4Ef5oD+NiErF_qmOnJM^vhMd1GFu#X-%?1a;<XgJ^)T`{WGrnRRV3`H$>P
z1V71JD&kz#arYd^;U4{Lq<Qs1l{_M2u}#D>FeIDN0sG;^VL8&`12UOf99|=|lx|M*
z11q<{f5X`}ejL7P6cQB_M~JNY)tt6a|7IIBbU$BB?`-bMK5B(pW%P(>qf=OQ@j=i~
z7Ri@WjJa?@eFfd$`q_haWaSll^y81Y;R`itJP9W&TQ;R3+f|>V2r}<Y9xM$hVe5n2
zm2X+M)4ccHpHr}}2AV#Zn@<?2S|1H33PamqMbfBa%@XubMmlWJKGzw0m13^jx%o3;
z<pI*W@^R563&FwEn-`py+zWK)>HdIrZHy<UQnfbc#(@)}hqpw1(9_C3zfodMj*_T+
zx{MVDL9>chWxvx;tR8rU+9*Mj^Qzio_Gfv*lzhEU-#9L%mnz<?v{MW8I^m%ZK2`}!
z?|~U(I~9D<{+OmwIV^lZ9lp(R1L-04uU2KJHtZaF?jCM4&*9_l7M)6@co@QM8}V8u
z$<9!16`pEx%|<x9y9Q5#&JP>}EU&#4TS;X?=>V*IV<bXR0&ha@*Vw>=YV;>T(61|i
z1rla!R~FKz3FS0rqH-<n%y+WgU&L$$T~!3}>det8BtuApy6>|T&X9uMZt0gUBD>{W
zAosrFnRy=q#f!yNGE5|Ebw{)M!V|)8zQfZ~K>G$3*pP@A%<vqQ<}^jriG6$pUahy<
zJVMi99k&^4h7Q~mZnIoj2zX>P279`(W!|8*Qr3hra^6%I<VH&cCoo0YWWisHdM7<c
zEx@rmii;X<z(|*--1(j|x2CdAkUL`<@5tGuJET^rV+0$49h=HaW!xCB=io^C@<OfJ
ztcQX*X@7~IBC*;W+7)1Y*0|N8P-M0i3DA7|?xmRhY34=tvhY@+B(J$ac%phJlxSVb
zCcy}w+;v|sh4qj$Ev8{=V>67C{8P5f7e{GC@oj~S6}i`K*Fm=L=}+u&EOVu`1^3b{
zPqNU%Z%UC}%*S9i;`h|yEGH{o7Y7p;K4aEAosDnqUdNcXsdMq{b12vCN6PFXlyx1W
z+KFx{cBWOm-{o4FGu6`qmAq)?gu6~qy#}P?x2{XKdBGon;@Mw>8b)>viLz7{1L!bb
z(Agd@z(sEctCyA^(x1GICJi&HHBkOK+MZz~z<-U%ddA}MGB{0%tw^2nSt0MqiNZWa
ztwQm<nPwo#b=OjY<PYtn3nJ4FDksh=+x<7^5UmiD+?TE$T))=r$0wA51<qUFt~c_9
z%zIdLl36{1Hint@Kl>J6n`65o<L6jnWZ!)zp2UqMr5vp8TB!&@lYi;GPfNN-D}%0r
z4jxn=jtEZ100Q@L3nXPxgQi-0r&N7_G_tXvP+I_qsziVgqM?Bn;?E$$C&5XOI^4G&
z%H=G<AGniAR>hdA3XMld(&F-oB^4L;=*zhH0%sc1RZHpnwI`(H5dFx4B&>PnVSaiX
zoZYpnpi)dyT+Hac4bfh(tXVsVky9r)VsfIdPk|!tHh(6%srjQpyLfCHFy~IeVST}L
z_@#OGr#+BHFUNb9osv_F>5K{enMB@>%K|vX^<uH%e0%2#@w83fK3a5Sk>zKfiHFFH
zbp-x=w^EgHte0+n0C047aU(T#?nlTwMA`^B%}-szR|XuJh-iPi4vkptvl&7m_t-K?
zU68&he~g9vl1br_;1P+}2tnDwFR36HdOYYb)|f=n9pw!CB6mc+FXHDqgc6pCmO}TX
z#x}YN6ZSP76RK;8?O^9_u9+WM(`2x*@_gLwJz1K*(+v-=K38dVbYNj!2UZXyT;rJ0
zwhF#xUDfzvuqAnlf_>TDR0~<aLu~n6-AqN{fC-b~>9dt!-p7XMG!a2jB^vTtb!Lf`
zeFh>2Al4wWh$AHvjz#iBDm=c)^iSPg(?T7w(B(#LGU^!pXC-)7grJIYG=y};Q@6n6
zFt@<AO|oBhYuoeo#t(F7o%b`Y7)qh76?(>&--W6e8{)d+EqP7~uX;a-#^{gmDUXeM
zdrpSr!^03|dBPscUsX(Ma0kDyxdX&&PTh@8UH8NEDv54~JsnFXo~x;QGI>2&`R0@2
zumCBaCB8XTmdUVVx7b`c>Twslk`%9%q~5dA#8T{^5^e9}&MbqVH&Y0BTLc_7rNt*a
zXZSTUG0^^$$vlrXvqNUlyBeK`VTARHsi5>hLr$|TT1DU~gik`Ymi8E*rZ<T(J<*xE
zc&iu(_vqGHpFN1qQo8m-l|5itQwg}To+uG~Ru)dHbe|Z9<Ao)2pDO1%xN^GpXf#u8
zXHa2Cq|z5Vv2l;~{9|5FH6JIQ8ihyQu-2Rv)Sv$cVx;0%N`f2J=Bjh3LAfc{XhQJY
zr{#o!E=DZOZ{4MOgrYuojj$F&bZBEMiL*G>MV+Z3N%TdbR!&A*rW~W93JE3#cv-HS
z#M1@V#OFR7K2~rw;o;*6nB5o4U8&fx=a0{NL<ef{A9RH}F^$EG6Cd0*<-0>D<dsbQ
zH<i1n_qJz2ZX?0Vd?4@Q@sE{IeEpxn8lGlpI^VY_>se)3)|7U0`@Q8kiymh-rRWcN
z$2!v651^dzY#%lT6<a!QR}U7DTy`_zz)oz!JSF)+Ar+_Un#OT9x-zR(yr{|z?}uzR
zoX7fxOC7a!0mw)m&wv(+ZCdbb1JU4TChKFbLU?<b4@FYI(p#3sHky{P!lviK<$4B5
zqL$e0EAs-7&jZEuigXiEgd*w3$=IY9SMR9Ij{8#mc`P&XM&UYbdD+i=oF=%+KH0kr
zH^#N7q+!*{g7wQ~>?Oy4)wM^An+IIA4tZor2^}>q_K*!>J`>{!UtIWk@^C9z)T{c{
z@$A%n8)G8wyQ=SMZM+zd?+rp#ca*obM5ebd5~}H6i^tOWgD(5Ov=k)7BuCegk9a*I
zq#TSG&&1Fcax?j8pPjFpQ*SrB57S)zxB#K}IwR5{ifTHju2uys<n&9j?I?eGWQ)Kg
z)c+~c0ycX{#4Tuqr<NIh577EkFQNa|OK&U)l$zWW`~=Ye01tEkfB--Ruyt~CH*;{{
zgIieX>R<sdx-~|0J-i73XqZ2LpZZg1MI?_q^$GyPj|1^VbK>P~NFYq)G4R6@gs&+n
zM%oS8k}BCXN+c2!h{!UuLN4XTJmZn5-fM9&F%Z^d-vaBWhLbZ?iV%@)?GDq|4U0Q3
zd#-2<&VRqOc$|F--qIg1>a@JG0ZLfia5AA#5OY3M?u%uHa`KN@`dLzLg_=h&(g~PB
zRd{WVgTTjv_jNcq95~iL;<(cXSUCH^RILx`!w%icffDH+`-$`sd0nqEE|*oYq%ko3
zZ4DxeT<4J_-2(=wy#lT&I*3ILskpi3>#3#&?EUQW{dO5WG<3^vZ3Dp++t#lY(mlrI
zDs7D@a4a@}D;gX1wy9NsDAo&xw+oS7#@bh%S_k>Po${(@a~BES@h&8C3y1vHCm>Yo
zrTly4J6QOWPgo@Px+;Nae=#2AN}x1d$;9^cH;wOq^GW!d@mJ2y?r!dIGZ#KPw?8T8
zwfujSBi?ALV#pHqAOGU&&1r5N1Fi9dYWeMz#46MZO_Y8g`evab#0?7cvX<?aRzAH<
zW;pJ&kTurCONkwSRJ5oCws)k85b}HCm_=tk1IuQCHptS4D%FupXAXB%JYj?B8Fy1t
z`H=P>eKmr`nkr2)DO$&PS=~*n^3{q~j-KN(EEfyaG>iKXr81^=#??9Hwowa66oNz2
z56%g+apza=Ujd~2&R-kWg|;n9c7ci>UqC=3H4J|G7&CQ8<gY#YJ-XY`^kdsI17^9s
z0#sM69rp$463!pct>fMaUn-es1l@lj()te_U*(eGJn-8hNBg^uH*>PJvUGFjbF_*d
zA5RDspo-WGs4Ws@#86a>3df3-o+{Uag&x`2*egLfZtY&1+$yuTxojV??yA%GK4KI5
zpwYw2IRg<p@efZ^JHxHSrmeJ}e2!iMZPtILYL1Y!ixyCk&zVdHtSar^C%(_uO}A{(
z`?@@SU4brP@;#0y-h4J~@&Luv+se9S^`f`C_5*>1Z9AAo=#exkOprLo61hgwF>al8
z3~rwz8?MHajx_bvGUl)Kmlltz)Lvs)h2VE8c=935+RDh7hjQ;R{?l(-e5ri9-<Hqs
zMfOkq3pXo*3AIU89xh(!9FZz7Xku)#;hDg!@Wu}h!8wm%HBDZ=sdM$QQ8f&qDsp@l
zIsZ6lK<so13#cI_r+(C!2X#K`=-3+il$J;kp_G=X&{js3mi8ip-21vLvh2kUWt6xQ
z;;a6L&u~|?-=zW#ogCxen=Ai2g3$o~(rWnwfga=E%grC-kIQdE1c(6tP4W4^fdBNH
z{sJr_2K-ll>+eqfJ@fu@a){)g^YHHoe~;l`2;IPcjHs3>7WN+puzpXQ-_II8_`~`i
DmuZwQ
deleted file mode 100644
index 7af871d36dec992e405752bc9db49e6dae4ea085..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5c8570dcc4086cb1a3e9270a2f8ef64e28899c02..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5b50b8556dcf426f67b134cfc084672e08bb5b53..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 91a7d9ffc1e848636ef732a8864a6d4cb0ee8869..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index a11a8eb494f1cb2e25f1016a71b43bc7f9c936cd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f4e07bf300d17fb2306c1dc189b5fafea56a952a..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_ProductAddonChecker.js
+++ /dev/null
@@ -1,264 +0,0 @@
-"use strict";
-
-Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
-Components.utils.import("resource://testing-common/httpd.js");
-Components.utils.import("resource://gre/modules/osfile.jsm");
-
-const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", AM_Ci.nsIFile, "initWithPath");
-
-let testserver = new HttpServer();
-testserver.registerDirectory("/data/", do_get_file("data/productaddons"));
-testserver.start();
-let root = testserver.identity.primaryScheme + "://" +
-           testserver.identity.primaryHost + ":" +
-           testserver.identity.primaryPort + "/data/"
-
-/**
- * Compares binary data of 2 arrays and returns true if they are the same
- *
- * @param arr1 The first array to compare
- * @param arr2 The second array to compare
-*/
-function compareBinaryData(arr1, arr2) {
-  do_check_eq(arr1.length, arr2.length);
-  for (let i = 0; i < arr1.length; i++) {
-    if (arr1[i] != arr2[i]) {
-      do_print("Data differs at index " + i +
-               ", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
-      return false;
-    }
-  }
-  return true;
-}
-
-/**
- * Reads a file's data and returns it
- *
- * @param file The file to read the data from
- * @return array of bytes for the data in the file.
-*/
-function getBinaryFileData(file) {
-  let fileStream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
-                   createInstance(AM_Ci.nsIFileInputStream);
-  // Open as RD_ONLY with default permissions.
-  fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
-
-  let stream = AM_Cc["@mozilla.org/binaryinputstream;1"].
-               createInstance(AM_Ci.nsIBinaryInputStream);
-  stream.setInputStream(fileStream);
-  let bytes = stream.readByteArray(stream.available());
-  fileStream.close();
-  return bytes;
-}
-
-/**
- * Compares binary data of 2 files and returns true if they are the same
- *
- * @param file1 The first file to compare
- * @param file2 The second file to compare
-*/
-function compareFiles(file1, file2) {
-  return compareBinaryData(getBinaryFileData(file1), getBinaryFileData(file2));
-}
-
-add_task(function* test_404() {
-  try {
-    let addons = yield ProductAddonChecker.getProductAddonList(root + "404.xml");
-    do_throw("Should not have returned anything");
-  }
-  catch (e) {
-    do_check_true(true, "Expected to throw for a missing update file");
-  }
-});
-
-add_task(function* test_not_xml() {
-  try {
-    let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.txt");
-    do_throw("Should not have returned anything");
-  }
-  catch (e) {
-    do_check_true(true, "Expected to throw for a non XML result");
-  }
-});
-
-add_task(function* test_invalid_xml() {
-  try {
-    let addons = yield ProductAddonChecker.getProductAddonList(root + "bad.xml");
-    do_throw("Should not have returned anything");
-  }
-  catch (e) {
-    do_check_true(true, "Expected to throw for invalid XML");
-  }
-});
-
-add_task(function* test_wrong_xml() {
-  try {
-    let addons = yield ProductAddonChecker.getProductAddonList(root + "bad2.xml");
-    do_throw("Should not have returned anything");
-  }
-  catch (e) {
-    do_check_true(true, "Expected to throw for a missing <updates> tag");
-  }
-});
-
-add_task(function* test_missing() {
-  let addons = yield ProductAddonChecker.getProductAddonList(root + "missing.xml");
-  do_check_eq(addons, null);
-});
-
-add_task(function* test_empty() {
-  let addons = yield ProductAddonChecker.getProductAddonList(root + "empty.xml");
-  do_check_true(Array.isArray(addons));
-  do_check_eq(addons.length, 0);
-});
-
-add_task(function* test_good_xml() {
-  let addons = yield ProductAddonChecker.getProductAddonList(root + "good.xml");
-  do_check_true(Array.isArray(addons));
-
-  // There are three valid entries in the XML
-  do_check_eq(addons.length, 5);
-
-  let addon = addons[0];
-  do_check_eq(addon.id, "test1");
-  do_check_eq(addon.URL, "http://example.com/test1.xpi");
-  do_check_eq(addon.hashFunction, undefined);
-  do_check_eq(addon.hashValue, undefined);
-  do_check_eq(addon.version, undefined);
-  do_check_eq(addon.size, undefined);
-
-  addon = addons[1];
-  do_check_eq(addon.id, "test2");
-  do_check_eq(addon.URL, "http://example.com/test2.xpi");
-  do_check_eq(addon.hashFunction, "md5");
-  do_check_eq(addon.hashValue, "djhfgsjdhf");
-  do_check_eq(addon.version, undefined);
-  do_check_eq(addon.size, undefined);
-
-  addon = addons[2];
-  do_check_eq(addon.id, "test3");
-  do_check_eq(addon.URL, "http://example.com/test3.xpi");
-  do_check_eq(addon.hashFunction, undefined);
-  do_check_eq(addon.hashValue, undefined);
-  do_check_eq(addon.version, "1.0");
-  do_check_eq(addon.size, 45);
-
-  addon = addons[3];
-  do_check_eq(addon.id, "test4");
-  do_check_eq(addon.URL, undefined);
-  do_check_eq(addon.hashFunction, undefined);
-  do_check_eq(addon.hashValue, undefined);
-  do_check_eq(addon.version, undefined);
-  do_check_eq(addon.size, undefined);
-
-  addon = addons[4];
-  do_check_eq(addon.id, undefined);
-  do_check_eq(addon.URL, "http://example.com/test5.xpi");
-  do_check_eq(addon.hashFunction, undefined);
-  do_check_eq(addon.hashValue, undefined);
-  do_check_eq(addon.version, undefined);
-  do_check_eq(addon.size, undefined);
-});
-
-add_task(function* test_download_nourl() {
-  try {
-    let path = yield ProductAddonChecker.downloadAddon({});
-
-    yield OS.File.remove(path);
-    do_throw("Should not have downloaded a file with a missing url");
-  }
-  catch (e) {
-    do_check_true(true, "Should have thrown when downloading a file with a missing url.");
-  }
-});
-
-add_task(function* test_download_missing() {
-  try {
-    let path = yield ProductAddonChecker.downloadAddon({
-      URL: root + "nofile.xpi",
-    });
-
-    yield OS.File.remove(path);
-    do_throw("Should not have downloaded a missing file");
-  }
-  catch (e) {
-    do_check_true(true, "Should have thrown when downloading a missing file.");
-  }
-});
-
-add_task(function* test_download_noverify() {
-  let path = yield ProductAddonChecker.downloadAddon({
-    URL: root + "unsigned.xpi",
-  });
-
-  let stat = yield OS.File.stat(path);
-  do_check_false(stat.isDir);
-  do_check_eq(stat.size, 452)
-
-  do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
-
-  yield OS.File.remove(path);
-});
-
-add_task(function* test_download_badsize() {
-  try {
-    let path = yield ProductAddonChecker.downloadAddon({
-      URL: root + "unsigned.xpi",
-      size: 400,
-    });
-
-    yield OS.File.remove(path);
-    do_throw("Should not have downloaded a file with a bad size");
-  }
-  catch (e) {
-    do_check_true(true, "Should have thrown when downloading a file with a bad size.");
-  }
-});
-
-add_task(function* test_download_badhashfn() {
-  try {
-    let path = yield ProductAddonChecker.downloadAddon({
-      URL: root + "unsigned.xpi",
-      hashFunction: "sha2567",
-      hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
-    });
-
-    yield OS.File.remove(path);
-    do_throw("Should not have downloaded a file with a bad hash function");
-  }
-  catch (e) {
-    do_check_true(true, "Should have thrown when downloading a file with a bad hash function.");
-  }
-});
-
-add_task(function* test_download_badhash() {
-  try {
-    let path = yield ProductAddonChecker.downloadAddon({
-      URL: root + "unsigned.xpi",
-      hashFunction: "sha256",
-      hashValue: "8b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
-    });
-
-    yield OS.File.remove(path);
-    do_throw("Should not have downloaded a file with a bad hash");
-  }
-  catch (e) {
-    do_check_true(true, "Should have thrown when downloading a file with a bad hash.");
-  }
-});
-
-add_task(function* test_download_works() {
-  let path = yield ProductAddonChecker.downloadAddon({
-    URL: root + "unsigned.xpi",
-    size: 452,
-    hashFunction: "sha256",
-    hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
-  });
-
-  let stat = yield OS.File.stat(path);
-  do_check_false(stat.isDir);
-
-  do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
-
-  yield OS.File.remove(path);
-});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gmpProvider.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 var GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
   () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 
 var gMockAddons = new Map();
@@ -334,17 +333,17 @@ add_task(function* test_pluginRegistrati
     yield promiseRestartManager();
     Assert.equal(addedPaths.indexOf(file.path), -1);
     Assert.deepEqual(removedPaths, [file.path]);
 
     // Setting the ABI to expected ABI should cause registration at startup.
     clearPaths();
     gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
                        TEST_VERSION);
-    gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI);
+    gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), GMPScope.GMPUtils.ABI());
     yield promiseRestartManager();
     Assert.notEqual(addedPaths.indexOf(file.path), -1);
     Assert.deepEqual(removedPaths, []);
 
     // Check that clearing the version doesn't trigger registration.
     clearPaths();
     gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
     Assert.deepEqual(addedPaths, []);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_reset.js
@@ -1,31 +1,19 @@
 // Tests that we reset to the default system add-ons correctly when switching
 // application versions
 const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
 
 // Enable signature checks for these tests
 Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
 
-const featureDir = FileUtils.getDir("ProfD", ["features"]);
-
-// Build the test sets
-let dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1", "features"], true);
-do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+const featureDir = gProfD.clone();
+featureDir.append("features");
 
-dir = FileUtils.getDir("ProfD", ["sysfeatures", "app2", "features"], true);
-do_get_file("data/system_addons/system1_2.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-dir = FileUtils.getDir("ProfD", ["sysfeatures", "app3", "features"], true);
-do_get_file("data/system_addons/system1_1_badcert.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true);
+const distroDir = do_get_file("data/system_addons/app0");
 registerDirectory("XREAppDist", distroDir);
 
 createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
 
 function makeUUID() {
   let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
                 getService(AM_Ci.nsIUUIDGenerator);
   return uuidGen.generateUUID().toString();
@@ -143,20 +131,20 @@ add_task(function* test_downgrade() {
 // Fake a mid-cycle install
 add_task(function* test_updated() {
   // Create a random dir to install into
   let dirname = makeUUID();
   FileUtils.getDir("ProfD", ["features", dirname], true);
   featureDir.append(dirname);
 
   // Copy in the system add-ons
-  let file = do_get_file("data/system_addons/system2_1.xpi");
-  file.copyTo(featureDir, "system2@tests.mozilla.org.xpi");
-  file = do_get_file("data/system_addons/system3_1.xpi");
-  file.copyTo(featureDir, "system3@tests.mozilla.org.xpi");
+  let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
+  file.copyTo(featureDir, file.leafName);
+  file = do_get_file("data/system_addons/app2/features/system3@tests.mozilla.org.xpi");
+  file.copyTo(featureDir, file.leafName);
 
   // Inject it into the system set
   let addonSet = {
     schema: 1,
     directory: featureDir.leafName,
     addons: {
       "system2@tests.mozilla.org": {
         version: "1.0"
@@ -173,18 +161,18 @@ add_task(function* test_updated() {
   yield check_installed(true, null, "1.0", "1.0");
 
   yield promiseShutdownManager();
 });
 
 // An additional add-on in the directory should be ignored
 add_task(function* test_skips_additional() {
   // Copy in the system add-ons
-  let file = do_get_file("data/system_addons/system1_1.xpi");
-  file.copyTo(featureDir, "system1@tests.mozilla.org.xpi");
+  let file = do_get_file("data/system_addons/app1/features/system1@tests.mozilla.org.xpi");
+  file.copyTo(featureDir, file.leafName);
 
   startupManager(false);
 
   yield check_installed(true, null, "1.0", "1.0");
 
   yield promiseShutdownManager();
 });
 
@@ -198,18 +186,18 @@ add_task(function* test_revert() {
   // the default set which is system add-ons 1 and 2.
   yield check_installed(false, "1.0", "1.0", null);
 
   yield promiseShutdownManager();
 });
 
 // Putting it back will make the set work again
 add_task(function* test_reuse() {
-  let file = do_get_file("data/system_addons/system2_1.xpi");
-  file.copyTo(featureDir, "system2@tests.mozilla.org.xpi");
+  let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
+  file.copyTo(featureDir, file.leafName);
 
   startupManager(false);
 
   yield check_installed(true, null, "1.0", "1.0");
 
   yield promiseShutdownManager();
 });
 
@@ -221,18 +209,18 @@ add_task(function* test_corrupt_pref() {
 
   yield check_installed(false, "1.0", "1.0", null);
 
   yield promiseShutdownManager();
 });
 
 // An add-on with a bad certificate should cause us to use the default set
 add_task(function* test_bad_profile_cert() {
-  let file = do_get_file("data/system_addons/system1_1_badcert.xpi");
-  file.copyTo(featureDir, "system1@tests.mozilla.org.xpi");
+  let file = do_get_file("data/system_addons/app3/features/system1@tests.mozilla.org.xpi");
+  file.copyTo(featureDir, file.leafName);
 
   // Inject it into the system set
   let addonSet = {
     schema: 1,
     directory: featureDir.leafName,
     addons: {
       "system1@tests.mozilla.org": {
         version: "2.0"
deleted file mode 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/test_system_update.js
+++ /dev/null
@@ -1,469 +0,0 @@
-// Tests that we reset to the default system add-ons correctly when switching
-// application versions
-const PREF_SYSTEM_ADDON_SET           = "extensions.systemAddonSet";
-const PREF_SYSTEM_ADDON_UPDATE_URL    = "extensions.systemAddon.update.url";
-const PREF_XPI_STATE                  = "extensions.xpiState";
-const PREF_APP_UPDATE_ENABLED         = "app.update.enabled";
-
-Components.utils.import("resource://testing-common/httpd.js");
-const { computeHash } = Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
-
-// Enable signature checks for these tests
-//Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
-
-const featureDir = FileUtils.getDir("ProfD", ["features"]);
-
-// Build the test sets
-let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true);
-do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-// Mark these in the past so the startup file scan notices when files have changed properly
-FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
-FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
-
-const prefilledSet = {
-  schema: 1,
-  directory: dir.leafName,
-  addons: {
-    "system2@tests.mozilla.org": {
-      version: "2.0"
-    },
-    "system3@tests.mozilla.org": {
-      version: "2.0"
-    },
-  }
-};
-
-dir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden", "features"], true);
-do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-
-dir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled", "features"], true);
-do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
-do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
-
-const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
-registerDirectory("XREAppDist", distroDir);
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2");
-
-let testserver = new HttpServer();
-testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
-testserver.start();
-let root = testserver.identity.primaryScheme + "://" +
-           testserver.identity.primaryHost + ":" +
-           testserver.identity.primaryPort + "/data/"
-Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
-
-function makeUUID() {
-  let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
-                getService(AM_Ci.nsIUUIDGenerator);
-  return uuidGen.generateUUID().toString();
-}
-
-function* serve_update(xml, perform_update) {
-  testserver.registerPathHandler("/data/update.xml", (request, response) => {
-    response.write(xml);
-  });
-
-  try {
-    yield perform_update();
-  }
-  finally {
-    testserver.registerPathHandler("/data/update.xml", null);
-  }
-}
-
-// Runs an update check making it use the passed in xml string. Uses the direct
-// call to the update function so we get rejections on failure.
-function* install_system_addons(xml) {
-  do_print("Triggering system add-on update check.");
-
-  yield serve_update(xml, function*() {
-    let { XPIProvider } = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
-    yield XPIProvider.updateSystemAddons();
-  });
-}
-
-// Runs a full add-on update check which will in some cases do a system add-on
-// update check. Always succeeds.
-function* update_all_addons(xml) {
-  do_print("Triggering full add-on update check.");
-
-  yield serve_update(xml, function() {
-    return new Promise(resolve => {
-      Services.obs.addObserver(function() {
-        Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
-
-        resolve();
-      }, "addons-background-update-complete", false);
-
-      // Trigger the background update timer handler
-      gInternalManager.notify(null);
-    });
-  });
-}
-
-// Builds an update.xml file for an update check based on the data passed.
-function* build_xml(addons) {
-  let xml = `<?xml version="1.0" encoding="UTF-8"?>\n\n<updates>\n`;
-  if (addons) {
-    xml += `  <addons>\n`;
-    for (let addon of addons) {
-      xml += `    <addon id="${addon.id}" URL="${root + addon.path}" version="${addon.version}"`;
-      if (addon.size)
-        xml += ` size="${addon.size}"`;
-      if (addon.hashFunction)
-        xml += ` hashFunction="${addon.hashFunction}"`;
-      if (addon.hashValue)
-        xml += ` hashValue="${addon.hashValue}"`;
-      xml += `/>\n`;
-    }
-    xml += `  </addons>\n`;
-  }
-  xml += `</updates>\n`;
-
-  return xml;
-}
-
-function* check_installed(inProfile, ...versions) {
-  for (let i = 0; i < versions.length; i++) {
-    let id = "system" + (i + 1) + "@tests.mozilla.org";
-    let addon = yield promiseAddonByID(id);
-
-    if (versions[i]) {
-      // Add-on should be installed
-      do_check_neq(addon, null);
-      do_check_eq(addon.version, versions[i]);
-      do_check_true(addon.isActive);
-      do_check_false(addon.foreignInstall);
-
-      // Verify the add-ons file is in the right place
-      let uri = addon.getResourceURI(null);
-      do_check_true(uri instanceof AM_Ci.nsIFileURL);
-
-      let file = uri.file.parent;
-      if (inProfile) {
-        file = file.parent;
-        do_check_eq(file.leafName, "features");
-        file = file.parent;
-        do_check_eq(file.path, gProfD.path);
-      }
-      else {
-        do_check_eq(file.leafName, "features");
-        file = file.parent;
-        do_check_eq(file.path, distroDir.path);
-      }
-
-      //do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
-
-      // Verify the add-on actually started
-      let installed = Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
-      do_check_eq(installed, versions[i]);
-    }
-    else {
-      if (inProfile) {
-        // Add-on should not be installed
-        do_check_eq(addon, null);
-      }
-      else {
-        // Either add-on should not be installed or it shouldn't be active
-        do_check_true(!addon || !addon.isActive);
-      }
-
-      try {
-        Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
-        do_throw("Expected pref to be missing");
-      }
-      catch (e) {
-      }
-    }
-  }
-}
-
-
-/**
- * Defines the set of initial conditions to run each test against. Each should
- * define the following properties:
- *
- * setup:        A task to setup the profile into the initial state.
- * initialState: The initial expected system add-on state after setup has run.
- */
-const TEST_CONDITIONS = {
-  // Runs tests with no updated or default system add-ons initially installed
-  blank: {
-    setup: function*() {
-      Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
-      distroDir.leafName = "empty";
-    },
-    initialState: [false, null, null, null, null, null],
-  },
-
-  // Runs tests with default system add-ons installed
-  withAppSet: {
-    setup: function*() {
-      Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
-      distroDir.leafName = "prefilled";
-    },
-    initialState: [false, null, "2.0", "2.0", null, null],
-  },
-
-  // Runs tests with updated system add-ons installed
-  withProfileSet: {
-    setup: function*() {
-      Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet));
-      distroDir.leafName = "empty";
-    },
-    initialState: [true, null, "2.0", "2.0", null, null],
-  },
-
-  // Runs tests with both default and updated system add-ons installed
-  withBothSets: {
-    setup: function*() {
-      Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(prefilledSet));
-      distroDir.leafName = "hidden";
-    },
-    initialState: [true, null, "2.0", "2.0", null, null],
-  },
-};
-
-
-/**
- * The tests to run. Each test must define an updateList or test. The following
- * properties are used:
- *
- * updateList: The set of add-ons the server should respond with.
- * test:       A function to run to perform the update check (replaces
- *             updateList)
- * fails:      An optional property, if true the update check is expected to
- *             fail.
- * finalState: An optional property, the expected final state of system add-ons,
- *             if missing the test condition's initialState is used.
- */
-const TESTS = {
-  // Test that an error response does nothing
-  error: {
-    test: function*() {
-      try {
-        yield install_system_addons("foobar");
-        do_throw("Expected to fail the update check");
-      }
-      catch (e) {
-        do_check_true(true, "Expected to fail the update check");
-      }
-    },
-  },
-
-  // Test that a blank response does nothing
-  blank: {
-    updateList: null,
-  },
-
-  // Test that an empty list updates to an empty set of system add-ons
-  empty: {
-    updateList: [],
-    finalState: [true, null, null, null, null, null]
-  },
-
-  // Tests that a new set of system add-ons gets installed
-  newset: {
-    updateList: [
-      { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" },
-      { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi" }
-    ],
-    finalState: [true, null, null, null, "1.0", "1.0"]
-  },
-
-  // Tests that an upgraded set of system add-ons gets installed
-  upgrades: {
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
-    ],
-    finalState: [true, null, "3.0", "3.0", null, null]
-  },
-
-  // Tests that a set of system add-ons, some new, some existing gets installed
-  overlapping: {
-    updateList: [
-      { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_2.xpi" },
-      { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_2.xpi" },
-      { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_3.xpi" },
-      { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
-    ],
-    finalState: [true, "2.0", "2.0", "3.0", "1.0", null]
-  },
-
-  // Specifying an incorrect version should stop us updating anything
-  badVersion: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "4.0", path: "system2_3.xpi" },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
-    ],
-  },
-
-  // Specifying an invalid size should stop us updating anything
-  badSize: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 2 },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
-    ],
-  },
-
-  // Specifying an incorrect hash should stop us updating anything
-  badHash: {
-    fails: true,
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "205a4c49bd513ebd30594e380c19e86bba1f83e2" }
-    ],
-  },
-
-  // Correct sizes and hashes should work
-  checkSizeHash: {
-    updateList: [
-      { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 858 },
-      { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "105a4c49bd513ebd30594e380c19e86bba1f83e2" },
-      { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi", size: 857, hashFunction: "sha1", hashValue: "664e9218be3c9acbb9029e715c1e5d2fbb4ea2cc" }
-    ],
-    finalState: [true, null, "3.0", "3.0", null, "1.0"]
-  },
-}
-
-add_task(function* setup() {
-  // Initialise the profile
-  startupManager();
-  yield promiseShutdownManager();
-})
-
-function* setup_conditions(setup) {
-  do_print("Setting up conditions.");
-  yield setup.setup();
-
-  // Blow away the cache to force a rescan of the filesystem
-  Services.prefs.clearUserPref(PREF_XPI_STATE);
-  startupManager(false);
-
-  // Make sure the initial state is correct
-  do_print("Checking initial state.");
-  yield check_installed(...setup.initialState);
-}
-
-function* verify_state(finalState) {
-  do_print("Checking final state.");
-
-  // Bug 1204156: Currently switching to the new state requires a restart
-  // yield check_installed(...finalState);
-
-  // Check that the new state is active after a restart
-  yield promiseRestartManager();
-  yield check_installed(...finalState);
-}
-
-function* exec_test(setup, test) {
-  yield setup_conditions(setup);
-
-  try {
-    if ("test" in test) {
-      yield test.test();
-    }
-    else {
-      yield install_system_addons(yield build_xml(test.updateList));
-    }
-
-    if (test.fails) {
-      do_throw("Expected this test to fail");
-    }
-  }
-  catch (e) {
-    if (!test.fails) {
-      do_throw(e);
-    }
-  }
-
-  yield verify_state(test.finalState ? test.finalState : setup.initialState);
-
-  yield promiseShutdownManager();
-}
-
-for (let setup of Object.keys(TEST_CONDITIONS)) {
-  for (let test of Object.keys(TESTS)) {
-    add_task(function*() {
-      do_print("Running test " + setup + " " + test);
-
-      yield exec_test(TEST_CONDITIONS[setup], TESTS[test]);
-    });
-  }
-}
-
-// Some custom tests
-
-// Test that the update check is performed as part of the regular add-on update
-// check
-add_task(function* test_addon_update() {
-  yield setup_conditions(TEST_CONDITIONS.blank);
-
-  yield update_all_addons(yield build_xml([
-    { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
-    { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
-  ]));
-
-  yield verify_state([true, null, "2.0", "2.0", null, null]);
-
-  yield promiseShutdownManager();
-});
-
-// Disabling app updates should block system add-on updates
-add_task(function* test_app_update_disabled() {
-  yield setup_conditions(TEST_CONDITIONS.blank);
-
-  Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
-  yield update_all_addons(yield build_xml([
-    { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
-    { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
-  ]));
-  Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
-
-  yield verify_state(TEST_CONDITIONS.blank.initialState);
-
-  yield promiseShutdownManager();
-});
-
-// Tests that a set that matches the hidden default set works
-add_task(function* test_match_default() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
-
-  yield install_system_addons(yield build_xml([
-    { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1.xpi" },
-    { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_1.xpi" }
-  ]));
-
-  // Bug 1204159: This should revert to the default set instead of installing
-  // new versions into the updated set.
-  //yield verify_state([false, "1.0", "1.0", null, null, null]);
-  yield verify_state([true, "1.0", "1.0", null, null, null]);
-
-  yield promiseShutdownManager();
-});
-
-// Tests that a set that matches the current set works
-add_task(function* test_match_current() {
-  yield setup_conditions(TEST_CONDITIONS.withBothSets);
-
-  yield install_system_addons(yield build_xml([
-    { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
-    { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
-  ]));
-
-  // Bug 1204159: This should remain with the current set instead of creating
-  // a new copy
-  //let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
-  //do_check_eq(set.directory, "prefilled");
-
-  yield verify_state([true, null, "2.0", "2.0", null, null]);
-
-  yield promiseShutdownManager();
-});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -18,16 +18,14 @@ skip-if = appname != "firefox"
 [test_hotfix_cert.js]
 [test_isReady.js]
 [test_metadata_update.js]
 [test_pluginInfoURL.js]
 [test_provider_markSafe.js]
 [test_provider_shutdown.js]
 [test_provider_unsafe_access_shutdown.js]
 [test_provider_unsafe_access_startup.js]
-[test_ProductAddonChecker.js]
 [test_shutdown.js]
-[test_system_update.js]
 [test_system_reset.js]
 [test_XPIcancel.js]
 [test_XPIStates.js]
 
 [include:xpcshell-shared.ini]
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -25,16 +25,18 @@ const PREF_APP_UPDATE_BACKGROUND_INTERVA
 const PREF_APP_UPDATE_BACKGROUNDERRORS    = "app.update.backgroundErrors";
 const PREF_APP_UPDATE_BACKGROUNDMAXERRORS = "app.update.backgroundMaxErrors";
 const PREF_APP_UPDATE_CANCELATIONS        = "app.update.cancelations";
 const PREF_APP_UPDATE_CERTS_BRANCH        = "app.update.certs.";
 const PREF_APP_UPDATE_CERT_CHECKATTRS     = "app.update.cert.checkAttributes";
 const PREF_APP_UPDATE_CERT_ERRORS         = "app.update.cert.errors";
 const PREF_APP_UPDATE_CERT_MAXERRORS      = "app.update.cert.maxErrors";
 const PREF_APP_UPDATE_CERT_REQUIREBUILTIN = "app.update.cert.requireBuiltIn";
+const PREF_APP_UPDATE_CUSTOM              = "app.update.custom";
+const PREF_APP_UPDATE_IMEI_HASH           = "app.update.imei_hash";
 const PREF_APP_UPDATE_ENABLED             = "app.update.enabled";
 const PREF_APP_UPDATE_IDLETIME            = "app.update.idletime";
 const PREF_APP_UPDATE_INCOMPATIBLE_MODE   = "app.update.incompatible.mode";
 const PREF_APP_UPDATE_INTERVAL            = "app.update.interval";
 const PREF_APP_UPDATE_LOG                 = "app.update.log";
 const PREF_APP_UPDATE_MODE                = "app.update.mode";
 const PREF_APP_UPDATE_NEVER_BRANCH        = "app.update.never.";
 const PREF_APP_UPDATE_NOTIFIEDUNSUPPORTED = "app.update.notifiedUnsupported";
@@ -47,16 +49,21 @@ const PREF_APP_UPDATE_URL               
 const PREF_APP_UPDATE_URL_DETAILS         = "app.update.url.details";
 const PREF_APP_UPDATE_URL_OVERRIDE        = "app.update.url.override";
 const PREF_APP_UPDATE_SERVICE_ENABLED     = "app.update.service.enabled";
 const PREF_APP_UPDATE_SERVICE_ERRORS      = "app.update.service.errors";
 const PREF_APP_UPDATE_SERVICE_MAX_ERRORS  = "app.update.service.maxErrors";
 const PREF_APP_UPDATE_SOCKET_ERRORS       = "app.update.socket.maxErrors";
 const PREF_APP_UPDATE_RETRY_TIMEOUT       = "app.update.socket.retryTimeout";
 
+const PREF_APP_DISTRIBUTION               = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION       = "distribution.version";
+
+const PREF_APP_B2G_VERSION                = "b2g.version";
+
 const PREF_EM_HOTFIX_ID                   = "extensions.hotfix.id";
 
 const URI_UPDATE_PROMPT_DIALOG  = "chrome://mozapps/content/update/updates.xul";
 const URI_UPDATE_HISTORY_DIALOG = "chrome://mozapps/content/update/history.xul";
 const URI_BRAND_PROPERTIES      = "chrome://branding/locale/brand.properties";
 const URI_UPDATES_PROPERTIES    = "chrome://mozapps/locale/update/updates.properties";
 const URI_UPDATE_NS             = "http://www.mozilla.org/2005/app-update";
 
@@ -75,16 +82,17 @@ const FILE_UPDATE_VERSION = "update.vers
 const FILE_UPDATE_ARCHIVE = "update.mar";
 const FILE_UPDATE_LINK    = "update.link";
 const FILE_UPDATE_LOG     = "update.log";
 const FILE_UPDATES_DB     = "updates.xml";
 const FILE_UPDATE_ACTIVE  = "active-update.xml";
 const FILE_PERMS_TEST     = "update.test";
 const FILE_LAST_LOG       = "last-update.log";
 const FILE_BACKUP_LOG     = "backup-update.log";
+const FILE_UPDATE_LOCALE  = "update.locale";
 
 const STATE_NONE            = "null";
 const STATE_DOWNLOADING     = "downloading";
 const STATE_PENDING         = "pending";
 const STATE_PENDING_SVC     = "pending-service";
 const STATE_APPLYING        = "applying";
 const STATE_APPLIED         = "applied";
 const STATE_APPLIED_OS      = "applied-os";
@@ -197,34 +205,227 @@ var gSDCardMountLock = null;
 // Gonk only
 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
   if (AppConstants.platform != "gonk") {
     throw Cr.NS_ERROR_NOT_IMPLEMENTED;
   }
   return Services.env.get("EXTERNAL_STORAGE");
 });
 
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
+                                  "resource://gre/modules/UpdateChannel.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gLogEnabled", function aus_gLogEnabled() {
   return getPref("getBoolPref", PREF_APP_UPDATE_LOG, false);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
   return Services.strings.createBundle(URI_UPDATES_PROPERTIES);
 });
 
 // shared code for suppressing bad cert dialogs
 XPCOMUtils.defineLazyGetter(this, "gCertUtils", function aus_gCertUtils() {
   let temp = { };
   Cu.import("resource://gre/modules/CertUtils.jsm", temp);
   return temp;
 });
 
+XPCOMUtils.defineLazyGetter(this, "gABI", function aus_gABI() {
+  let abi = null;
+  try {
+    abi = Services.appinfo.XPCOMABI;
+  }
+  catch (e) {
+    LOG("gABI - XPCOM ABI unknown: updates are not possible.");
+  }
+
+  if (AppConstants.platform == "macosx") {
+    // Mac universal build should report a different ABI than either macppc
+    // or mactel.
+    let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
+                   getService(Ci.nsIMacUtils);
+
+    if (macutils.isUniversalBinary) {
+      abi += "-u-" + macutils.architecturesInBinary;
+    }
+  } else if (AppConstants.platform == "win") {
+    // Windows build should report the CPU architecture that it's running on.
+    abi += "-" + gWinCPUArch;
+  }
+  return abi;
+});
+
+XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
+  let osVersion;
+  try {
+    osVersion = Services.sysinfo.getProperty("name") + " " +
+                Services.sysinfo.getProperty("version");
+  }
+  catch (e) {
+    LOG("gOSVersion - OS Version unknown: updates are not possible.");
+  }
+
+  if (osVersion) {
+    if (AppConstants.platform == "win") {
+      const BYTE = ctypes.uint8_t;
+      const WORD = ctypes.uint16_t;
+      const DWORD = ctypes.uint32_t;
+      const WCHAR = ctypes.char16_t;
+      const BOOL = ctypes.int;
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
+      const SZCSDVERSIONLENGTH = 128;
+      const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
+          [
+          {dwOSVersionInfoSize: DWORD},
+          {dwMajorVersion: DWORD},
+          {dwMinorVersion: DWORD},
+          {dwBuildNumber: DWORD},
+          {dwPlatformId: DWORD},
+          {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
+          {wServicePackMajor: WORD},
+          {wServicePackMinor: WORD},
+          {wSuiteMask: WORD},
+          {wProductType: BYTE},
+          {wReserved: BYTE}
+          ]);
+
+      // This structure is described at:
+      // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+      const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+          [
+          {wProcessorArchitecture: WORD},
+          {wReserved: WORD},
+          {dwPageSize: DWORD},
+          {lpMinimumApplicationAddress: ctypes.voidptr_t},
+          {lpMaximumApplicationAddress: ctypes.voidptr_t},
+          {dwActiveProcessorMask: DWORD.ptr},
+          {dwNumberOfProcessors: DWORD},
+          {dwProcessorType: DWORD},
+          {dwAllocationGranularity: DWORD},
+          {wProcessorLevel: WORD},
+          {wProcessorRevision: WORD}
+          ]);
+
+      let kernel32 = false;
+      try {
+        kernel32 = ctypes.open("Kernel32");
+      } catch (e) {
+        LOG("gOSVersion - Unable to open kernel32! " + e);
+        osVersion += ".unknown (unknown)";
+      }
+
+      if (kernel32) {
+        try {
+          // Get Service pack info
+          try {
+            let GetVersionEx = kernel32.declare("GetVersionExW",
+                                                ctypes.default_abi,
+                                                BOOL,
+                                                OSVERSIONINFOEXW.ptr);
+            let winVer = OSVERSIONINFOEXW();
+            winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
+
+            if(0 !== GetVersionEx(winVer.address())) {
+              osVersion += "." + winVer.wServicePackMajor +
+                           "." + winVer.wServicePackMinor;
+            } else {
+              LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)");
+              osVersion += ".unknown";
+            }
+          } catch (e) {
+            LOG("gOSVersion - error getting service pack information. Exception: " + e);
+            osVersion += ".unknown";
+          }
+        } finally {
+          kernel32.close();
+        }
+
+        // Add processor architecture
+        osVersion += " (" + gWinCPUArch + ")";
+      }
+    }
+
+    try {
+      osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")";
+    }
+    catch (e) {
+      // Not all platforms have a secondary widget library, so an error is nothing to worry about.
+    }
+    osVersion = encodeURIComponent(osVersion);
+  }
+  return osVersion;
+});
+
+/* Windows only getter that returns the processor architecture. */
+XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() {
+  // Get processor architecture
+  let arch = "unknown";
+
+  const WORD = ctypes.uint16_t;
+  const DWORD = ctypes.uint32_t;
+
+  // This structure is described at:
+  // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
+  const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
+      [
+      {wProcessorArchitecture: WORD},
+      {wReserved: WORD},
+      {dwPageSize: DWORD},
+      {lpMinimumApplicationAddress: ctypes.voidptr_t},
+      {lpMaximumApplicationAddress: ctypes.voidptr_t},
+      {dwActiveProcessorMask: DWORD.ptr},
+      {dwNumberOfProcessors: DWORD},
+      {dwProcessorType: DWORD},
+      {dwAllocationGranularity: DWORD},
+      {wProcessorLevel: WORD},
+      {wProcessorRevision: WORD}
+      ]);
+
+  let kernel32 = false;
+  try {
+    kernel32 = ctypes.open("Kernel32");
+  } catch (e) {
+    LOG("gWinCPUArch - Unable to open kernel32! Exception: " + e);
+  }
+
+  if (kernel32) {
+    try {
+      let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
+                                                 ctypes.default_abi,
+                                                 ctypes.void_t,
+                                                 SYSTEM_INFO.ptr);
+      let winSystemInfo = SYSTEM_INFO();
+      // Default to unknown
+      winSystemInfo.wProcessorArchitecture = 0xffff;
+
+      GetNativeSystemInfo(winSystemInfo.address());
+      switch (winSystemInfo.wProcessorArchitecture) {
+        case 9:
+          arch = "x64";
+          break;
+        case 6:
+          arch = "IA64";
+          break;
+        case 0:
+          arch = "x86";
+          break;
+      }
+    } catch (e) {
+      LOG("gWinCPUArch - error getting processor architecture. " +
+          "Exception: " + e);
+    } finally {
+      kernel32.close();
+    }
+  }
+
+  return arch;
+});
+
 /**
  * Tests to make sure that we can write to a given directory.
  *
  * @param updateTestFile a test file in the directory that needs to be tested.
  * @param createDirectory whether a test directory should be created.
  * @throws if we don't have right access to the directory.
  */
 function testWriteAccess(updateTestFile, createDirectory) {
@@ -521,23 +722,23 @@ XPCOMUtils.defineLazyGetter(this, "gCanC
   var enabled = getPref("getBoolPref", PREF_APP_UPDATE_ENABLED, true);
   if (!enabled && Services.prefs.prefIsLocked(PREF_APP_UPDATE_ENABLED)) {
     LOG("gCanCheckForUpdates - unable to automatically check for updates, " +
         "the preference is disabled and admistratively locked.");
     return false;
   }
 
   // If we don't know the binary platform we're updating, we can't update.
-  if (!UpdateUtils.ABI) {
+  if (!gABI) {
     LOG("gCanCheckForUpdates - unable to check for updates, unknown ABI");
     return false;
   }
 
   // If we don't know the OS version we're updating, we can't update.
-  if (!UpdateUtils.OSVersion) {
+  if (!gOSVersion) {
     LOG("gCanCheckForUpdates - unable to check for updates, unknown OS " +
         "version");
     return false;
   }
 
   LOG("gCanCheckForUpdates - able to check for updates");
   return true;
 });
@@ -1037,16 +1238,68 @@ function cleanupActiveUpdate() {
   um.activeUpdate = null;
   um.saveUpdates();
 
   // Now trash the updates directory, since we're done with it
   cleanUpUpdatesDir();
 }
 
 /**
+ * Gets the locale from the update.locale file for replacing %LOCALE% in the
+ * update url. The update.locale file can be located in the application
+ * directory or the GRE directory with preference given to it being located in
+ * the application directory.
+ */
+function getLocale() {
+  if (gLocale) {
+    return gLocale;
+  }
+
+  let channel;
+  for (let res of ['app', 'gre']) {
+    channel = Services.io.newChannel2("resource://" + res + "/" + FILE_UPDATE_LOCALE,
+                                      null,
+                                      null,
+                                      null,      // aLoadingNode
+                                      Services.scriptSecurityManager.getSystemPrincipal(),
+                                      null,      // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST);
+    try {
+      var inputStream = channel.open();
+      gLocale = readStringFromInputStream(inputStream);
+    } catch(e) {}
+    if (gLocale)
+      break;
+  }
+
+  if (!gLocale)
+    throw Components.Exception(FILE_UPDATE_LOCALE + " file doesn't exist in " +
+                               "either the application or GRE directories",
+                               Cr.NS_ERROR_FILE_NOT_FOUND);
+
+  LOG("getLocale - getting locale from file: " + channel.originalURI.spec +
+      ", locale: " + gLocale);
+  return gLocale;
+}
+
+/* Get the distribution pref values, from defaults only */
+function getDistributionPrefValue(aPrefName) {
+  var prefValue = "default";
+
+  try {
+    prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName);
+  } catch (e) {
+    // use default when pref not found
+  }
+
+  return prefValue;
+}
+
+/**
  * An enumeration of items in a JS array.
  * @constructor
  */
 function ArrayEnumerator(aItems) {
   this._index = 0;
   if (aItems) {
     for (var i = 0; i < aItems.length; ++i) {
       if (!aItems[i])
@@ -2269,19 +2522,19 @@ UpdateService.prototype = {
     let validUpdateURL = true;
     try {
       this.backgroundChecker.getUpdateURL(false);
     } catch (e) {
       validUpdateURL = false;
     }
     // The following checks are done here so they can be differentiated from
     // foreground checks.
-    if (!UpdateUtils.OSVersion) {
+    if (!gOSVersion) {
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_VERSION);
-    } else if (!UpdateUtils.ABI) {
+    } else if (!gABI) {
       AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_NO_OS_ABI);
     } else if (!validUpdateURL) {
       if (overridePrefHasValue) {
         if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_URL_OVERRIDE)) {
           AUSTLMY.pingCheckCode(this._pingSuffix,
                                 AUSTLMY.CHK_INVALID_USER_OVERRIDE_URL);
         } else {
           AUSTLMY.pingCheckCode(this._pingSuffix,
@@ -3013,24 +3266,24 @@ UpdateManager.prototype = {
     return this._updates.length;
   },
 
   /**
    * See nsIUpdateService.idl
    */
   get activeUpdate() {
     if (this._activeUpdate &&
-        this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
+        this._activeUpdate.channel != UpdateChannel.get()) {
       LOG("UpdateManager:get activeUpdate - channel has changed, " +
           "reloading default preferences to workaround bug 802022");
       // Workaround to get distribution preferences loaded (Bug 774618). This
       // can be removed after bug 802022 is fixed.
       let prefSvc = Services.prefs.QueryInterface(Ci.nsIObserver);
       prefSvc.observe(null, "reload-default-prefs", null);
-      if (this._activeUpdate.channel != UpdateUtils.UpdateChannel) {
+      if (this._activeUpdate.channel != UpdateChannel.get()) {
         // User switched channels, clear out any old active updates and remove
         // partial downloads
         this._activeUpdate = null;
         this.saveUpdates();
 
         // Destroy the updates directory, since we're done with it.
         cleanUpUpdatesDir();
       }
@@ -3258,17 +3511,50 @@ Checker.prototype = {
       }
     }
 
     if (!url || url == "") {
       LOG("Checker:getUpdateURL - update URL not defined");
       return null;
     }
 
-    url = UpdateUtils.formatUpdateURL(url);
+    url = url.replace(/%PRODUCT%/g, Services.appinfo.name);
+    url = url.replace(/%VERSION%/g, Services.appinfo.version);
+    url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID);
+    url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + gABI);
+    url = url.replace(/%OS_VERSION%/g, gOSVersion);
+    if (/%LOCALE%/.test(url)) {
+      url = url.replace(/%LOCALE%/g, getLocale());
+    }
+    url = url.replace(/%CHANNEL%/g, UpdateChannel.get());
+    url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion);
+    url = url.replace(/%DISTRIBUTION%/g,
+                      getDistributionPrefValue(PREF_APP_DISTRIBUTION));
+    url = url.replace(/%DISTRIBUTION_VERSION%/g,
+                      getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
+    url = url.replace(/%CUSTOM%/g, getPref("getCharPref", PREF_APP_UPDATE_CUSTOM, ""));
+    url = url.replace(/\+/g, "%2B");
+
+    if (AppConstants.platform == "gonk") {
+      let sysLibs = {};
+      Cu.import("resource://gre/modules/systemlibs.js", sysLibs);
+      let productDevice = sysLibs.libcutils.property_get("ro.product.device");
+      let buildType = sysLibs.libcutils.property_get("ro.build.type");
+      url = url.replace(/%PRODUCT_MODEL%/g,
+                        sysLibs.libcutils.property_get("ro.product.model"));
+      if (buildType == "user" || buildType == "userdebug") {
+        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice);
+      } else {
+        url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType);
+      }
+      url = url.replace(/%B2G_VERSION%/g,
+                        getPref("getCharPref", PREF_APP_B2G_VERSION, null));
+      url = url.replace(/%IMEI%/g,
+                        getPref("getCharPref", PREF_APP_UPDATE_IMEI_HASH, "default"));
+    }
 
     if (force) {
       url += (url.indexOf("?") != -1 ? "&" : "?") + "force=1";
     }
 
     LOG("Checker:getUpdateURL - update URL: " + url);
     return url;
   },
@@ -3350,17 +3636,17 @@ Checker.prototype = {
       let update;
       try {
         update = new Update(updateElement);
       } catch (e) {
         LOG("Checker:_updates get - invalid <update/>, ignoring...");
         continue;
       }
       update.serviceURL = this.getUpdateURL(this._forced);
-      update.channel = UpdateUtils.UpdateChannel;
+      update.channel = UpdateChannel.get();
       updates.push(update);
     }
 
     return updates;
   },
 
   /**
    * Returns the status code for the XMLHttpRequest