merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 14 Oct 2016 11:59:12 +0200
changeset 363033 1391a2889aeb2bdd61ad6ef838e65826e35aabc2
parent 362927 3a35f6cea288558cd0433f1b8b00c02e8bc3e4da (current diff)
parent 363032 96d1b7238832ebf3e7e03053db19a8e557f6ca42 (diff)
child 363034 9ab31bcd2d404a6ea0586549674992c02699680a
child 363061 0d101ebfd95cf7f63fd036e5d951b84f27ac6a4d
child 363099 f0b64ccb0aefcd96baedf92538aa4c24368ce01b
push id1369
push userjlorenzo@mozilla.com
push dateMon, 27 Feb 2017 14:59:41 +0000
treeherdermozilla-release@d75a1dba431f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
first release with
nightly linux32
1391a2889aeb / 52.0a1 / 20161014030204 / files
nightly linux64
1391a2889aeb / 52.0a1 / 20161014030204 / files
nightly mac
1391a2889aeb / 52.0a1 / 20161014030204 / files
nightly win32
1391a2889aeb / 52.0a1 / 20161014030204 / files
nightly win64
1391a2889aeb / 52.0a1 / 20161014030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
b2g/chrome/content/runapp.js
b2g/components/WebappsUpdateTimer.js
b2g/components/WebappsUpdater.jsm
b2g/components/test/mochitest/app/client.html
b2g/components/test/mochitest/app/index.html
b2g/components/test/mochitest/app/manifest.webapp
b2g/components/test/mochitest/app/manifest.webapp^headers^
b2g/components/test/mochitest/app/sw.sjs
b2g/components/test/mochitest/app/version.html
b2g/components/test/mochitest/app/wait_for_update.html
b2g/components/test/mochitest/chrome.ini
b2g/components/test/mochitest/test_aboutserviceworkers.html
browser/components/nsBrowserGlue.js
browser/installer/package-manifest.in
browser/modules/ContentWebRTC.jsm
browser/themes/linux/downloads/buttons.png
browser/themes/osx/downloads/buttons.png
browser/themes/osx/downloads/buttons@2x.png
browser/themes/windows/downloads/buttons-XP.png
browser/themes/windows/downloads/buttons.png
caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
devtools/server/actors/webapps.js
devtools/shared/apps/tests/data/app-certified.zip
devtools/shared/apps/tests/data/app-overload.zip
devtools/shared/apps/tests/data/app-redirect.zip
devtools/shared/apps/tests/data/app-system.zip
devtools/shared/apps/tests/data/app-updated.zip
devtools/shared/apps/tests/data/app.zip
devtools/shared/apps/tests/debugger-protocol-helper.js
devtools/shared/apps/tests/mochitest.ini
devtools/shared/apps/tests/redirect.sjs
devtools/shared/apps/tests/test_webapps_actor.html
dom/apps/AppDownloadManager.jsm
dom/apps/FreeSpaceWatcher.jsm
dom/apps/ImportExport.jsm
dom/apps/Langpacks.jsm
dom/apps/OfflineCacheInstaller.jsm
dom/apps/OperatorApps.jsm
dom/apps/ScriptPreloader.jsm
dom/apps/StoreTrustAnchor.jsm
dom/apps/Webapps.js
dom/apps/Webapps.jsm
dom/apps/Webapps.manifest
dom/apps/tests/apps/bad_content_type.webapp
dom/apps/tests/apps/basic.webapp
dom/apps/tests/apps/basic.webapp^headers^
dom/apps/tests/apps/installs_allowed_from_chrome_mochitests.webapp
dom/apps/tests/apps/installs_allowed_from_chrome_mochitests.webapp^headers^
dom/apps/tests/apps/installs_allowed_from_example.com.webapp
dom/apps/tests/apps/installs_allowed_from_example.com.webapp^headers^
dom/apps/tests/apps/invalid_activity_href.webapp
dom/apps/tests/apps/invalid_activity_href.webapp^headers^
dom/apps/tests/apps/invalid_activity_href2.webapp
dom/apps/tests/apps/invalid_activity_href2.webapp^headers^
dom/apps/tests/apps/invalid_entry_point.webapp
dom/apps/tests/apps/invalid_entry_point.webapp^headers^
dom/apps/tests/apps/invalid_launch_path.webapp
dom/apps/tests/apps/invalid_launch_path.webapp^headers^
dom/apps/tests/apps/invalid_launch_path2.webapp
dom/apps/tests/apps/invalid_launch_path2.webapp^headers^
dom/apps/tests/apps/invalid_locale_entry_point.webapp
dom/apps/tests/apps/invalid_locale_entry_point.webapp^headers^
dom/apps/tests/apps/invalid_message.webapp
dom/apps/tests/apps/invalid_message.webapp^headers^
dom/apps/tests/apps/json_syntax_error.webapp
dom/apps/tests/apps/json_syntax_error.webapp^headers^
dom/apps/tests/apps/launch_paths.webapp
dom/apps/tests/apps/launch_paths.webapp^headers^
dom/apps/tests/apps/missing_required_field.webapp
dom/apps/tests/apps/missing_required_field.webapp^headers^
dom/apps/tests/apps/no_delegated_install.webapp
dom/apps/tests/apps/no_delegated_install.webapp^headers^
dom/apps/tests/apps/utf8.webapp
dom/apps/tests/apps/utf8.webapp^headers^
dom/apps/tests/asmjs/asmjs_app.sjs
dom/apps/tests/asmjs/index.html
dom/apps/tests/asmjs/manifest.webapp
dom/apps/tests/chromeAddCert.js
dom/apps/tests/common.js
dom/apps/tests/cross_origin.html
dom/apps/tests/file_app.sjs
dom/apps/tests/file_app.template.html
dom/apps/tests/file_bug_779982.html
dom/apps/tests/file_bug_779982.js
dom/apps/tests/file_bug_945152.html
dom/apps/tests/file_bug_945152.sjs
dom/apps/tests/file_cached_app.template.appcache
dom/apps/tests/file_cached_app.template.webapp
dom/apps/tests/file_hosted_app.template.webapp
dom/apps/tests/file_hosted_certified.webapp
dom/apps/tests/file_hosted_certified.webapp^headers^
dom/apps/tests/file_invalidWidget_app.template.webapp
dom/apps/tests/file_manifest.json
dom/apps/tests/file_manifest.json^headers^
dom/apps/tests/file_packaged_app.sjs
dom/apps/tests/file_packaged_app.template.html
dom/apps/tests/file_packaged_app.template.webapp
dom/apps/tests/file_script.template.js
dom/apps/tests/file_test_widget.js
dom/apps/tests/file_widget_app.template.html
dom/apps/tests/file_widget_app.template.webapp
dom/apps/tests/icon15.png
dom/apps/tests/icon15alternate.png
dom/apps/tests/icon48.png
dom/apps/tests/install_and_redirect_helper.xul
dom/apps/tests/langpack/event.html
dom/apps/tests/langpack/fr/app.json
dom/apps/tests/langpack/fr/app.properties
dom/apps/tests/langpack/fr/manifest.json
dom/apps/tests/langpack/index.html
dom/apps/tests/langpack/lang1.webapp
dom/apps/tests/langpack/lang1.webapp^headers^
dom/apps/tests/langpack/lang2.webapp
dom/apps/tests/langpack/lang2.webapp^headers^
dom/apps/tests/langpack/localizedvalues.html
dom/apps/tests/langpack/manifest.webapp
dom/apps/tests/langpack/manifest.webapp^headers^
dom/apps/tests/langpack/manifest_no_version.webapp
dom/apps/tests/langpack/manifest_version_xyz.webapp
dom/apps/tests/langpack/resources.html
dom/apps/tests/marketplace/marketplace_app.webapp
dom/apps/tests/marketplace/marketplace_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_app.zip
dom/apps/tests/marketplace/marketplace_dev_app.webapp
dom/apps/tests/marketplace/marketplace_dev_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_dev_app.zip
dom/apps/tests/marketplace/marketplace_dev_privileged_app.webapp
dom/apps/tests/marketplace/marketplace_dev_privileged_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_dev_privileged_app.zip
dom/apps/tests/marketplace/marketplace_dev_reviewers_app.webapp
dom/apps/tests/marketplace/marketplace_dev_reviewers_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_dev_reviewers_app.zip
dom/apps/tests/marketplace/marketplace_privileged_app.webapp
dom/apps/tests/marketplace/marketplace_privileged_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_privileged_app.zip
dom/apps/tests/marketplace/marketplace_reviewers_app.webapp
dom/apps/tests/marketplace/marketplace_reviewers_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_reviewers_app.zip
dom/apps/tests/marketplace/marketplace_stage_app.webapp
dom/apps/tests/marketplace/marketplace_stage_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_stage_app.zip
dom/apps/tests/marketplace/marketplace_stage_privileged_app.webapp
dom/apps/tests/marketplace/marketplace_stage_privileged_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_stage_privileged_app.zip
dom/apps/tests/marketplace/marketplace_stage_reviewers_app.webapp
dom/apps/tests/marketplace/marketplace_stage_reviewers_app.webapp^headers^
dom/apps/tests/marketplace/marketplace_stage_reviewers_app.zip
dom/apps/tests/mochitest.ini
dom/apps/tests/pkg_install_iframe.html
dom/apps/tests/signed/corrupt_app_1.zip
dom/apps/tests/signed/origin_app_1.zip
dom/apps/tests/signed/unknown_issuer_app_1.zip
dom/apps/tests/signed/unsigned_app_1.zip
dom/apps/tests/signed/unsigned_app_2.zip
dom/apps/tests/signed/valid_app_1.zip
dom/apps/tests/signed/valid_app_2.zip
dom/apps/tests/signed_app.sjs
dom/apps/tests/signed_app_template.webapp
dom/apps/tests/test_app_enabled.html
dom/apps/tests/test_app_update.html
dom/apps/tests/test_bug_1168300.html
dom/apps/tests/test_bug_765063.xul
dom/apps/tests/test_bug_771294.xul
dom/apps/tests/test_bug_779982.html
dom/apps/tests/test_bug_795164.html
dom/apps/tests/test_bug_945152.html
dom/apps/tests/test_checkInstalled.html
dom/apps/tests/test_cross_origin.xul
dom/apps/tests/test_import_export.html
dom/apps/tests/test_install_app.xul
dom/apps/tests/test_install_dev_mode.html
dom/apps/tests/test_install_errors.xul
dom/apps/tests/test_install_multiple_apps_origin.html
dom/apps/tests/test_install_receipts.html
dom/apps/tests/test_install_utf8.xul
dom/apps/tests/test_langpacks.html
dom/apps/tests/test_launch_paths.xul
dom/apps/tests/test_list_api.xul
dom/apps/tests/test_marketplace_pkg_install.html
dom/apps/tests/test_operator_app_install.js
dom/apps/tests/test_operator_app_install.xul
dom/apps/tests/test_packaged_app_asmjs.html
dom/apps/tests/test_packaged_app_common.js
dom/apps/tests/test_packaged_app_install.html
dom/apps/tests/test_packaged_app_update.html
dom/apps/tests/test_receipt_operations.html
dom/apps/tests/test_signed_pkg_install.html
dom/apps/tests/test_theme_role.html
dom/apps/tests/test_third_party_homescreen.html
dom/apps/tests/test_uninstall_errors.html
dom/apps/tests/test_web_app_install.html
dom/apps/tests/test_widget.html
dom/apps/tests/test_widget_browser.html
dom/apps/tests/unit/head.js
dom/apps/tests/unit/tail.js
dom/broadcastchannel/tests/manifest.webapp
dom/broadcastchannel/tests/server.sjs
dom/broadcastchannel/tests/test_broadcastchannel_mozbrowser.html
dom/broadcastchannel/tests/test_broadcastchannel_mozbrowser2.html
dom/browser-element/mochitest/browserElement_AllowEmbedAppsInNestedOOIframe.js
dom/browser-element/mochitest/browserElement_AppFramePermission.js
dom/browser-element/mochitest/browserElement_AppWindowNamespace.js
dom/browser-element/mochitest/browserElement_CloseApp.js
dom/browser-element/mochitest/browserElement_DisallowEmbedAppsInOOP.js
dom/browser-element/mochitest/browserElement_MultipleAudioChannels.js
dom/browser-element/mochitest/file_browserElement_AllowEmbedAppsInNestedOOIframe.html
dom/browser-element/mochitest/file_browserElement_AppFramePermission.html
dom/browser-element/mochitest/file_browserElement_AppWindowNamespace.html
dom/browser-element/mochitest/file_browserElement_CloseApp.html
dom/browser-element/mochitest/file_browserElement_DisallowEmbedAppsInOOP.html
dom/browser-element/mochitest/file_browserElement_MultipleAudioChannels.html
dom/browser-element/mochitest/multipleAudioChannels_manifest.webapp
dom/browser-element/mochitest/multipleAudioChannels_manifest.webapp^headers^
dom/browser-element/mochitest/priority/test_BackgroundLRU.html
dom/browser-element/mochitest/priority/test_MultipleActivities.html
dom/browser-element/mochitest/test_browserElement_MultipleAudioChannels.html
dom/browser-element/mochitest/test_browserElement_inproc_AppFramePermission.html
dom/browser-element/mochitest/test_browserElement_inproc_AppWindowNamespace.html
dom/browser-element/mochitest/test_browserElement_inproc_CloseApp.html
dom/browser-element/mochitest/test_browserElement_inproc_DisallowEmbedAppsInOOP.html
dom/browser-element/mochitest/test_browserElement_oop_AllowEmbedAppsInNestedOOIframe.html
dom/browser-element/mochitest/test_browserElement_oop_AppFramePermission.html
dom/browser-element/mochitest/test_browserElement_oop_AppWindowNamespace.html
dom/browser-element/mochitest/test_browserElement_oop_CloseApp.html
dom/browser-element/mochitest/test_browserElement_oop_DisallowEmbedAppsInOOP.html
dom/cache/test/mochitest/app/after_clear.html
dom/cache/test/mochitest/app/before_clear.html
dom/cache/test/mochitest/app/index.html
dom/cache/test/mochitest/app/manifest.webapp
dom/cache/test/mochitest/app/manifest.webapp^headers^
dom/cache/test/mochitest/app/sw.js
dom/cache/test/mochitest/app/test.js
dom/cache/test/mochitest/test_cache_clear_on_app_uninstall.html
dom/downloads/tests/common_app.js
dom/downloads/tests/file_app.sjs
dom/downloads/tests/file_app.template.webapp
dom/downloads/tests/shim_app_as_test.js
dom/downloads/tests/shim_app_as_test_chrome.js
dom/downloads/tests/test_downloads_adopt_download.html
dom/downloads/tests/testapp_downloads_adopt_download.html
dom/downloads/tests/testapp_downloads_adopt_download.js
dom/downloads/tests/testapp_downloads_adopt_download.manifest
dom/indexedDB/test/test_webapp_clearBrowserData_inproc_inproc.html
dom/indexedDB/test/test_webapp_clearBrowserData_inproc_oop.html
dom/indexedDB/test/test_webapp_clearBrowserData_oop_inproc.html
dom/indexedDB/test/webapp_clearBrowserData.js
dom/indexedDB/test/webapp_clearBrowserData_appFrame.html
dom/indexedDB/test/webapp_clearBrowserData_browserFrame.html
dom/ipc/tests/file_app.sjs
dom/ipc/tests/file_app.template.webapp
dom/ipc/tests/test_permission_embed.html
dom/ipc/tests/test_permission_for_in_process_app.html
dom/ipc/tests/test_permission_for_nested_oop_app.html
dom/ipc/tests/test_permission_for_oop_app.html
dom/ipc/tests/test_permission_for_two_oop_apps.html
dom/ipc/tests/test_permission_framescript.js
dom/ipc/tests/test_permission_helper.js
dom/ipc/tests/test_permission_when_oop_app_crashes.html
dom/media/MediaManager.cpp
dom/media/MediaPermissionGonk.cpp
dom/media/nsIMediaManager.idl
dom/messagechannel/MessagePortList.cpp
dom/messagechannel/MessagePortList.h
dom/permission/tests/test_permission_basics.html
dom/permission/tests/test_webapps-manage.html
dom/security/test/csp/chrome.ini
dom/security/test/csp/file_bug768029.html
dom/security/test/csp/file_bug773891.html
dom/security/test/csp/file_bug773891.sjs
dom/security/test/csp/test_bug768029.html
dom/security/test/csp/test_bug773891.html
dom/security/test/csp/test_bug949549.html
dom/tests/browser/browser_webapps_permissions.js
dom/tests/browser/browser_webapps_perms_reinstall.js
dom/tests/browser/test-webapp-original.webapp
dom/tests/browser/test-webapp-reinstall.webapp
dom/tests/browser/test-webapp.webapp
dom/tests/browser/test-webapps-permissions.html
dom/tests/mochitest/fetch/app-protocol/README.txt
dom/tests/mochitest/fetch/app-protocol/application.zip
dom/tests/mochitest/fetch/app-protocol/foo.txt
dom/tests/mochitest/fetch/app-protocol/index.html
dom/tests/mochitest/fetch/app-protocol/manifest.webapp
dom/tests/mochitest/fetch/app-protocol/update.webapp
dom/tests/mochitest/fetch/app-protocol/update.webapp^headers^
dom/tests/mochitest/fetch/test_fetch_app_protocol.html
dom/tests/mochitest/localstorage/frameAppIsolation.html
dom/tests/mochitest/localstorage/frame_clear_browser_data.html
dom/tests/mochitest/localstorage/test_appIsolation.html
dom/tests/mochitest/localstorage/test_app_uninstall.html
dom/tests/mochitest/localstorage/test_clear_browser_data.html
dom/tests/mochitest/notification/test_notification_resend.html
dom/webidl/Apps.webidl
dom/webidl/MessagePortList.webidl
dom/webidl/moz.build
extensions/cookie/test/channel_utils.js
extensions/cookie/test/chrome.ini
extensions/cookie/test/test_app_uninstall_cookies.html
extensions/cookie/test/test_app_uninstall_permissions.html
extensions/cookie/test/test_permissionmanager_app_isolation.html
layout/base/nsLayoutUtils.cpp
mobile/android/chrome/content/WebrtcUI.js
modules/libpref/init/all.js
netwerk/test/mochitests/test_web_packaged_app.html
testing/mochitest/tests/Harness_sanity/app.html
testing/mochitest/tests/Harness_sanity/file_app.sjs
testing/mochitest/tests/Harness_sanity/file_app.template.webapp
testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushAppPermissions.html
testing/mochitest/tests/Harness_sanity/test_bug816847.html
new file mode 100644
--- /dev/null
+++ b/.cargo/.gitignore
@@ -0,0 +1,1 @@
+config
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1300895 - Needed clobber to fix mochitests on windows 7 vm opt?
+Bug 1261019 - Clobber on OS X to fix bustage caused by removed files
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -224,17 +224,16 @@ DEFAULT_TEST_PREFS = {
     # Indicate that the download panel has been shown once so that whichever
     # download test runs first doesn't show the popup inconsistently.
     'browser.download.panel.shown': True,
     # Assume the about:newtab page's intro panels have been shown to not depend on
     # which test runs first and happens to open about:newtab
     'browser.newtabpage.introShown': True,
     # Disable useragent updates.
     'general.useragent.updates.enabled': False,
-    'dom.mozApps.debug': True,
     'dom.apps.customization.enabled': True,
     'media.eme.enabled': True,
     'media.eme.apiVisible': True,
     # Don't forceably kill content processes after a timeout
     'dom.ipc.tabs.shutdownTimeoutSecs': 0,
     'general.useragent.locale': "en-US",
     'intl.locale.matchOS': "en-US",
     'dom.indexedDB.experimental': True
--- a/addon-sdk/source/test/preferences/test.json
+++ b/addon-sdk/source/test/preferences/test.json
@@ -32,17 +32,16 @@
   "layout.css.grid.enabled": true,
   "layout.spammy_warnings.enabled": false,
   "dom.mozSettings.enabled": true,
   "network.http.bypass-cachelock-threshold": 200000,
   "geo.provider.testing": true,
   "browser.pagethumbnails.capturing_disabled": true,
   "browser.download.panel.shown": true,
   "general.useragent.updates.enabled": false,
-  "dom.mozApps.debug": true,
   "dom.apps.customization.enabled": true,
   "media.eme.enabled": true,
   "media.eme.apiVisible": true,
   "dom.ipc.tabs.shutdownTimeoutSecs": 0,
   "general.useragent.locale": "en-US",
   "intl.locale.matchOS": "en-US",
   "dom.indexedDB.experimental": true
 }
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -871,21 +871,16 @@ pref("gfx.canvas.max-size-for-skia-gl", 
 pref("gfx.gralloc.fence-with-readpixels", true);
 
 // enable screen mirroring to external display
 pref("gfx.screen-mirroring.enabled", true);
 
 // The url of the page used to display network error details.
 pref("b2g.neterror.url", "net_error.html");
 
-// The origin used for the shared themes uri space.
-pref("b2g.theme.origin", "app://theme.gaiamobile.org");
-pref("dom.mozApps.themable", true);
-pref("dom.mozApps.selected_theme", "default_theme.gaiamobile.org");
-
 // Enable PAC generator for B2G.
 pref("network.proxy.pac_generator", true);
 
 // List of app origins to apply browsing traffic proxy setting, separated by
 // comma.  Specify '*' in the list to apply to all apps.
 pref("network.proxy.browsing.app_origins", "app://system.gaiamobile.org");
 
 // Enable Web Speech synthesis API
deleted file mode 100644
--- a/b2g/chrome/content/runapp.js
+++ /dev/null
@@ -1,171 +0,0 @@
-"use strict";
-
-// runapp.js:
-// Provide a --runapp APPNAME command-line option.
-
-var runAppObj;
-window.addEventListener('load', function() {
-  // Get the command line arguments that were passed to the b2g client
-  let args;
-  try {
-    let service = Cc["@mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds"].getService(Ci.nsISupports);
-    args = service.wrappedJSObject.cmdLine;
-  } catch(e) {}
-
-  if (!args) {
-    return;
-  }
-
-  let appname;
-
-  // - Check if the argument is present before doing any work.
-  try {
-    // Returns null if the argument was not specified.  Throws
-    // NS_ERROR_INVALID_ARG if there is no parameter specified (because
-    // it was the last argument or the next argument starts with '-').
-    // However, someone could still explicitly pass an empty argument!
-    appname = args.handleFlagWithParam('runapp', false);
-  } catch(e) {
-    // treat a missing parameter like an empty parameter (=> show usage)
-    appname = '';
-  }
-
-  // not specified, bail.
-  if (appname === null) {
-    return;
-  }
-
-  runAppObj = new AppRunner(appname);
-  Services.obs.addObserver(runAppObj, 'remote-browser-shown', false);
-  Services.obs.addObserver(runAppObj, 'inprocess-browser-shown', false);
-});
-
-window.addEventListener('unload', function() {
-  if (runAppObj) {
-    Services.obs.removeObserver(runAppObj, 'remote-browser-shown');
-    Services.obs.removeObserver(runAppObj, 'inprocess-browser-shown');
-  }
-});
-
-function AppRunner(aName) {
-  this._appName = aName;
-  this._apps = [];
-}
-AppRunner.prototype = {
-  observe: function(aSubject, aTopic, aData) {
-    let frameLoader = aSubject;
-    // get a ref to the app <iframe>
-    frameLoader.QueryInterface(Ci.nsIFrameLoader);
-    // Ignore notifications that aren't from a BrowserOrApp
-    if (!frameLoader.ownerIsMozBrowserOrAppFrame) {
-      return;
-    }
-
-    let frame = frameLoader.ownerElement;
-    if (!frame.appManifestURL) { // Ignore all frames but app frames
-      return;
-    }
-
-    if (aTopic == 'remote-browser-shown' ||
-        aTopic == 'inprocess-browser-shown') {
-      this.doRunApp(frame);
-    }
-  },
-
-  doRunApp: function(currentFrame) {
-    // - Get the list of apps since the parameter was specified
-    if (this._apps.length) {
-      this.getAllSuccess(this._apps, currentFrame)
-    } else {
-      var req = navigator.mozApps.mgmt.getAll();
-      req.onsuccess = function() {
-        this._apps = req.result;
-        this.getAllSuccess(this._apps, currentFrame)
-      }.bind(this);
-      req.onerror = this.getAllError.bind(this);
-    }
-  },
-
-  getAllSuccess: function(apps, currentFrame) {
-    function findAppWithName(name) {
-      let normalizedSearchName = name.replace(/[- ]+/g, '').toLowerCase();
-
-      for (let i = 0; i < apps.length; i++) {
-        let app = apps[i];
-        let normalizedAppName =
-          app.manifest.name.replace(/[- ]+/g, '').toLowerCase();
-        if (normalizedSearchName === normalizedAppName) {
-          return app;
-        }
-      }
-      return null;
-    }
-
-    function usageAndDie(justApps) {
-      if (!justApps)
-        dump(
-          'The --runapp argument specifies an app to automatically run at\n'+
-          'startup.  We match against app names per their manifest and \n' +
-          'ignoring capitalization, dashes, and whitespace.\n' +
-          '\nThe system will load as usual except the lock screen will be ' +
-          'automatically be disabled.\n\n' +
-          'Known apps:\n');
-
-      for (let i = 0; i < apps.length; i++) {
-        dump('  ' + apps[i].manifest.name + '\n');
-      }
-
-      // Exit the b2g client
-      Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit);
-    }
-
-    if (this._appName === '') {
-      usageAndDie();
-      return;
-    }
-
-    let appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
-    let currentApp = appsService.getAppByManifestURL(currentFrame.appManifestURL);
-
-    if (!currentApp || currentApp.role !== 'homescreen') {
-      return;
-    }
-
-    let app = findAppWithName(this._appName);
-    if (!app) {
-      dump('Could not find app: "' + this._appName + '". Maybe you meant one of:\n');
-      usageAndDie(true);
-      return;
-    }
-
-    currentFrame.addEventListener('mozbrowserloadend', launchApp);
-
-    function launchApp() {
-      currentFrame.removeEventListener('mozbrowserloadend', launchApp);
-
-      let setReq =
-        navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
-      setReq.onsuccess = function() {
-        // give the event loop 100ms to disable the lock screen
-        window.setTimeout(function() {
-          dump('--runapp launching app: ' + app.manifest.name + '\n');
-          app.launch();
-        }, 100);
-      };
-      setReq.onerror = function() {
-        dump('--runapp failed to disable lock-screen.  Giving up.\n');
-      };
-
-      dump('--runapp found app: ' + app.manifest.name +
-           ', disabling lock screen...\n');
-
-      // Disable observers once we have made the request to launch the app.
-      Services.obs.removeObserver(runAppObj, 'remote-browser-shown');
-      Services.obs.removeObserver(runAppObj, 'inprocess-browser-shown');
-    }
-  },
-
-  getAllError: function() {
-    dump('Problem getting the list of all apps!');
-  }
-};
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -476,56 +476,16 @@ setUpdateTrackingId();
         window.navigator.mozSettings.createLock().set(setting);
       } catch (e) {
         console.log('Unable to read pref layers.low-precision-opacity: ' + e);
       }
     }
   });
 })();
 
-// ================ Theme selection ============
-// theme.selected holds the manifest url of the currently used theme.
-SettingsListener.observe("theme.selected",
-                         "app://default_theme.gaiamobile.org/manifest.webapp",
-                         function(value) {
-  if (!value) {
-    return;
-  }
-
-  let newTheme;
-  try {
-    let enabled = Services.prefs.getBoolPref("dom.mozApps.themable");
-    if (!enabled) {
-      return;
-    }
-
-    // Make sure this is a url, and only keep the host part to set the pref.
-    let uri = Services.io.newURI(value, null, null);
-    // We only support overriding in the app:// protocol handler.
-    if (uri.scheme !== "app") {
-      return;
-    }
-    newTheme = uri.host;
-  } catch(e) {
-    return;
-  }
-
-  let currentTheme;
-  try {
-    currentTheme = Services.prefs.getCharPref('dom.mozApps.selected_theme');
-  } catch(e) {};
-
-  if (currentTheme != newTheme) {
-    debug("New theme selected " + value);
-    Services.prefs.setCharPref('dom.mozApps.selected_theme', newTheme);
-    Services.prefs.savePrefFile(null);
-    Services.obs.notifyObservers(null, 'app-theme-changed', newTheme);
-  }
-});
-
 // =================== Proxy server ======================
 (function setupBrowsingProxySettings() {
   function setPAC() {
     let usePAC;
     try {
       usePAC = Services.prefs.getBoolPref('network.proxy.pac_generator');
     } catch (ex) {}
 
@@ -634,18 +594,16 @@ var settingsToObserve = {
   'devtools.eventlooplag.threshold': 100,
   'devtools.remote.wifi.visible': {
     resetToPref: true
   },
   'devtools.telemetry.supported_performance_marks': {
     resetToPref: true
   },
 
-  'dom.mozApps.use_reviewer_certs': false,
-  'dom.mozApps.signed_apps_installable_from': 'https://marketplace.firefox.com',
   'dom.presentation.discovery.enabled': false,
   'dom.presentation.discoverable': false,
   'dom.serviceWorkers.testing.enabled': false,
   'gfx.layerscope.enabled': false,
   'layers.draw-borders': false,
   'layers.draw-tile-borders': false,
   'layers.dump': false,
   'layers.enable-tiles': AppConstants.platform !== "win",
--- a/b2g/chrome/content/shell.html
+++ b/b2g/chrome/content/shell.html
@@ -30,19 +30,16 @@
 #ifndef ANDROID
 #ifndef MOZ_GRAPHENE
   <!-- various task that has to happen only on desktop -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/desktop.js"> </script>
   <!-- this script handles the screen argument for desktop builds -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/screen.js"> </script>
-  <!-- this script handles the "runapp" argument for desktop builds -->
-  <script type="application/javascript;version=1.8"
-          src="chrome://b2g/content/runapp.js"> </script>
 #endif
 #else
   <!-- this file is only loaded on Gonk to manage ADB state -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/devtools/adb.js"> </script>
 #endif
   <!-- manages DevTools server state -->
   <script type="application/javascript;version=1.8"
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -703,18 +703,16 @@ var shell = {
     this.reportCrash(true);
 
     SystemAppProxy.registerFrame(shell.contentBrowser);
 
     this.sendEvent(window, 'ContentStart');
 
     Services.obs.notifyObservers(null, 'content-start', null);
 
-    isGonk && Cu.import('resource://gre/modules/OperatorApps.jsm');
-
     if (AppConstants.MOZ_GRAPHENE &&
         Services.prefs.getBoolPref("b2g.nativeWindowGeometry.fullscreen")) {
       window.fullScreen = true;
     }
 
     shell.handleCmdLine();
   },
 
@@ -943,19 +941,16 @@ window.addEventListener('ContentStart', 
     } else if (e.detail.type == "delete-crash" && e.detail.crashID) {
       debugCrashReport("deleting crash at user request ", e.detail.crashID);
       shell.deleteCrash(e.detail.crashID);
     }
   });
 });
 
 window.addEventListener('ContentStart', function update_onContentStart() {
-  Cu.import('resource://gre/modules/WebappsUpdater.jsm');
-  WebappsUpdater.handleContentStart(shell);
-
   if (!AppConstants.MOZ_UPDATER) {
     return;
   }
 
   let promptCc = Cc["@mozilla.org/updates/update-prompt;1"];
   if (!promptCc) {
     return;
   }
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -24,17 +24,16 @@ chrome.jar:
   content/devtools/hud.js               (content/devtools/hud.js)
 #ifndef MOZ_WIDGET_GONK
   content/desktop.css                   (content/desktop.css)
   content/images/desktop/home-black.png (content/images/desktop/home-black.png)
   content/images/desktop/home-white.png (content/images/desktop/home-white.png)
   content/images/desktop/rotate.png     (content/images/desktop/rotate.png)
   content/desktop.js                    (content/desktop.js)
   content/screen.js                     (content/screen.js)
-  content/runapp.js                     (content/runapp.js)
 #endif
 * content/content.css                   (content/content.css)
   content/touchcontrols.css             (content/touchcontrols.css)
 
   content/identity.js                   (content/identity.js)
 
 #ifndef MOZ_GRAPHENE
 % override chrome://global/skin/media/videocontrols.css chrome://b2g/content/touchcontrols.css
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -59,21 +59,16 @@ contract @mozilla.org/network/protocol/a
 contract @mozilla.org/network/protocol/about;1?what=neterror {920400b1-cf8f-4760-a9c4-441417b15134}
 
 #ifndef MOZ_GRAPHENE
 # FilePicker.js
 component {436ff8f9-0acc-4b11-8ec7-e293efba3141} FilePicker.js
 contract @mozilla.org/filepicker;1 {436ff8f9-0acc-4b11-8ec7-e293efba3141}
 #endif
 
-# WebappsUpdateTimer.js
-component {637b0f77-2429-49a0-915f-abf5d0db8b9a} WebappsUpdateTimer.js
-contract @mozilla.org/b2g/webapps-update-timer;1 {637b0f77-2429-49a0-915f-abf5d0db8b9a}
-category update-timer WebappsUpdateTimer @mozilla.org/b2g/webapps-update-timer;1,getService,background-update-timer,webapps.update.interval,86400
-
 # FxAccountsUIGlue.js
 component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
 contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
 
 # HelperAppDialog.js
 component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
 contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}
 
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -19,17 +19,16 @@ const PROMPT_FOR_UNKNOWN = ["audio-captu
                             "video-capture"];
 // Due to privary issue, permission requests like GetUserMedia should prompt
 // every time instead of providing session persistence.
 const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"];
 const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
 Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
 var permissionSpecificChecker = {};
--- a/b2g/components/UpdatePrompt.js
+++ b/b2g/components/UpdatePrompt.js
@@ -7,17 +7,16 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/WebappsUpdater.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 const VERBOSE = 1;
 var log =
   VERBOSE ?
   function log_dump(msg) { dump("UpdatePrompt: "+ msg +"\n"); } :
   function log_noop(msg) { };
 
@@ -134,17 +133,16 @@ UpdateCheckListener.prototype = {
     Services.aus.QueryInterface(Ci.nsIUpdateCheckListener);
     Services.aus.onError(request, update);
   }
 };
 
 function UpdatePrompt() {
   this.wrappedJSObject = this;
   this._updateCheckListener = new UpdateCheckListener(this);
-  Services.obs.addObserver(this, "update-check-start", false);
 }
 
 UpdatePrompt.prototype = {
   classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt,
                                          Ci.nsIUpdateCheckListener,
                                          Ci.nsIRequestObserver,
                                          Ci.nsIProgressEventSink,
@@ -636,19 +634,16 @@ UpdatePrompt.prototype = {
       case "idle":
         this._waitingForIdle = false;
         this.showApplyPrompt(this._update);
         // Fall through
       case "quit-application":
         Services.idle.removeIdleObserver(this, this.applyIdleTimeout / 1000);
         Services.obs.removeObserver(this, "quit-application");
         break;
-      case "update-check-start":
-        WebappsUpdater.updateApps();
-        break;
     }
   },
 
   // nsITimerCallback
 
   notify: function UP_notify(aTimer) {
     if (aTimer == this._applyPromptTimer) {
       log("Timed out waiting for result, restarting");
deleted file mode 100644
--- a/b2g/components/WebappsUpdateTimer.js
+++ /dev/null
@@ -1,67 +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 component triggers an app update check even when system updates are
- * disabled to make sure we always check for app updates.
- */
-
-"use strict";
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/WebappsUpdater.jsm");
-
-function debug(aStr) {
-  //dump("--*-- WebappsUpdateTimer: " + aStr);
-}
-
-function WebappsUpdateTimer() {
-}
-
-WebappsUpdateTimer.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback]),
-  classID: Components.ID("{637b0f77-2429-49a0-915f-abf5d0db8b9a}"),
-
-  notify: function(aTimer) {
-    try {
-      // We want to check app updates if system updates are disabled or
-      // if they update frecency is not daily.
-      if (Services.prefs.getBoolPref("app.update.enabled") === true &&
-          Services.prefs.getIntPref("app.update.interval") === 86400) {
-        return;
-      }
-    } catch(e) {
-      // That should never happen..
-    }
-
-    // If we are offline, wait to be online to start the update check.
-    if (Services.io.offline) {
-      debug("Network is offline. Setting up an offline status observer.");
-      Services.obs.addObserver(this, "network:offline-status-changed", false);
-      return;
-    }
-
-    // This will trigger app updates in b2g/components/WebappsUpdater.jsm
-    // that also takes care of notifying gaia.
-    WebappsUpdater.updateApps();
-  },
-
-  observe: function(aSubject, aTopic, aData) {
-    if (aTopic !== "network:offline-status-changed" ||
-        aData !== "online") {
-      return;
-    }
-
-    debug("Network is online. Checking updates.");
-    Services.obs.removeObserver(this, "network:offline-status-changed");
-    WebappsUpdater.updateApps();
-  }
-};
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsUpdateTimer]);
deleted file mode 100644
--- a/b2g/components/WebappsUpdater.jsm
+++ /dev/null
@@ -1,109 +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";
-
-this.EXPORTED_SYMBOLS = ["WebappsUpdater"];
-
-const Cc = Components.classes;
-const Cu = Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "settings",
-                                   "@mozilla.org/settingsService;1",
-                                   "nsISettingsService");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
-                                  "resource://gre/modules/SystemAppProxy.jsm");
-
-function debug(aStr) {
-  //dump("--*-- WebappsUpdater: " + aStr);
-}
-
-this.WebappsUpdater = {
-  _checkingApps: false,
-
-  handleContentStart: function() {
-  },
-
-  sendChromeEvent: function(aType, aDetail) {
-    let detail = aDetail || {};
-    detail.type = aType;
-
-    let sent = SystemAppProxy.dispatchEvent(detail);
-    if (!sent) {
-      debug("Warning: Couldn't send update event " + aType +
-          ": no content browser. Will send again when content becomes available.");
-      return false;
-    }
-
-    return true;
-  },
-
-  _appsUpdated: function(aApps) {
-    debug("appsUpdated: " + aApps.length + " apps to update");
-    let lock = settings.createLock();
-    lock.set("apps.updateStatus", "check-complete", null);
-    this.sendChromeEvent("apps-update-check", { apps: aApps });
-    this._checkingApps = false;
-  },
-
-  // Trigger apps update check and wait for all to be done before
-  // notifying gaia.
-  updateApps: function() {
-    debug("updateApps (" + this._checkingApps + ")");
-    // Don't start twice.
-    if (this._checkingApps) {
-      return;
-    }
-
-    let allowUpdate = true;
-    try {
-      allowUpdate = Services.prefs.getBoolPref("webapps.update.enabled");
-    } catch (ex) { }
-
-    if (!allowUpdate) {
-      return;
-    }
-
-    this._checkingApps = true;
-
-    let self = this;
-
-    let window = Services.wm.getMostRecentWindow("navigator:browser");
-    let all = window.navigator.mozApps.mgmt.getAll();
-
-    all.onsuccess = function() {
-      let appsCount = this.result.length;
-      let appsChecked = 0;
-      let appsToUpdate = [];
-      this.result.forEach(function updateApp(aApp) {
-        let update = aApp.checkForUpdate();
-        update.onsuccess = function() {
-          if (aApp.downloadAvailable) {
-            appsToUpdate.push(aApp.manifestURL);
-          }
-
-          appsChecked += 1;
-          if (appsChecked == appsCount) {
-            self._appsUpdated(appsToUpdate);
-          }
-        }
-        update.onerror = function() {
-          appsChecked += 1;
-          if (appsChecked == appsCount) {
-            self._appsUpdated(appsToUpdate);
-          }
-        }
-      });
-    }
-
-    all.onerror = function() {
-      // Could not get the app list, just notify to update nothing.
-      self._appsUpdated([]);
-    }
-  }
-};
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -18,17 +18,16 @@ EXTRA_COMPONENTS += [
     'HelperAppDialog.js',
     'MailtoProtocolHandler.js',
     'OMAContentHandler.js',
     'PresentationRequestUIGlue.js',
     'ProcessGlobal.js',
     'SmsProtocolHandler.js',
     'SystemMessageInternal.js',
     'TelProtocolHandler.js',
-    'WebappsUpdateTimer.js',
 ]
 
 if CONFIG['OS_TARGET'] != 'Android':
     EXTRA_COMPONENTS += [
       'CommandLine.js',
       'OopCommandLine.js',
       'SimulatorScreen.js'
     ]
@@ -62,17 +61,16 @@ EXTRA_JS_MODULES += [
     'LogParser.jsm',
     'LogShake.jsm',
     'OrientationChangeHandler.jsm',
     'SafeMode.jsm',
     'Screenshot.jsm',
     'SignInToWebsite.jsm',
     'SystemAppProxy.jsm',
     'TelURIParser.jsm',
-    'WebappsUpdater.jsm',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
     EXTRA_JS_MODULES += [
       'GlobalSimulatorScreen.jsm'
     ]
 
 XPIDL_SOURCES += [
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/client.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-  <head>
-    <title>Test app for bug 1171917</title>
-    <script type="application/javascript;version=1.7">
-
-function onLoad() {
-  navigator.serviceWorker.ready.then(() => {
-    parent.postMessage({status: "callback", data: "ready"}, "*");
-  });
-}
-
-    </script>
-  </head>
-  <body onload="onLoad()">
-  </body>
-</html>
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/index.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Test app for bug 1171917</title>
-    <script type="application/javascript;version=1.7">
-
-function ok(aCondition, aMessage) {
-  if (aCondition) {
-    alert("OK: " + aMessage);
-  } else {
-    alert("KO: " + aMessage);
-  }
-}
-
-function done() {
-  alert("DONE");
-}
-
-function testFrame(src) {
-  return new Promise((resolve) => {
-    var iframe = document.createElement("iframe");
-    iframe.src = src;
-    window.onmessage = function(e) {
-      if (e.data.status == "callback") {
-        window.onmessage = null;
-        var result = e.data.data;
-        document.body.removeChild(iframe);
-        iframe.src = "about:blank";
-        iframe = null;
-        resolve(result);
-      }
-    };
-    document.body.appendChild(iframe);
-  });
-}
-
-function registerServiceWorker() {
-  return navigator.serviceWorker.register("sw.sjs", {scope: "."});
-}
-
-function runTests() {
-  var lastSeenVersion;
-  return Promise.resolve()
-    .then(() => {
-      // Check whether the service worker is already registered and save its
-      // version.
-      return navigator.serviceWorker.getRegistration(".").then((swr) => {
-        if (!swr) {
-          lastSeenVersion = 0;
-          return registerServiceWorker();
-        }
-        return testFrame("version.html").then((body) => {
-          lastSeenVersion = parseInt(body);
-          return Promise.resolve();
-        });
-      });
-    })
-    .then(() => {
-      // Wait until the service worker starts controlling the client.
-      return testFrame("client.html");
-    })
-    .then(() => {
-      return new Promise((resolve) => {
-        testFrame("wait_for_update.html").then(() => {
-          // Fetch current version. It should be greater than the last seen version.
-          testFrame("version.html").then((body) => {
-            var currentVersion = parseInt(body);
-            ok(lastSeenVersion < currentVersion, "New service worker version seen");
-            resolve();
-          });
-        });
-      });
-    })
-    .then(done)
-    .catch((e) => {
-      dump("Unexpected error " + e);
-    });
-}
-
-    </script>
-  </head>
-  <body onload="runTests()">
-  </body>
-</html>
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/manifest.webapp
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "name": "App",
-  "launch_path": "/index.html",
-  "description": "Test app for bug 1171917"
-}
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/manifest.webapp^headers^
+++ /dev/null
@@ -1,1 +0,0 @@
-Content-Type: application/manifest+json
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/sw.sjs
+++ /dev/null
@@ -1,28 +0,0 @@
-// The update process does not complete unless the service worker script changes.
-// The service worker script is a server-side JS file which allows us to get a
-// slightly different service worker script file each time the file is fetched.
-// Everytime there is either a registration or an update the script will be
-// fetched and a slightly different version of the script will be returned.
-
-function handleRequest(request, response) {
-  var stateName = request.scheme + "counter";
-  if (!getState(stateName)) {
-    setState(stateName, "1");
-  } else {
-    // Make sure that we pass a string value to setState!
-    setState(stateName, "" + (parseInt(getState(stateName)) + 1));
-  }
-  response.setHeader("Content-Type", "application/javascript", false);
-  response.write(getScript(stateName));
-}
-
-function getScript(stateName) {
-  return "oninstall = function(evt) {" +
-           "evt.waitUntil(self.skipWaiting());" +
-         "}; " +
-         "onfetch = function(evt) {" +
-           "if (evt.request.url.indexOf('get-sw-version') > -1) {" +
-             "evt.respondWith(new Response('" + getState(stateName) + "'));" +
-           "}" +
-         "};";
-}
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/version.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-  <head>
-    <title>Test app for bug 1171917</title>
-    <script type="application/javascript;version=1.7">
-
-function onLoad() {
-  fetch("get-sw-version").then(function(r) {
-    return r.text();
-  }).then(function(body) {
-    parent.postMessage({status: "callback", data: body}, "*");
-  });
-}
-
-    </script>
-  </head>
-  <body onload="onLoad()">
-  </body>
-</html>
deleted file mode 100644
--- a/b2g/components/test/mochitest/app/wait_for_update.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <title>Test app for bug 1171917</title>
-    <script type="application/javascript;version=1.7">
-
-function update() {
-  alert("UPDATE");
-}
-
-function onLoad() {
-  navigator.serviceWorker.getRegistration(".").then(function(swr) {
-    swr.onupdatefound = function() {
-      swr.onupdatefound = null;
-      parent.postMessage({status: "callback", data: "done"}, "*");
-    };
-    update();
-  });
-}
-
-    </script>
-  </head>
-  <body onload="onLoad()">
-  </body>
-</html>
deleted file mode 100644
--- a/b2g/components/test/mochitest/chrome.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[DEFAULT]
-skip-if = toolkit != "gonk"
-support-files =
-  app/*
-
-[test_aboutserviceworkers.html]
--- a/b2g/components/test/mochitest/systemapp_helper.js
+++ b/b2g/components/test/mochitest/systemapp_helper.js
@@ -75,25 +75,16 @@ function listener(event) {
     next(); // call checkEventListening();
   } else {
     assert.ok(false, "Unexpected event of type " + event.type);
   }
 }
 
 
 var steps = [
-  function waitForWebapps() {
-    // We are using webapps API later in this test and we need to ensure
-    // it is fully initialized before trying to use it
-    let { DOMApplicationRegistry } =  Cu.import('resource://gre/modules/Webapps.jsm', {});
-    DOMApplicationRegistry.registryReady.then(function () {
-      next();
-    });
-  },
-
   function earlyEvents() {
     // Immediately try to send events
     SystemAppProxy._sendCustomEvent("mozChromeEvent", { name: "first" }, true);
     SystemAppProxy._sendCustomEvent("custom", { name: "second" }, true);
     next();
   },
 
   function createFrame() {
deleted file mode 100644
--- a/b2g/components/test/mochitest/test_aboutserviceworkers.html
+++ /dev/null
@@ -1,276 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1171917
-
-The tests being added here are about testing the logic underneath the
-about:serviceworkers panel in the settings app. That logic is a B2G dedicate
-component.
-The component let us update and unregister a Service Worker Registration. It
-communicates with content through content/chrome events. We mock them so we can
-send messages for updating/unregistering a particular Service Worker
-Registration.
-
-These tests install an app and the app registers a service worker. Through the
-component described above we update the corresponding Service Worker
-Registration, we test that the Service Worker gets updated and
-then we unregister it.
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1171917</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-  <script type="application/javascript;version=1.7">
-    
-const ASW_CHROME_EVENT = "mozAboutServiceWorkersChromeEvent";
-const ASW_CONTENT_EVENT = "mozAboutServiceWorkersContentEvent";
-
-const { utils: Cu } = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AboutServiceWorkers",
-  "resource://gre/modules/AboutServiceWorkers.jsm");
-
-SimpleTest.waitForExplicitFinish();
-
-const gOrigin = "http://test/chrome/b2g/components/test/mochitest/app";
-const appManifestURL = gOrigin + "/manifest.webapp";
-let gApp;
-
-const ORIGINAL_INIT = AboutServiceWorkers.init;
-const ORIGINAL_SENDRESULT = AboutServiceWorkers.sendResult;
-const ORIGINAL_SENDERROR = AboutServiceWorkers.sendError;
-
-function debug(aMsg) {
-  dump("test_aboutserviceworkers.html - " + aMsg + "\n");
-}
-
-function mockInit() {
-  window.addEventListener(ASW_CONTENT_EVENT, AboutServiceWorkers);
-}
-
-function mockSendResult(aId, aResult) {
-  let result = {
-    detail: {
-      id: aId,
-      result: aResult
-    }
-  };
-  debug("mockSendResult, " + JSON.stringify(result));
-  let event = new CustomEvent(ASW_CHROME_EVENT, result);
-  window.dispatchEvent(event);
-}
-
-function mockSendError(aId, aError) {
-  let error = {
-    detail: {
-      id: aId,
-      error: aError
-    }
-  };
-  debug("mockSendError, " + JSON.stringify(error));
-  let event = new CustomEvent(ASW_CHROME_EVENT, error);
-  window.dispatchEvent(event);
-}
-
-function attachMocks() {
-  AboutServiceWorkers.init = mockInit;
-  AboutServiceWorkers.sendResult = mockSendResult;
-  AboutServiceWorkers.sendError = mockSendError;
-  return Promise.resolve();
-}
-
-function restoreMocks() {
-  AboutServiceWorkers.init = ORIGINAL_INIT;
-  AboutServiceWorkers.sendResult = ORIGINAL_SENDRESULT;
-  AboutServiceWorkers.sendError = ORIGINAL_SENDERROR;
-  return Promise.resolve();
-}
-
-function sendContentEvent(detail) {
-  let event = new CustomEvent(ASW_CONTENT_EVENT, {
-    detail: detail
-  });
-  window.dispatchEvent(event);
-}
-
-function chromeRequest(request) {
-  if (!request) {
-    return Promise.reject("InternalErrorMissingEventDetail");
-  }
-
-  return new Promise((resolve, reject) => {
-    let id = request.id;
-    window.addEventListener(ASW_CHROME_EVENT,
-                            function onChromeEvent(event) {
-      window.removeEventListener(ASW_CHROME_EVENT, onChromeEvent);
-      let message = event.detail;
-      if (!message || !message.id || message.id != id) {
-        return reject('InternalErrorWrongChromeEvent');
-      }
-
-      if (message.error) {
-        reject(message.error);
-      } else if (message.result) {
-        resolve(message.result);
-      }
-    });
-
-    sendContentEvent(request);
-  });
-}
-
-function uninstallApp() {
-  return new Promise((resolve, reject) => {
-    if (!gApp) {
-      return reject();
-    }
-    let req = navigator.mozApps.mgmt.uninstall(gApp);
-    req.onsuccess = resolve;
-    req.onerror = reject;
-  });
-}
-
-function update() {
-  return Promise.resolve()
-    .then(() => {
-      return chromeRequest({id: "0", name: "init"});
-    })
-    .then((result) => {
-      return chromeRequest({
-        id: "1",
-        name: "update",
-        principal: result.registrations[0].principal,
-        scope: result.registrations[0].scope
-      });
-    });
-}
-
-function testApp() {
-  if (!gApp) {
-    ok(false, "No test application to launch");
-    return Promise.reject();
-  }
-  return new Promise((resolve, reject) => {
-    let iframe = document.createElement("iframe");
-    let domParent = document.getElementById("container");
-    iframe.setAttribute("mozbrowser", "true");
-    iframe.setAttribute("mozapp", gApp.manifestURL);
-    iframe.addEventListener("mozbrowsershowmodalprompt", function listener(e) {
-      let message = e.detail.message;
-      if (/KO/.exec(message)) {
-        ok(false, "Message from app: " + message);
-      } else if (/OK/.exec(message)) {
-        ok(true, "Message from app: " + message);
-      } else if (/UPDATE/.exec(message)) {
-        ok(true, "Message from app: " + message);
-        update();
-      } else if (/DONE/.exec(message)) {
-        ok(true, "Message from app: " + message);
-        iframe.src = "about:blank";
-        domParent.removeChild(iframe);
-        iframe = null;
-        resolve();
-      }
-    }, false);
-    domParent.appendChild(iframe);
-    ok(true, "origin " +  gOrigin + gApp.manifest.launch_path);
-    SpecialPowers.wrap(iframe.contentWindow).location =
-      gOrigin + gApp.manifest.launch_path;
-  });
-}
-
-function installApp() {
-  return new Promise((resolve, reject) => {
-    let req = navigator.mozApps.install(appManifestURL);
-    req.onsuccess = function() {
-      gApp = req.result;
-      is(req.result.manifestURL, appManifestURL, "app installed");
-      if (req.result.installState == "installed") {
-        is(req.result.installState, "installed", "app downloaded");
-        resolve()
-      } else {
-        req.result.ondownloadapplied = function() {
-          is(req.result.installState, "installed", "app downloaded");
-          resolve();
-        }
-      }
-    }
-    req.onerror = reject;
-  });
-}
-
-function setup() {
-  info("Setting up");
-  return new Promise((resolve, reject) => {
-    SpecialPowers.pushPrefEnv({"set": [
-      ["dom.mozBrowserFramesEnabled", true],
-      ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-    ]}, () => {
-      SpecialPowers.pushPermissions([
-        { "type": "webapps-manage", "allow": 1, "context": document },
-        { "type": "browser", "allow": 1, "context": document },
-        { "type": "embed-apps", "allow": 1, "context": document }
-      ], () => {
-        SpecialPowers.autoConfirmAppInstall(() => {
-          SpecialPowers.autoConfirmAppUninstall(resolve);
-        });
-      });
-    });
-  });
-}
-
-function cleanUp() {
-  restoreMocks();
-  SimpleTest.finish();
-}
-
-function go() {
-  setup()
-    .then(attachMocks)
-    .then(() => {
-      AboutServiceWorkers.init();
-      return Promise.resolve();
-    })
-    .then(installApp)
-    .then(testApp)
-    .then(() => {
-      return chromeRequest({id: "2", name: "init"});
-    })
-    .then((result) => {
-      return chromeRequest({
-        id: "3",
-        name: "unregister",
-        principal: result.registrations[0].principal,
-        scope: result.registrations[0].scope
-      });
-    })
-    .then(() => {
-      return chromeRequest({id: "4", name: "init"});
-    })
-    .then((result) => {
-      ok(result.registrations && result.registrations.length === 0,
-         "Service worker registration was successfuly unregistered");
-    })
-    .then(uninstallApp)
-    .then(cleanUp)
-    .catch((e) => {
-      ok(false, "Unexpected error " + e);
-      cleanUp();
-    });
-}
-  </script>
-</head>
-<body onload="go()">
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1171917">Mozilla Bug 1171917</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-</pre>
-<div id="container"></div>
-</body>
-</html>
--- a/b2g/components/test/moz.build
+++ b/b2g/components/test/moz.build
@@ -1,9 +1,8 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
-MOCHITEST_CHROME_MANIFESTS += ['mochitest/chrome.ini']
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -590,18 +590,16 @@
 @RESPATH@/components/servicesComponents.manifest
 @RESPATH@/components/cryptoComponents.manifest
 @RESPATH@/components/CaptivePortalDetectComponents.manifest
 @RESPATH@/components/captivedetect.js
 @RESPATH@/components/TelemetryStartup.js
 @RESPATH@/components/TelemetryStartup.manifest
 @RESPATH@/components/XULStore.js
 @RESPATH@/components/XULStore.manifest
-@RESPATH@/components/Webapps.js
-@RESPATH@/components/Webapps.manifest
 @RESPATH@/components/AppsService.js
 @RESPATH@/components/AppsService.manifest
 @RESPATH@/components/Push.js
 @RESPATH@/components/Push.manifest
 @RESPATH@/components/PushComponents.js
 
 @RESPATH@/components/nsDOMIdentity.js
 @RESPATH@/components/nsIDService.js
@@ -851,17 +849,16 @@ bin/libfreebl_32int64_3.so
 @RESPATH@/components/MarionetteComponents.manifest
 @RESPATH@/components/marionettecomponent.js
 #endif
 @RESPATH@/components/AlertsService.js
 @RESPATH@/components/ContentPermissionPrompt.js
 #ifdef MOZ_UPDATER
 @RESPATH@/components/UpdatePrompt.js
 #endif
-@RESPATH@/components/WebappsUpdateTimer.js
 @RESPATH@/components/DirectoryProvider.js
 @RESPATH@/components/ProcessGlobal.js
 @RESPATH@/components/OMAContentHandler.js
 @RESPATH@/components/RecoveryService.js
 @RESPATH@/components/MailtoProtocolHandler.js
 @RESPATH@/components/SmsProtocolHandler.js
 @RESPATH@/components/TelProtocolHandler.js
 @RESPATH@/components/B2GAboutRedirector.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1412,19 +1412,28 @@ var BookmarkingUI = {
       // Update the menu when a bookmark has been removed.
       // The native menubar on Mac doesn't support live update, so this won't
       // work there.
       this._populateRecentBookmarks(aHeaderItem, aExtraCSSClass);
     };
 
     let updatePlacesContextMenu = (shouldHidePrefUI = false) => {
       let prefEnabled = !shouldHidePrefUI && Services.prefs.getBoolPref(this.RECENTLY_BOOKMARKED_PREF);
-      document.getElementById("placesContext_showRecentlyBookmarked").hidden = shouldHidePrefUI || prefEnabled;
-      document.getElementById("placesContext_hideRecentlyBookmarked").hidden = shouldHidePrefUI || !prefEnabled;
-      document.getElementById("placesContext_recentlyBookmarkedSeparator").hidden = shouldHidePrefUI;
+      let showItem = document.getElementById("placesContext_showRecentlyBookmarked");
+      let hideItem = document.getElementById("placesContext_hideRecentlyBookmarked");
+      let separator = document.getElementById("placesContext_recentlyBookmarkedSeparator");
+      showItem.hidden = shouldHidePrefUI || prefEnabled;
+      hideItem.hidden = shouldHidePrefUI || !prefEnabled;
+      separator.hidden = shouldHidePrefUI;
+      if (!shouldHidePrefUI) {
+        // Move to the bottom of the menu.
+        separator.parentNode.appendChild(separator);
+        showItem.parentNode.appendChild(showItem);
+        hideItem.parentNode.appendChild(hideItem);
+      }
     };
 
     let onPlacesContextMenuShowing = event => {
       if (event.target == event.currentTarget) {
         let triggerPopup = event.target.triggerNode;
         while (triggerPopup && triggerPopup.localName != "menupopup") {
           triggerPopup = triggerPopup.parentNode;
         }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -356,33 +356,30 @@
                               gContextMenu = null;
                               updateEditUIVisibility();">
 #include browser-context.inc
     </menupopup>
 
     <menupopup id="placesContext">
       <menuseparator id="placesContext_recentlyBookmarkedSeparator"
                      ignoreitem="true"
-                     ordinal="2"
                      hidden="true"/>
       <menuitem id="placesContext_hideRecentlyBookmarked"
                 label="&hideRecentlyBookmarked.label;"
                 accesskey="&hideRecentlyBookmarked.accesskey;"
                 oncommand="BookmarkingUI.hideRecentlyBookmarked();"
                 closemenu="single"
                 ignoreitem="true"
-                ordinal="2"
                 hidden="true"/>
       <menuitem id="placesContext_showRecentlyBookmarked"
                 label="&showRecentlyBookmarked.label;"
                 accesskey="&showRecentlyBookmarked.accesskey;"
                 oncommand="BookmarkingUI.showRecentlyBookmarked();"
                 closemenu="single"
                 ignoreitem="true"
-                ordinal="2"
                 hidden="true"/>
     </menupopup>
 
     <panel id="ctrlTab-panel" hidden="true" norestorefocus="true" level="top">
       <hbox>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -6448,17 +6448,17 @@
                     class="tab-background-end"/>
         </xul:hbox>
         <xul:hbox xbl:inherits="pinned,selected=visuallyselected,titlechanged,attention"
                   class="tab-content" align="center">
           <xul:image xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected"
                      class="tab-throbber"
                      role="presentation"
                      layer="true" />
-          <xul:image xbl:inherits="src=image,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
+          <xul:image xbl:inherits="src=image,loadingprincipal=iconLoadingPrincipal,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
                      anonid="tab-icon-image"
                      class="tab-icon-image"
                      validate="never"
                      role="presentation"/>
           <xul:image xbl:inherits="sharing,selected=visuallyselected"
                      anonid="sharing-icon"
                      class="tab-sharing-icon-overlay"
                      role="presentation"/>
--- a/browser/components/contextualidentity/test/browser/browser_favicon.js
+++ b/browser/components/contextualidentity/test/browser/browser_favicon.js
@@ -17,17 +17,17 @@ let gHttpServer = null;
 let gUserContextId;
 let gFaviconData;
 
 function getIconFile() {
   new Promise(resolve => {
     NetUtil.asyncFetch({
       uri: "http://www.example.com/browser/browser/components/contextualidentity/test/browser/favicon-normal32.png",
       loadUsingSystemPrincipal: true,
-      contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
+      contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
     }, function(inputStream, status) {
         let size = inputStream.available();
         gFaviconData = NetUtil.readInputStreamToString(inputStream, size);
         resolve();
     });
   });
 }
 
--- a/browser/components/downloads/content/download.xml
+++ b/browser/components/downloads/content/download.xml
@@ -11,49 +11,53 @@
 <bindings id="downloadBindings"
           xmlns="http://www.mozilla.org/xbl"
           xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="download"
            extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
     <content orient="horizontal"
-             align="center"
              onclick="DownloadsView.onDownloadClick(event);">
-      <xul:stack>
-        <xul:image class="downloadTypeIcon"
-                   validate="always"
-                   xbl:inherits="src=image"/>
-        <xul:image class="downloadBlockedBadge" />
-      </xul:stack>
-      <xul:vbox pack="center"
+      <xul:hbox class="downloadMainArea"
                 flex="1"
-                class="downloadContainer"
-                style="width: &downloadDetails.width;">
-        <!-- We're letting localizers put a min-width in here primarily
-             because of the downloads summary at the bottom of the list of
-             download items. An element in the summary has the same min-width
-             on a description, and we don't want the panel to change size if the
-             summary isn't being displayed, so we ensure that items share the
-             same minimum width.
-             -->
-        <xul:description class="downloadTarget"
-                         crop="center"
-                         style="min-width: &downloadsSummary.minWidth2;"
-                         xbl:inherits="value=displayName,tooltiptext=displayName"/>
-        <xul:progressmeter anonid="progressmeter"
-                           class="downloadProgress"
-                           min="0"
-                           max="100"
-                           xbl:inherits="mode=progressmode,value=progress"/>
-        <xul:description class="downloadDetails"
-                         crop="end"
-                         xbl:inherits="value=status,tooltiptext=statusTip"/>
-      </xul:vbox>
-      <xul:stack>
+                align="center">
+        <xul:stack>
+          <xul:image class="downloadTypeIcon"
+                     validate="always"
+                     xbl:inherits="src=image"/>
+          <xul:image class="downloadBlockedBadge" />
+        </xul:stack>
+        <xul:vbox pack="center"
+                  flex="1"
+                  class="downloadContainer"
+                  style="width: &downloadDetails.width;">
+          <!-- We're letting localizers put a min-width in here primarily
+               because of the downloads summary at the bottom of the list of
+               download items. An element in the summary has the same min-width
+               on a description, and we don't want the panel to change size if the
+               summary isn't being displayed, so we ensure that items share the
+               same minimum width.
+               -->
+          <xul:description class="downloadTarget"
+                           crop="center"
+                           style="min-width: &downloadsSummary.minWidth2;"
+                           xbl:inherits="value=displayName,tooltiptext=displayName"/>
+          <xul:progressmeter anonid="progressmeter"
+                             class="downloadProgress"
+                             min="0"
+                             max="100"
+                             xbl:inherits="mode=progressmode,value=progress"/>
+          <xul:description class="downloadDetails"
+                           crop="end"
+                           xbl:inherits="value=status,tooltiptext=statusTip"/>
+        </xul:vbox>
+      </xul:hbox>
+      <xul:toolbarseparator />
+      <xul:stack class="downloadButtonArea">
         <xul:button class="downloadButton downloadCancel downloadIconCancel"
                     tooltiptext="&cmd.cancel.label;"
                     oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_cancel');"/>
         <xul:button class="downloadButton downloadRetry downloadIconRetry"
                     tooltiptext="&cmd.retry.label;"
                     oncommand="DownloadsView.onDownloadCommand(event, 'downloadsCmd_retry');"/>
         <xul:button class="downloadButton downloadShow downloadIconShow"
 #ifdef XP_MACOSX
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -13,17 +13,18 @@ richlistitem[type="download"]:not([selec
   -moz-user-focus: none;
 }
 
 .downloadsHideDropmarker > #downloadsFooterButtonsSplitter,
 .downloadsHideDropmarker > #downloadsFooterDropmarker {
   display: none;
 }
 
-richlistitem[type="download"].download-state[state="1"]:not([exists]) .downloadShow {
+richlistitem[type="download"].download-state[state="1"]:not([exists]) > .downloadButtonArea,
+richlistitem[type="download"].download-state[state="1"]:not([exists]) > toolbarseparator {
   display: none;
 }
 
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress,
 #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails,
 #downloadsFooter:not([showingsummary]) #downloadsSummary {
   display: none;
 }
@@ -157,16 +158,21 @@ richlistitem.download button {
 
 .download-state:not(:-moz-any([state="2"], /* Failed             */
                               [state="3"]) /* Canceled           */)
                                            .downloadRetry,
 
 .download-state:not(          [state="1"]  /* Finished           */)
                                            .downloadShow,
 
+.download-state:-moz-any(     [state="6"], /* Blocked (parental) */
+                              [state="7"], /* Scanning           */
+                              [state="9"]) /* Blocked (policy)   */
+                                           > toolbarseparator,
+
 /* The "show blocked info" button is shown only in the downloads panel. */
 .downloadShowBlockedInfo
 {
   display: none;
 }
 
 /*** Downloads panel ***/
 
@@ -212,20 +218,17 @@ richlistitem.download button {
   transform: translateX(-101%);
 }
 
 /** When the subview is showing... **/
 
 /* Hide the buttons of all downloads except the one that triggered the
    subview. */
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state:not([showingsubview]) .downloadButton {
-  display: none;
-}
-#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton {
-  display: inline;
+  visibility: hidden;
 }
 
 /* For the download that triggered the subview, move its button farther to the
    right by removing padding so that a minimum amount of the main view's right
    edge needs to be shown. */
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] {
   padding: 0;
 }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -664,19 +664,16 @@ BrowserGlue.prototype = {
 
     // apply distribution customizations
     // prefs are applied in _onAppDefaults()
     this._distributionCustomizer.applyCustomizations();
 
     // handle any UI migration
     this._migrateUI();
 
-    // Evaluate Webapps.jsm early to resolve ts_paint regression bug 1256667.
-    Cu.import("resource://gre/modules/Webapps.jsm", {});
-
     PageThumbs.init();
     webrtcUI.init();
     AboutHome.init();
 
     DirectoryLinksProvider.init();
     NewTabUtils.init();
     NewTabUtils.links.addProvider(DirectoryLinksProvider);
     AboutNewTab.init();
--- a/browser/components/originattributes/test/browser/browser.ini
+++ b/browser/components/originattributes/test/browser/browser.ini
@@ -1,13 +1,19 @@
 [DEFAULT]
 skip-if = buildapp == "mulet"
 tags = usercontextid firstpartyisolation originattributes
 support-files =
   dummy.html
+  file_favicon.html
+  file_favicon.png
+  file_favicon.png^headers^
+  file_favicon_cache.html
+  file_favicon_cache.png
+  file_favicon_thirdParty.html
   file_firstPartyBasic.html
   file_sharedworker.html
   file_sharedworker.js
   head.js
   test.js
   test.js^headers^
   test.html
   test2.html
@@ -19,14 +25,16 @@ support-files =
   test_firstParty_http_redirect.html
   test_firstParty_http_redirect.html^headers^
   test_firstParty_iframe_http_redirect.html
   test_firstParty_postMessage.html
   window.html
   worker_blobify.js
   worker_deblobify.js
 
+[browser_favicon_firstParty.js]
+[browser_favicon_userContextId.js]
 [browser_firstPartyIsolation.js]
 [browser_localStorageIsolation.js]
 [browser_blobURLIsolation.js]
 [browser_imageCacheIsolation.js]
 [browser_sharedworker.js]
 [browser_httpauth.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
@@ -0,0 +1,340 @@
+/**
+ * Bug 1277803 - A test case for testing favicon loading across different first party domains.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+
+const FIRST_PARTY_ONE = "example.com";
+const FIRST_PARTY_TWO = "example.org";
+const THIRD_PARTY = "mochi.test:8888";
+
+const TEST_SITE_ONE = "http://" + FIRST_PARTY_ONE;
+const TEST_SITE_TWO = "http://" + FIRST_PARTY_TWO;
+const THIRD_PARTY_SITE = "http://" + THIRD_PARTY;
+const TEST_DIRECTORY = "/browser/browser/components/originattributes/test/browser/";
+
+const TEST_PAGE = TEST_DIRECTORY + "file_favicon.html";
+const TEST_THIRD_PARTY_PAGE = TEST_DIRECTORY + "file_favicon_thirdParty.html";
+const TEST_CACHE_PAGE = TEST_DIRECTORY + "file_favicon_cache.html";
+
+const FAVICON_URI = TEST_DIRECTORY + "file_favicon.png";
+const TEST_FAVICON_CACHE_URI = TEST_DIRECTORY + "file_favicon_cache.png";
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+  let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  let imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+  let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
+                          .getService(Ci.nsIFaviconService);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "places-favicons-expired") {
+          resolve();
+          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "places-favicons-expired", false);
+    faviconService.expireAllFavicons();
+  });
+}
+
+function observeFavicon(aFirstPartyDomain, aExpectedCookie, aPageURI) {
+  let faviconReqXUL = false;
+  let faviconReqPlaces = false;
+  let expectedPrincipal = Services.scriptSecurityManager
+                                  .createCodebasePrincipal(aPageURI, { firstPartyDomain: aFirstPartyDomain });
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        // Make sure that the topic is 'http-on-modify-request'.
+        if (aTopic === "http-on-modify-request") {
+          // We check the firstPartyDomain for the originAttributes of the loading
+          // channel. All requests for the favicon should contain the correct
+          // firstPartyDomain. There are two requests for a favicon loading, one
+          // from the Places library and one from the XUL image. The difference
+          // of them is the loading principal. The Places will use the content
+          // principal and the XUL image will use the system principal.
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let reqLoadInfo = httpChannel.loadInfo;
+          let loadingPrincipal = reqLoadInfo.loadingPrincipal;
+          let triggeringPrincipal = reqLoadInfo.triggeringPrincipal;
+
+          // Make sure this is a favicon request.
+          if (!httpChannel.URI.spec.endsWith(FAVICON_URI)) {
+            return;
+          }
+
+          // Check the first party domain.
+          is(reqLoadInfo.originAttributes.firstPartyDomain, aFirstPartyDomain,
+            "The loadInfo has correct first party domain");
+
+          if (loadingPrincipal.equals(systemPrincipal)) {
+            faviconReqXUL = true;
+            ok(triggeringPrincipal.equals(expectedPrincipal),
+              "The triggeringPrincipal of favicon loading from XUL should be the content principal.");
+          } else {
+            faviconReqPlaces = true;
+            ok(loadingPrincipal.equals(expectedPrincipal),
+              "The loadingPrincipal of favicon loading from Places should be the content prinicpal");
+          }
+
+          let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+          is(faviconCookie, aExpectedCookie, "The cookie of the favicon loading is correct.");
+        } else {
+          ok(false, "Received unexpected topic: ", aTopic);
+        }
+
+        if (faviconReqXUL && faviconReqPlaces) {
+          Services.obs.removeObserver(observer, "http-on-modify-request", false);
+          resolve();
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-modify-request", false);
+  });
+}
+
+function waitOnFaviconResponse(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "http-on-examine-response" ||
+            aTopic === "http-on-examine-cached-response") {
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let loadInfo = httpChannel.loadInfo;
+
+          if (httpChannel.URI.spec !== aFaviconURL) {
+            return;
+          }
+
+          let result = {
+            topic: aTopic,
+            firstPartyDomain: loadInfo.originAttributes.firstPartyDomain
+          };
+
+          resolve(result);
+          Services.obs.removeObserver(observer, "http-on-examine-response", false);
+          Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-examine-response", false);
+    Services.obs.addObserver(observer, "http-on-examine-cached-response", false);
+  });
+}
+
+function waitOnFaviconLoaded(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      onPageChanged(uri, attr, value, id) {
+
+        if (attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON &&
+            value === aFaviconURL) {
+          resolve();
+          PlacesUtils.history.removeObserver(observer, false);
+        }
+      },
+    };
+
+    PlacesUtils.history.addObserver(observer, false);
+  });
+}
+
+function* openTab(aURL) {
+  let tab = gBrowser.addTab(aURL);
+
+  // Select tab and make sure its browser is focused.
+  gBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+function* assignCookiesUnderFirstParty(aURL, aFirstParty, aCookieValue) {
+  // Open a tab under the given aFirstParty, and this tab will have an
+  // iframe which loads the aURL.
+  let tabInfo = yield openTabInFirstParty(aURL, aFirstParty);
+
+  // Add cookies into the iframe.
+  yield ContentTask.spawn(tabInfo.browser, aCookieValue, function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+function* generateCookies(aThirdParty) {
+  // we generate two different cookies for two first party domains.
+  let cookies = [];
+  cookies.push(Math.random().toString());
+  cookies.push(Math.random().toString());
+
+  let firstSiteURL;
+  let secondSiteURL;
+
+  if (aThirdParty) {
+    // Add cookies into the third party site with different first party domain.
+    firstSiteURL = THIRD_PARTY_SITE;
+    secondSiteURL = THIRD_PARTY_SITE;
+  } else {
+    // Add cookies into sites.
+    firstSiteURL = TEST_SITE_ONE;
+    secondSiteURL = TEST_SITE_TWO;
+  }
+
+  yield assignCookiesUnderFirstParty(firstSiteURL, TEST_SITE_ONE, cookies[0]);
+  yield assignCookiesUnderFirstParty(secondSiteURL, TEST_SITE_TWO, cookies[1]);
+
+  return cookies;
+}
+
+function* doTest(aTestPage, aExpectedCookies, aFaviconURL) {
+  let firstPageURI = makeURI(TEST_SITE_ONE + aTestPage);
+  let secondPageURI = makeURI(TEST_SITE_TWO + aTestPage);
+
+  // Start to observe the event of that favicon has been fully loaded.
+  let promiseFaviconLoaded = waitOnFaviconLoaded(aFaviconURL);
+
+  // Start to observe the favicon requests earlier in case we miss it.
+  let promiseObserveFavicon = observeFavicon(FIRST_PARTY_ONE, aExpectedCookies[0], firstPageURI);
+
+  // Open the tab for the first site.
+  let tabInfo = yield openTab(TEST_SITE_ONE + aTestPage);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  // Waiting until favicon loaded.
+  yield promiseFaviconLoaded;
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+
+  // Start to observe the favicon requests earlier in case we miss it.
+  promiseObserveFavicon = observeFavicon(FIRST_PARTY_TWO, aExpectedCookies[1], secondPageURI);
+
+  // Open the tab for the second site.
+  tabInfo = yield openTab(TEST_SITE_TWO + aTestPage);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+add_task(function* setup() {
+  // Make sure first party isolation is enabled.
+  yield SpecialPowers.pushPrefEnv({"set": [
+      ["privacy.firstparty.isolate", true]
+  ]});
+});
+
+// A clean up function to prevent affecting other tests.
+registerCleanupFunction(() => {
+  // Clear all cookies.
+  let cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
+                     .getService(Ci.nsICookieManager);
+  cookieMgr.removeAll();
+
+  // Clear all image caches and network caches.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+});
+
+add_task(function* test_favicon_firstParty() {
+  for (let testThirdParty of [false, true]) {
+    // Clear all image caches and network caches before running the test.
+    clearAllImageCaches();
+
+    let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+    networkCache.clear();
+
+    // Clear Places favicon caches.
+    yield clearAllPlacesFavicons();
+
+    let cookies = yield generateCookies(testThirdParty);
+
+    if (testThirdParty) {
+      yield doTest(TEST_THIRD_PARTY_PAGE, cookies, THIRD_PARTY_SITE + FAVICON_URI);
+    } else {
+      yield doTest(TEST_PAGE, cookies, TEST_SITE_ONE + FAVICON_URI);
+    }
+  }
+});
+
+add_task(function* test_favicon_cache_firstParty() {
+  // Clear all image caches and network caches before running the test.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Open the tab for the first site.
+  let tabInfoA = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
+
+  // Start to observer the event of that favicon has been fully loaded and cached.
+  let promiseForFaviconLoaded = waitOnFaviconLoaded(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Wait for the favicon response of the first tab.
+  let response = yield waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Make sure the favicon is loaded through the network and its first party domain is correct.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network.");
+  is(response.firstPartyDomain, FIRST_PARTY_ONE, "We should only observe the network response for the first first party.");
+
+  // Waiting until the favicon has been loaded and cached.
+  yield promiseForFaviconLoaded;
+
+  // Open the tab again for checking the image cache is working correctly.
+  let tabInfoB = yield openTab(TEST_SITE_ONE + TEST_CACHE_PAGE);
+
+  // Start to observe the favicon response, the second tab actually will not
+  // make any network request since the favicon will be loaded by the cache for
+  // both Places and XUL image. So here, we are going to observe the favicon
+  // response for the third tab which opens with the second first party.
+  let promiseForFaviconResponse = waitOnFaviconResponse(THIRD_PARTY_SITE + TEST_FAVICON_CACHE_URI);
+
+  // Open the tab for the second site.
+  let tabInfoC = yield openTab(TEST_SITE_TWO + TEST_CACHE_PAGE);
+
+  // Wait for the favicon response. In this case, we suppose to catch the
+  // response for the third tab but not the second tab since it will not
+  // go through the network.
+  response = yield promiseForFaviconResponse;
+
+  // Check that the favicon response has came from the network and it has the
+  // correct first party domain.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network again.");
+  is(response.firstPartyDomain, FIRST_PARTY_TWO, "We should only observe the network response for the second first party.");
+
+  yield BrowserTestUtils.removeTab(tabInfoA.tab);
+  yield BrowserTestUtils.removeTab(tabInfoB.tab);
+  yield BrowserTestUtils.removeTab(tabInfoC.tab);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
@@ -0,0 +1,250 @@
+/**
+ * Bug 1277803 - A test caes for testing favicon loading across different userContextId.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+  "resource://gre/modules/Promise.jsm");
+
+const TEST_SITE = "http://mochi.test:8888";
+
+const TEST_PAGE = TEST_SITE + "/browser/browser/components/originattributes/" +
+                  "test/browser/file_favicon.html";
+const FAVICON_URI = TEST_SITE + "/browser/browser/components/originattributes/" +
+                    "test/browser/file_favicon.png";
+const TEST_THIRD_PARTY_PAGE = "http://example.com/browser/browser/components/" +
+                              "originattributes/test/browser/file_favicon_thirdParty.html";
+
+const USER_CONTEXT_ID_PERSONAL = 1;
+const USER_CONTEXT_ID_WORK     = 2;
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+  var tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  var imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+  let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
+                          .getService(Ci.nsIFaviconService);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "places-favicons-expired") {
+          resolve();
+          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "places-favicons-expired", false);
+    faviconService.expireAllFavicons();
+  });
+}
+
+function FaviconObserver(aUserContextId, aExpectedCookie, aPageURI) {
+  this.reset(aUserContextId, aExpectedCookie, aPageURI);
+}
+
+FaviconObserver.prototype = {
+  observe(aSubject, aTopic, aData) {
+    // Make sure that the topic is 'http-on-modify-request'.
+    if (aTopic === "http-on-modify-request") {
+      // We check the userContextId for the originAttributes of the loading
+      // channel. All requests for the favicon should contain the correct
+      // userContextId. There are two requests for a favicon loading, one
+      // from the Places library and one from the XUL image. The difference
+      // of them is the loading principal. The Places will use the content
+      // principal and the XUL image will use the system principal.
+
+      let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+      let reqLoadInfo = httpChannel.loadInfo;
+      let loadingPrincipal;
+      let triggeringPrincipal;
+
+      // Make sure this is a favicon request.
+      if (httpChannel.URI.spec !== FAVICON_URI) {
+        return;
+      }
+
+      if (reqLoadInfo) {
+        loadingPrincipal = reqLoadInfo.loadingPrincipal;
+        triggeringPrincipal = reqLoadInfo.triggeringPrincipal;
+      }
+
+      // Check the userContextId.
+      is(reqLoadInfo.originAttributes.userContextId, this._curUserContextId,
+        "The loadInfo has correct userContextId");
+
+      if (loadingPrincipal.equals(systemPrincipal)) {
+        this._faviconReqXUL = true;
+        ok(triggeringPrincipal.equals(this._expectedPrincipal),
+          "The triggeringPrincipal of favicon loading from XUL should be the content principal.");
+      } else {
+        this._faviconReqPlaces = true;
+        ok(loadingPrincipal.equals(this._expectedPrincipal),
+          "The loadingPrincipal of favicon loading from Places should be the content prinicpal");
+      }
+
+      let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+      is(faviconCookie, this._expectedCookie, "The cookie of the favicon loading is correct.");
+    } else {
+      ok(false, "Received unexpected topic: ", aTopic);
+    }
+
+    if (this._faviconReqXUL && this._faviconReqPlaces) {
+      this._faviconLoaded.resolve();
+    }
+  },
+
+  reset(aUserContextId, aExpectedCookie, aPageURI) {
+    this._curUserContextId = aUserContextId;
+    this._expectedCookie = aExpectedCookie;
+    this._expectedPrincipal = Services.scriptSecurityManager
+                                      .createCodebasePrincipal(aPageURI, { userContextId: aUserContextId });
+    this._faviconReqXUL = false;
+    this._faviconReqPlaces = false;
+    this._faviconLoaded = new Promise.defer();
+  },
+
+  get promise() {
+    return this._faviconLoaded.promise;
+  }
+};
+
+function waitOnFaviconLoaded(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      onPageChanged(uri, attr, value, id) {
+
+        if (attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON &&
+            value === aFaviconURL) {
+          resolve();
+          PlacesUtils.history.removeObserver(observer, false);
+        }
+      },
+    };
+
+    PlacesUtils.history.addObserver(observer, false);
+  });
+}
+
+function* generateCookies() {
+  // we generate two different cookies for two userContextIds.
+  let cookies = [];
+  cookies.push(Math.random().toString());
+  cookies.push(Math.random().toString());
+
+  // Then, we add cookies into the site for 'personal' and 'work'.
+  let tabInfoA = yield openTabInUserContext(TEST_SITE, USER_CONTEXT_ID_PERSONAL);
+  let tabInfoB = yield openTabInUserContext(TEST_SITE, USER_CONTEXT_ID_WORK);
+
+  yield ContentTask.spawn(tabInfoA.browser, cookies[0], function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield ContentTask.spawn(tabInfoB.browser, cookies[1], function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield BrowserTestUtils.removeTab(tabInfoA.tab);
+  yield BrowserTestUtils.removeTab(tabInfoB.tab);
+
+  return cookies;
+}
+
+function* doTest(aTestPage) {
+  let cookies = yield generateCookies();
+  let pageURI = makeURI(aTestPage);
+
+  // Create the observer object for observing request channels of the personal
+  // container.
+  let observer = new FaviconObserver(USER_CONTEXT_ID_PERSONAL, cookies[0], pageURI);
+
+  Services.obs.addObserver(observer, "http-on-modify-request", false);
+
+  // Open the tab with the personal container.
+  let tabInfo = yield openTabInUserContext(aTestPage, USER_CONTEXT_ID_PERSONAL);
+
+  // Waiting for favicon requests are all made.
+  yield observer.promise;
+  // Waiting for favicon loaded.
+  yield waitOnFaviconLoaded(FAVICON_URI);
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+
+  // Reset the observer for observing requests for the work container.
+  observer.reset(USER_CONTEXT_ID_WORK, cookies[1], pageURI);
+  tabInfo = yield openTabInUserContext(aTestPage, USER_CONTEXT_ID_WORK);
+
+  // Waiting for favicon requests are all made.
+  yield observer.promise;
+
+  Services.obs.removeObserver(observer, "http-on-modify-request", false);
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+add_task(function* setup() {
+  // Make sure userContext is enabled.
+  yield SpecialPowers.pushPrefEnv({"set": [
+      ["privacy.userContext.enabled", true]
+  ]});
+});
+
+// A clean up function to prevent affecting other tests.
+registerCleanupFunction(() => {
+  // Clear all cookies.
+  let cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
+                     .getService(Ci.nsICookieManager);
+  cookieMgr.removeAll();
+
+  // Clear all image caches and network caches.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  clearAllPlacesFavicons();
+});
+
+add_task(function* test_favicon_userContextId() {
+  // Clear all image caches before running the test.
+  clearAllImageCaches();
+
+  // Clear all network caches.
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  yield clearAllPlacesFavicons();
+
+  yield doTest(TEST_PAGE);
+});
+
+add_task(function* test_thirdPartyFavicon_userContextId() {
+  // Clear all image caches before running the test.
+  clearAllImageCaches();
+
+  // Clear all network caches.
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear Places favicon caches.
+  yield clearAllPlacesFavicons();
+
+  yield doTest(TEST_THIRD_PARTY_PAGE);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="file_favicon.png" />
+  </head>
+  <body>
+    Favicon!!
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5535363c94df7314765551f65311ec6f98729d8e
GIT binary patch
literal 344
zc$@)P0jK_nP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003WNkl<ZSi@ud
z|NsAgh5-aj3<C^C9l%0R#sBwE#yKee29*B}N^?LcMg=I}1WL<7#aV`G2>ibbr58i#
zeNc((P#P98>`-}V2r!yL=>({_1(eS*I75IDqa1+6`Tu4pJs&FZfkdNSpkC~N^7XN%
ziNTl#4nlptmQ0_+z#gbM8_{x!@L&&t|69@WIcn7X|AQkj;gW}$#~3qYLI76MeMc4k
zL!iLKDUaQ}p<fv>vJfohhiYxer~!@C3s8YCB*r;9fSSj^Ib=e>8|uYfC?P<0IRG;c
zE&mVZy1*Xl@?ezO@c*9=cpEsFAp@-q8U3Mr{vneF1fh&<D9wk`BKrRaM~jF7M~jF-
q8*kfi5VnX$TC13Gv~M`#9RL8-wO_*zBf!o80000<MNUMnLSTX;M3C|T
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon.png^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon_cache.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="http://mochi.test:8888/browser/browser/components/originattributes/test/browser/file_favicon_cache.png" />
+  </head>
+  <body>
+    Third Party Favicon!!
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5535363c94df7314765551f65311ec6f98729d8e
GIT binary patch
literal 344
zc$@)P0jK_nP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003WNkl<ZSi@ud
z|NsAgh5-aj3<C^C9l%0R#sBwE#yKee29*B}N^?LcMg=I}1WL<7#aV`G2>ibbr58i#
zeNc((P#P98>`-}V2r!yL=>({_1(eS*I75IDqa1+6`Tu4pJs&FZfkdNSpkC~N^7XN%
ziNTl#4nlptmQ0_+z#gbM8_{x!@L&&t|69@WIcn7X|AQkj;gW}$#~3qYLI76MeMc4k
zL!iLKDUaQ}p<fv>vJfohhiYxer~!@C3s8YCB*r;9fSSj^Ib=e>8|uYfC?P<0IRG;c
zE&mVZy1*Xl@?ezO@c*9=cpEsFAp@-q8U3Mr{vneF1fh&<D9wk`BKrRaM~jF7M~jF-
q8*kfi5VnX$TC13Gv~M`#9RL8-wO_*zBf!o80000<MNUMnLSTX;M3C|T
new file mode 100644
--- /dev/null
+++ b/browser/components/originattributes/test/browser/file_favicon_thirdParty.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="http://mochi.test:8888/browser/browser/components/originattributes/test/browser/file_favicon.png" />
+  </head>
+  <body>
+    Third Party Favicon!!
+  </body>
+</html>
\ No newline at end of file
--- a/browser/components/privatebrowsing/test/browser/browser.ini
+++ b/browser/components/privatebrowsing/test/browser/browser.ini
@@ -10,30 +10,34 @@ support-files =
   browser_privatebrowsing_localStorage_page2.html
   browser_privatebrowsing_placesTitleNoUpdate.html
   browser_privatebrowsing_protocolhandler_page.html
   browser_privatebrowsing_windowtitle_page.html
   head.js
   popup.html
   title.sjs
   empty_file.html
+  file_favicon.html
+  file_favicon.png
+  file_favicon.png^headers^
 
 [browser_privatebrowsing_DownloadLastDirWithCPS.js]
 [browser_privatebrowsing_about.js]
 tags = trackingprotection
 [browser_privatebrowsing_aboutHomeButtonAfterWindowClose.js]
 [browser_privatebrowsing_aboutSessionRestore.js]
 [browser_privatebrowsing_cache.js]
 [browser_privatebrowsing_certexceptionsui.js]
 [browser_privatebrowsing_concurrent.js]
 [browser_privatebrowsing_context_and_chromeFlags.js]
 [browser_privatebrowsing_crh.js]
 [browser_privatebrowsing_downloadLastDir.js]
 [browser_privatebrowsing_downloadLastDir_c.js]
 [browser_privatebrowsing_downloadLastDir_toggle.js]
+[browser_privatebrowsing_favicon.js]
 [browser_privatebrowsing_geoprompt.js]
 [browser_privatebrowsing_lastpbcontextexited.js]
 [browser_privatebrowsing_localStorage.js]
 [browser_privatebrowsing_localStorage_before_after.js]
 [browser_privatebrowsing_noSessionRestoreMenuOption.js]
 [browser_privatebrowsing_nonbrowser.js]
 [browser_privatebrowsing_opendir.js]
 [browser_privatebrowsing_placesTitleNoUpdate.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
@@ -0,0 +1,293 @@
+/* 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 test make sure that the favicon of the private browsing is isolated.
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_SITE = "http://mochi.test:8888";
+const TEST_CACHE_SITE = "http://www.example.com";
+const TEST_DIRECTORY = "/browser/browser/components/privatebrowsing/test/browser/";
+
+const TEST_PAGE = TEST_SITE + TEST_DIRECTORY + "file_favicon.html";
+const TEST_CACHE_PAGE = TEST_CACHE_SITE + TEST_DIRECTORY + "file_favicon.html";
+const FAVICON_URI = TEST_SITE + TEST_DIRECTORY + "file_favicon.png";
+const FAVICON_CACHE_URI = TEST_CACHE_SITE + TEST_DIRECTORY + "file_favicon.png";
+
+let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+
+function clearAllImageCaches() {
+  let tools = SpecialPowers.Cc["@mozilla.org/image/tools;1"]
+                             .getService(SpecialPowers.Ci.imgITools);
+  let imageCache = tools.getImgCacheForDocument(window.document);
+  imageCache.clearCache(true);  // true=chrome
+  imageCache.clearCache(false); // false=content
+}
+
+function clearAllPlacesFavicons() {
+  let faviconService = Cc["@mozilla.org/browser/favicon-service;1"]
+                          .getService(Ci.nsIFaviconService);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "places-favicons-expired") {
+          resolve();
+          Services.obs.removeObserver(observer, "places-favicons-expired", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "places-favicons-expired", false);
+    faviconService.expireAllFavicons();
+  });
+}
+
+function observeFavicon(aIsPrivate, aExpectedCookie, aPageURI) {
+  let faviconReqXUL = false;
+  let faviconReqPlaces = false;
+  let attr = {};
+
+  if (aIsPrivate) {
+    attr.privateBrowsingId = 1;
+  }
+
+  let expectedPrincipal = Services.scriptSecurityManager
+                                  .createCodebasePrincipal(aPageURI, attr);
+
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        // Make sure that the topic is 'http-on-modify-request'.
+        if (aTopic === "http-on-modify-request") {
+          // We check the privateBrowsingId for the originAttributes of the loading
+          // channel. All requests for the favicon should contain the correct
+          // privateBrowsingId. There are two requests for a favicon loading, one
+          // from the Places library and one from the XUL image. The difference
+          // of them is the loading principal. The Places will use the content
+          // principal and the XUL image will use the system principal.
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let reqLoadInfo = httpChannel.loadInfo;
+          let loadingPrincipal = reqLoadInfo.loadingPrincipal;
+          let triggeringPrincipal = reqLoadInfo.triggeringPrincipal;
+
+          // Make sure this is a favicon request.
+          if (httpChannel.URI.spec !== FAVICON_URI) {
+            return;
+          }
+
+          // Check the privateBrowsingId.
+          if (aIsPrivate) {
+            is(reqLoadInfo.originAttributes.privateBrowsingId, 1, "The loadInfo has correct privateBrowsingId");
+          } else {
+            is(reqLoadInfo.originAttributes.privateBrowsingId, 0, "The loadInfo has correct privateBrowsingId");
+          }
+
+          if (loadingPrincipal.equals(systemPrincipal)) {
+            faviconReqXUL = true;
+            ok(triggeringPrincipal.equals(expectedPrincipal),
+              "The triggeringPrincipal of favicon loading from XUL should be the content principal.");
+          } else {
+            faviconReqPlaces = true;
+            ok(loadingPrincipal.equals(expectedPrincipal),
+              "The loadingPrincipal of favicon loading from Places should be the content prinicpal");
+          }
+
+          let faviconCookie = httpChannel.getRequestHeader("cookie");
+
+          is(faviconCookie, aExpectedCookie, "The cookie of the favicon loading is correct.");
+        } else {
+          ok(false, "Received unexpected topic: ", aTopic);
+        }
+
+        if (faviconReqXUL && faviconReqPlaces) {
+          resolve();
+          Services.obs.removeObserver(observer, "http-on-modify-request", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-modify-request", false);
+  });
+}
+
+function waitOnFaviconResponse(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      observe(aSubject, aTopic, aData) {
+        if (aTopic === "http-on-examine-response" ||
+            aTopic === "http-on-examine-cached-response") {
+
+          let httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
+          let loadInfo = httpChannel.loadInfo;
+
+          if (httpChannel.URI.spec !== aFaviconURL) {
+            return;
+          }
+
+          let result = {
+            topic: aTopic,
+            privateBrowsingId: loadInfo.originAttributes.privateBrowsingId
+          };
+
+          resolve(result);
+          Services.obs.removeObserver(observer, "http-on-examine-response", false);
+          Services.obs.removeObserver(observer, "http-on-examine-cached-response", false);
+        }
+      }
+    };
+
+    Services.obs.addObserver(observer, "http-on-examine-response", false);
+    Services.obs.addObserver(observer, "http-on-examine-cached-response", false);
+  });
+}
+
+function waitOnFaviconLoaded(aFaviconURL) {
+  return new Promise(resolve => {
+    let observer = {
+      onPageChanged(uri, attr, value, id) {
+
+        if (attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON &&
+            value === aFaviconURL) {
+          resolve();
+          PlacesUtils.history.removeObserver(observer, false);
+        }
+      },
+    };
+
+    PlacesUtils.history.addObserver(observer, false);
+  });
+}
+
+function* assignCookies(aBrowser, aURL, aCookieValue){
+  let tabInfo = yield openTab(aBrowser, aURL);
+
+  yield ContentTask.spawn(tabInfo.browser, aCookieValue, function* (value) {
+    content.document.cookie = value;
+  });
+
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+}
+
+function* openTab(aBrowser, aURL) {
+  let tab = aBrowser.addTab(aURL);
+
+  // Select tab and make sure its browser is focused.
+  aBrowser.selectedTab = tab;
+  tab.ownerGlobal.focus();
+
+  let browser = aBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+  return {tab, browser};
+}
+
+// A clean up function to prevent affecting other tests.
+registerCleanupFunction(() => {
+  // Clear all cookies.
+  let cookieMgr = Cc["@mozilla.org/cookiemanager;1"]
+                     .getService(Ci.nsICookieManager);
+  cookieMgr.removeAll();
+
+  // Clear all image caches and network caches.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+});
+
+add_task(function* test_favicon_privateBrowsing() {
+  // Clear all image caches before running the test.
+  clearAllImageCaches();
+
+  // Clear all favicons in Places.
+  yield clearAllPlacesFavicons();
+
+  // Create a private browsing window.
+  let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
+  let pageURI = makeURI(TEST_PAGE);
+
+  // Generate two random cookies for non-private window and private window
+  // respectively.
+  let cookies = [];
+  cookies.push(Math.random().toString());
+  cookies.push(Math.random().toString());
+
+  // Open a tab in private window and add a cookie into it.
+  yield assignCookies(privateWindow.gBrowser, TEST_SITE, cookies[0]);
+
+  // Open a tab in non-private window and add a cookie into it.
+  yield assignCookies(gBrowser, TEST_SITE, cookies[1]);
+
+  // Add the observer earlier in case we don't capture events in time.
+  let promiseObserveFavicon = observeFavicon(true, cookies[0], pageURI);
+
+  // Open a tab for the private window.
+  let tabInfo = yield openTab(privateWindow.gBrowser, TEST_PAGE);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+
+  // Add the observer earlier in case we don't capture events in time.
+  promiseObserveFavicon = observeFavicon(false, cookies[1], pageURI);
+
+  // Open a tab for the non-private window.
+  tabInfo = yield openTab(gBrowser, TEST_PAGE);
+
+  // Waiting until favicon requests are all made.
+  yield promiseObserveFavicon;
+
+  // Close the tab.
+  yield BrowserTestUtils.removeTab(tabInfo.tab);
+  yield BrowserTestUtils.closeWindow(privateWindow);
+});
+
+add_task(function* test_favicon_cache_privateBrowsing() {
+  // Clear all image cahces and network cache before running the test.
+  clearAllImageCaches();
+
+  let networkCache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                        .getService(Ci.nsICacheStorageService);
+  networkCache.clear();
+
+  // Clear all favicons in Places.
+  yield clearAllPlacesFavicons();
+
+  // Add an observer for making sure the favicon has been loaded and cached.
+  let promiseFaviconLoaded = waitOnFaviconLoaded(FAVICON_CACHE_URI);
+
+  // Open a tab for the non-private window.
+  let tabInfoNonPrivate = yield openTab(gBrowser, TEST_CACHE_PAGE);
+
+  let response = yield waitOnFaviconResponse(FAVICON_CACHE_URI);
+
+  yield promiseFaviconLoaded;
+
+  // Check that the favicon response has come from the network and it has the
+  // correct privateBrowsingId.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network.");
+  is(response.privateBrowsingId, 0, "We should observe the network response for the non-private tab.");
+
+  // Create a private browsing window.
+  let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
+
+  // Open a tab for the private window.
+  let tabInfoPrivate = yield openTab(privateWindow.gBrowser, TEST_CACHE_PAGE);
+
+  // Wait for the favicon response of the private tab.
+  response = yield waitOnFaviconResponse(FAVICON_CACHE_URI);
+
+  // Make sure the favicon is loaded through the network and its privateBrowsingId is correct.
+  is(response.topic, "http-on-examine-response", "The favicon image should be loaded through the network again.");
+  is(response.privateBrowsingId, 1, "We should observe the network response for the private tab.");
+
+  yield BrowserTestUtils.removeTab(tabInfoPrivate.tab);
+  yield BrowserTestUtils.removeTab(tabInfoNonPrivate.tab);
+  yield BrowserTestUtils.closeWindow(privateWindow);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_favicon.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <meta charset='utf-8'>
+    <title>Favicon Test for originAttributes</title>
+    <link rel="icon" type="image/png" href="file_favicon.png" />
+  </head>
+  <body>
+    Favicon!!
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5535363c94df7314765551f65311ec6f98729d8e
GIT binary patch
literal 344
zc$@)P0jK_nP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80003WNkl<ZSi@ud
z|NsAgh5-aj3<C^C9l%0R#sBwE#yKee29*B}N^?LcMg=I}1WL<7#aV`G2>ibbr58i#
zeNc((P#P98>`-}V2r!yL=>({_1(eS*I75IDqa1+6`Tu4pJs&FZfkdNSpkC~N^7XN%
ziNTl#4nlptmQ0_+z#gbM8_{x!@L&&t|69@WIcn7X|AQkj;gW}$#~3qYLI76MeMc4k
zL!iLKDUaQ}p<fv>vJfohhiYxer~!@C3s8YCB*r;9fSSj^Ib=e>8|uYfC?P<0IRG;c
zE&mVZy1*Xl@?ezO@c*9=cpEsFAp@-q8U3Mr{vneF1fh&<D9wk`BKrRaM~jF7M~jF-
q8*kfi5VnX$TC13Gv~M`#9RL8-wO_*zBf!o80000<MNUMnLSTX;M3C|T
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/file_favicon.png^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-cache
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -506,18 +506,16 @@
 @RESPATH@/components/TelemetryStartup.js
 @RESPATH@/components/TelemetryStartup.manifest
 @RESPATH@/components/XULStore.js
 @RESPATH@/components/XULStore.manifest
 @RESPATH@/components/messageWakeupService.js
 @RESPATH@/components/messageWakeupService.manifest
 @RESPATH@/components/SettingsManager.js
 @RESPATH@/components/SettingsManager.manifest
-@RESPATH@/components/Webapps.js
-@RESPATH@/components/Webapps.manifest
 @RESPATH@/components/AppsService.js
 @RESPATH@/components/AppsService.manifest
 @RESPATH@/components/recording-cmdline.js
 @RESPATH@/components/recording-cmdline.manifest
 @RESPATH@/components/htmlMenuBuilder.js
 @RESPATH@/components/htmlMenuBuilder.manifest
 
 @RESPATH@/components/PermissionSettings.js
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -68,20 +68,20 @@ this.ContentWebRTC = {
         break;
       }
       case "webrtc:Allow": {
         let callID = aMessage.data.callID;
         let contentWindow = Services.wm.getOuterWindowWithId(aMessage.data.windowID);
         let devices = contentWindow.pendingGetUserMediaRequests.get(callID);
         forgetGUMRequest(contentWindow, callID);
 
-        let allowedDevices = Cc["@mozilla.org/supports-array;1"]
-                               .createInstance(Ci.nsISupportsArray);
+        let allowedDevices = Cc["@mozilla.org/array;1"]
+                               .createInstance(Ci.nsIMutableArray);
         for (let deviceIndex of aMessage.data.devices)
-           allowedDevices.AppendElement(devices[deviceIndex]);
+           allowedDevices.appendElement(devices[deviceIndex], /*weak =*/ false);
 
         Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", callID);
         break;
       }
       case "webrtc:Deny":
         denyGUMRequest(aMessage.data);
         break;
       case "webrtc:StopSharing":
@@ -256,18 +256,18 @@ function forgetPendingListsEventually(aC
     return;
   }
   aContentWindow.pendingGetUserMediaRequests = null;
   aContentWindow.pendingPeerConnectionRequests = null;
   aContentWindow.removeEventListener("unload", ContentWebRTC);
 }
 
 function updateIndicators() {
-  let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows;
-  let count = contentWindowSupportsArray.Count();
+  let contentWindowArray = MediaManagerService.activeMediaCaptureWindows;
+  let count = contentWindowArray.length;
 
   let state = {
     showGlobalIndicator: count > 0,
     showCameraIndicator: false,
     showMicrophoneIndicator: false,
     showScreenSharingIndicator: ""
   };
 
@@ -275,17 +275,17 @@ function updateIndicators() {
                .getService(Ci.nsIMessageSender);
   cpmm.sendAsyncMessage("webrtc:UpdatingIndicators");
 
   // If several iframes in the same page use media streams, it's possible to
   // have the same top level window several times. We use a Set to avoid
   // sending duplicate notifications.
   let contentWindows = new Set();
   for (let i = 0; i < count; ++i) {
-    contentWindows.add(contentWindowSupportsArray.GetElementAt(i).top);
+    contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top);
   }
 
   for (let contentWindow of contentWindows) {
     let tabState = getTabStateForContentWindow(contentWindow);
     if (tabState.camera)
       state.showCameraIndicator = true;
     if (tabState.microphone)
       state.showMicrophoneIndicator = true;
--- a/browser/themes/linux/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/linux/downloads/allDownloadsViewOverlay.css
@@ -4,85 +4,8 @@
 
 %include ../../shared/downloads/allDownloadsViewOverlay.inc.css
 
 /*** List items ***/
 
 :root {
   --downloads-item-height: 5em;
 }
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 80px, 16px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 96px, 16px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 112px, 16px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 128px, 16px, 112px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@item@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@item@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@item@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 80px, 32px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 96px, 32px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 112px, 32px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 128px, 32px, 112px);
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 80px, 48px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 96px, 48px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 112px, 48px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 128px, 48px, 112px);
-}
deleted file mode 100644
index 0c91695aa5c5351fec6011df01bb2c364f28f749..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/linux/downloads/downloads.css
+++ b/browser/themes/linux/downloads/downloads.css
@@ -11,119 +11,24 @@
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -5px;
 }
 
 /*** List items and similar elements in the summary ***/
 
 :root {
   --downloads-item-height: 6em;
-  --downloads-item-border-top-color: hsla(0,0%,100%,.2);
-  --downloads-item-border-bottom-color: hsla(0,0%,0%,.15);
   --downloads-item-font-size-factor: 0.9;
   --downloads-item-target-margin-bottom: 7px;
   --downloads-item-details-margin-top: 1px;
   --downloads-item-details-opacity: 0.6;
 }
 
 .downloadButton:focus > .button-box {
   outline: 1px -moz-dialogtext dotted;
 }
 
 /*** Highlighted list items ***/
 
 @keyfocus@ @itemFocused@ {
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -1px;
 }
-
-@notKeyfocus@ @itemFinished@[exists]:hover,
-@item@[showingsubview] {
-  border-radius: 3px;
-  border-top: 1px solid hsla(0,0%,100%,.3);
-  border-bottom: 1px solid hsla(0,0%,0%,.2);
-  background-color: Highlight;
-  background-image: linear-gradient(hsla(0,0%,100%,.1), transparent);
-  color: HighlightText;
-  cursor: pointer;
-}
-
-@notKeyfocus@ @itemFinished@[exists][verdict="Malware"]:hover,
-@item@[showingsubview][verdict="Malware"] {
-  background-color: hsl(4, 82%, 47%);
-  color: white;
-}
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@itemNotFinished@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@itemNotFinished@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@itemNotFinished@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 96px, 32px, 80px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 112px, 32px, 96px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 128px, 32px, 112px);
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-
-.downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 16px, 64px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 32px, 64px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover {
-  -moz-image-region: rect(48px, 48px, 64px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active {
-  -moz-image-region: rect(48px, 64px, 64px, 48px);
-}
-
-.downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 16px, 80px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 32px, 80px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 48px, 80px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 64px, 80px, 48px);
-}
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -43,17 +43,16 @@ browser.jar:
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
 * skin/classic/browser/customizableui/panelUI.css (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
 * skin/classic/browser/downloads/downloads.css        (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css            (feeds/subscribe.css)
   skin/classic/browser/feeds/subscribe-ui.css         (feeds/subscribe-ui.css)
--- a/browser/themes/osx/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/osx/downloads/allDownloadsViewOverlay.css
@@ -4,170 +4,8 @@
 
 %include ../../shared/downloads/allDownloadsViewOverlay.inc.css
 
 /*** List items ***/
 
 :root {
   --downloads-item-height: 6em;
 }
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 80px, 16px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 96px, 16px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 112px, 16px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 128px, 16px, 112px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@item@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@item@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@item@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 80px, 32px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 96px, 32px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 112px, 32px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 128px, 32px, 112px);
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-@itemFocused@ .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 80px, 48px, 64px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 96px, 48px, 80px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 112px, 48px, 96px);
-}
-@itemFocused@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 128px, 48px, 112px);
-}
-
-@media (min-resolution: 2dppx) {
-  .downloadButton {
-    list-style-image: url("chrome://browser/skin/downloads/buttons@2x.png");
-  }
-  .downloadButton > .button-box > .button-icon {
-    width: 16px;
-    height: 16px;
-  }
-
-  .downloadButton.downloadIconCancel {
-     -moz-image-region: rect(0px, 32px, 32px, 0px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 64px, 32px, 32px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel:hover {
-    -moz-image-region: rect(0px, 96px, 32px, 64px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel:active {
-    -moz-image-region: rect(0px, 128px, 32px, 96px);
-  }
-  @itemFocused@ .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 160px, 32px, 128px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 192px, 32px, 160px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-    -moz-image-region: rect(0px, 224px, 32px, 192px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel:active {
-    -moz-image-region: rect(0px, 256px, 32px, 224px);
-  }
-
-  .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 32px, 64px, 0px);
-  }
-  @item@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 64px, 64px, 32px);
-  }
-  @item@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(32px, 96px, 64px, 64px);
-  }
-  @item@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(32px, 128px, 64px, 96px);
-  }
-  @itemFocused@ .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 160px, 64px, 128px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 192px, 64px, 160px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(32px, 224px, 64px, 192px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(32px, 256px, 64px, 224px);
-  }
-
-  .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 32px, 96px, 0px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 64px, 96px, 32px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry:hover {
-    -moz-image-region: rect(64px, 96px, 96px, 64px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry:active {
-    -moz-image-region: rect(64px, 128px, 96px, 96px);
-  }
-  @itemFocused@ .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 160px, 96px, 128px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 192px, 96px, 160px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-    -moz-image-region: rect(64px, 224px, 96px, 192px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry:active {
-    -moz-image-region: rect(64px, 256px, 96px, 224px);
-  }
-}
deleted file mode 100644
index 2aad9c7be2a2a56a9690ab1ea26a5a788399a807..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index fba4edbb53abee13b57c2a6b7e39329c5c19ed15..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -15,329 +15,29 @@
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
 /*** List items and similar elements in the summary ***/
 
 :root {
   --downloads-item-height: 7em;
-  --downloads-item-border-top-color: hsla(0,0%,100%,.07);
-  --downloads-item-border-bottom-color: hsla(0,0%,0%,.2);
   --downloads-item-font-size-factor: 0.95;
   --downloads-item-target-margin-bottom: 6px;
   --downloads-item-details-margin-top: 0;
   --downloads-item-details-opacity: 0.7;
 }
 
 .downloadButton:focus > .button-box {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
-/*** Highlighted list items ***/
-
-@keyfocus@ @itemFocused@,
-@notKeyfocus@ @itemFinished@[exists]:hover,
-@item@[showingsubview] {
-  border-radius: 3px;
-  border-top: 1px solid hsla(0,0%,100%,.2);
-  border-bottom: 1px solid hsla(0,0%,0%,.4);
-  background-color: Highlight;
-  color: HighlightText;
-}
-
-@item@[showingsubview] {
-  transition: background-color var(--panelui-subview-transition-duration),
-              color var(--panelui-subview-transition-duration);
-}
-
-@keyfocus@ @itemFocused@[verdict="Malware"],
-@notKeyfocus@ @itemFinished@[exists][verdict="Malware"]:hover,
-@item@[showingsubview][verdict="Malware"] {
-  background-color: hsl(4, 82%, 47%);
-  color: white;
-}
-
-@notKeyfocus@ @itemFinished@[exists]:hover {
-  cursor: pointer;
-}
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-@keyfocus@ @itemFocused@ .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 80px, 16px, 64px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 96px, 16px, 80px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 112px, 16px, 96px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 128px, 16px, 112px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow,
-@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:hover,
-@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:active,
-@keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-@keyfocus@ @itemFocused@ .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 80px, 32px, 64px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow,
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 96px, 32px, 80px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover,
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 112px, 32px, 96px);
-}
-@notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active,
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 128px, 32px, 112px);
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-@keyfocus@ @itemFocused@ .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 80px, 48px, 64px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 96px, 48px, 80px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 112px, 48px, 96px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 128px, 48px, 112px);
-}
-
-.downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 16px, 64px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 32px, 64px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover {
-  -moz-image-region: rect(48px, 48px, 64px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active {
-  -moz-image-region: rect(48px, 64px, 64px, 48px);
-}
-@keyfocus@ @itemFocused@ .downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 80px, 64px, 64px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 96px, 64px, 80px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:hover {
-  -moz-image-region: rect(48px, 112px, 64px, 96px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:active {
-  -moz-image-region: rect(48px, 128px, 64px, 112px);
+@item@[verdict="Malware"] {
+  color: #aa1b08;
 }
 
-.downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 16px, 80px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 32px, 80px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 48px, 80px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 64px, 80px, 48px);
-}
-@keyfocus@ @itemFocused@ .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 80px, 80px, 64px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 96px, 80px, 80px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 112px, 80px, 96px);
-}
-@keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 128px, 80px, 112px);
-}
-
-@media (min-resolution: 2dppx) {
-  .downloadButton {
-    list-style-image: url("chrome://browser/skin/downloads/buttons@2x.png");
-  }
-  .downloadButton > .button-box > .button-icon {
-    width: 16px;
-    height: 16px;
-  }
-
-  .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 32px, 32px, 0px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 64px, 32px, 32px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel:hover {
-    -moz-image-region: rect(0px, 96px, 32px, 64px);
-  }
-  @item@:hover .downloadButton.downloadIconCancel:active {
-    -moz-image-region: rect(0px, 128px, 32px, 96px);
-  }
-  @keyfocus@ @itemFocused@ .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 160px, 32px, 128px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 192px, 32px, 160px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-    -moz-image-region: rect(0px, 224px, 32px, 192px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconCancel:active {
-    -moz-image-region: rect(0px, 256px, 32px, 224px);
-  }
+/*** Highlighted list items ***/
 
-  .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 32px, 64px, 0px);
-  }
-  @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow,
-  @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 64px, 64px, 32px);
-  }
-  @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:hover,
-  @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(32px, 96px, 64px, 64px);
-  }
-  @notKeyfocus@ @itemNotFinished@:hover .downloadButton.downloadIconShow:active,
-  @keyfocus@ @itemFinished@:hover:not([selected]) .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(32px, 128px, 64px, 96px);
-  }
-  @keyfocus@ @itemFocused@ .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 160px, 64px, 128px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow,
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(32px, 192px, 64px, 160px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover,
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(32px, 224px, 64px, 192px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active,
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(32px, 256px, 64px, 224px);
-  }
-
-  .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 32px, 96px, 0px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 64px, 96px, 32px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry:hover {
-    -moz-image-region: rect(64px, 96px, 96px, 64px);
-  }
-  @item@:hover .downloadButton.downloadIconRetry:active {
-    -moz-image-region: rect(64px, 128px, 96px, 96px);
-  }
-  @keyfocus@ @itemFocused@ .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 160px, 96px, 128px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(64px, 192px, 96px, 160px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-    -moz-image-region: rect(64px, 224px, 96px, 192px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadIconRetry:active {
-    -moz-image-region: rect(64px, 256px, 96px, 224px);
-  }
-
-  .downloadButton.downloadShowBlockedInfo {
-    -moz-image-region: rect(96px, 32px, 128px, 0px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo {
-    -moz-image-region: rect(96px, 64px, 128px, 32px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo:hover {
-    -moz-image-region: rect(96px, 96px, 128px, 64px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo:active {
-    -moz-image-region: rect(96px, 128px, 128px, 96px);
-  }
-  @keyfocus@ @itemFocused@ .downloadButton.downloadShowBlockedInfo {
-    -moz-image-region: rect(96px, 160px, 128px, 128px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo {
-    -moz-image-region: rect(96px, 192px, 128px, 160px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:hover {
-    -moz-image-region: rect(96px, 224px, 128px, 192px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:active {
-    -moz-image-region: rect(96px, 256px, 128px, 224px);
-  }
-
-  .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 32px, 160px, 0px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 64px, 160px, 32px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 96px, 160px, 64px);
-  }
-  @item@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 128px, 160px, 96px);
-  }
-  @keyfocus@ @itemFocused@ .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 160px, 160px, 128px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 192px, 160px, 160px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 224px, 160px, 192px);
-  }
-  @keyfocus@ @itemFocused@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-    -moz-image-region: rect(128px, 256px, 160px, 224px);
-  }
-
-  #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton {
-    list-style-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png");
-  }
-  #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton:-moz-locale-dir(rtl) {
-    list-style-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png");
-  }
+@keyfocus@ @itemFocused@ {
+  outline: 2px -moz-mac-focusring solid;
+  outline-offset: -2px;
 }
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -63,18 +63,16 @@ browser.jar:
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
 * skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
-  skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
   skin/classic/browser/downloads/download-notification-finish.png  (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-finish@2x.png  (downloads/download-notification-finish@2x.png)
   skin/classic/browser/downloads/download-notification-start.png  (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-notification-start@2x.png  (downloads/download-notification-start@2x.png)
 * skin/classic/browser/downloads/downloads.css              (downloads/downloads.css)
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
--- a/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
+++ b/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
@@ -60,29 +60,94 @@
 @item@[verdict="PotentiallyUnwanted"] .downloadBlockedBadge {
   background-image: url("chrome://browser/skin/warning.svg");
 }
 
 @item@[verdict="Uncommon"] .downloadBlockedBadge {
   background-image: url("chrome://browser/skin/info.svg");
 }
 
+@item@ > toolbarseparator {
+  display: none;
+}
+
 .downloadTarget {
   margin-bottom: 3px;
   cursor: inherit;
 }
 
 .downloadDetails {
   opacity: 0.7;
   font-size: 95%;
   cursor: inherit;
 }
 
 .downloadButton {
   -moz-appearance: none;
+  -moz-box-align: center;
   background: transparent;
   min-width: 0;
   min-height: 0;
   margin: 3px;
   border: none;
   padding: 5px;
-  list-style-image: url("chrome://browser/skin/downloads/buttons.png");
+  color: inherit;
+}
+
+.downloadButton > .button-box {
+  -moz-appearance: none;
+  padding: 2px !important;
+  border-radius: 50%;
+  color: graytext;
+}
+
+.downloadButton > .button-box > .button-icon {
+  width: 16px;
+  height: 16px;
+  margin: 0;
+  filter: url("chrome://browser/skin/filters.svg#fill");
+  fill: currentColor;
+}
+
+.downloadButton > .button-box > .button-text {
+  display: none;
+}
+
+.downloadButton:hover > .button-box {
+  background-color: graytext;
+  color: -moz-field;
+}
+
+.downloadButton:hover:active > .button-box {
+  background-color: -moz-fieldtext;
 }
+
+@itemFocused@ > .downloadButtonArea > .downloadButton > .button-box {
+  color: inherit;
+}
+
+@itemFocused@ > .downloadButtonArea > .downloadButton:hover > .button-box {
+  background-color: HighlightText;
+  color: Highlight;
+}
+
+@itemFocused@ > .downloadButtonArea > .downloadButton:hover:active > .button-box {
+  background-color: -moz-field;
+  color: -moz-fieldtext;
+}
+
+/*** Button icons ***/
+
+.downloadIconCancel > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#cancel");
+}
+
+.downloadIconShow > .button-box > .button-icon {
+%ifdef XP_MACOSX
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#magnifier");
+%else
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#folder");
+%endif
+}
+
+.downloadIconRetry > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#retry");
+}
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -20,17 +20,17 @@
 
 #downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent,
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
   padding: 0;
 }
 
 #downloadsListBox {
   background: transparent;
-  padding: 4px;
+  padding: 0;
   color: inherit;
   -moz-appearance: none;
   margin: 0;
   border: none;
 }
 
 #emptyDownloads {
   padding: 16px 25px;
@@ -40,17 +40,18 @@
   text-align: center;
 }
 
 .downloadsPanelFooter {
   background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
 }
 
-.downloadsPanelFooter toolbarseparator {
+.downloadsPanelFooter toolbarseparator,
+@item@ > toolbarseparator {
   margin: 0;
   border: 0;
   min-width: 0;
   border-left: 1px solid var(--panel-separator-color);
   -moz-appearance: none;
 }
 
 .downloadsPanelFooterButton {
@@ -103,16 +104,21 @@
 #downloadsPanel[hasdownloads] #downloadsFooterButtons:not(.downloadsHideDropmarker) > #downloadsHistory {
   padding-inline-start: 68px;
 }
 
 toolbarseparator.downloadsDropmarkerSplitter {
   margin: 7px 0;
 }
 
+@item@ > toolbarseparator {
+  margin: 10px 0;
+}
+
+@item@:hover > toolbarseparator,
 #downloadsFooter:hover toolbarseparator.downloadsDropmarkerSplitter,
 #downloadsFooter[showingdropdown] toolbarseparator {
   margin: 0;
 }
 
 .downloadsDropmarker {
   padding: 0 21px;
 }
@@ -163,28 +169,27 @@ toolbarseparator.downloadsDropmarkerSpli
 richlistitem[type="download"] {
   height: var(--downloads-item-height);
   padding-inline-end: 0;
   color: inherit;
 }
 
 richlistitem[type="download"] {
   margin: 0;
-  border-top: 1px solid var(--downloads-item-border-top-color);
-  border-bottom: 1px solid var(--downloads-item-border-bottom-color);
+  border-bottom: 1px solid var(--panel-separator-color);
   background: transparent;
-  padding: 8px;
-}
-
-richlistitem[type="download"]:first-child {
-  border-top: 1px solid transparent;
+  padding: 0;
 }
 
 richlistitem[type="download"]:last-child {
-  border-bottom: 1px solid transparent;
+  border-bottom: none;
+}
+
+richlistitem[type="download"] > .downloadMainArea {
+  padding: 8px;
 }
 
 .downloadTypeIcon {
   margin-top: 8px;
   margin-inline-end: 12px;
   margin-bottom: 8px;
   margin-inline-start: 0;
   width: 32px;
@@ -239,48 +244,124 @@ richlistitem[type="download"]:last-child
 
 #downloadsSummaryDetails,
 .downloadDetails {
   margin-top: var(--downloads-item-details-margin-top);
   opacity: var(--downloads-item-details-opacity);
   cursor: inherit;
 }
 
+@item@[verdict] > toolbarseparator {
+  visibility: hidden;
+}
+
 .downloadButton {
   -moz-appearance: none;
-  min-width: 0;
-  min-height: 0;
-  margin: 3px;
+  min-width: 58px;
+  margin: 0;
   border: none;
   background: transparent;
-  padding: 5px;
-  list-style-image: url("chrome://browser/skin/downloads/buttons.png");
+  padding: 8px;
+  color: graytext;
 }
 
-.downloadButton > .button-box {
+.downloadButton > .button-box > .button-icon {
+  width: 16px;
+  height: 16px;
+  margin: 1px;
+  filter: url("chrome://browser/skin/filters.svg#fill");
+  fill: currentColor;
+}
+
+.downloadButton > .button-box > .button-text {
+  margin: 0 !important;
   padding: 0;
 }
 
+@itemFinished@[exists]:hover > .downloadMainArea,
+@item@:not([verdict]):hover > .downloadButtonArea {
+  background-color: var(--arrowpanel-dimmed);
+}
+
+@itemFinished@[exists] .downloadMainArea:hover,
+@item@:not([verdict]) > .downloadButtonArea:hover,
+@item@[verdict]:hover {
+  background-color: var(--arrowpanel-dimmed-further);
+}
+
+@itemFinished@[exists] > .downloadMainArea:hover:active,
+@item@:not([verdict]) > .downloadButtonArea:hover:active,
+@item@[verdict]:hover:active {
+  background-color: var(--arrowpanel-dimmed-even-further);
+}
+
+@item@[showingsubview] {
+  background-color: Highlight;
+  color: HighlightText;
+  transition: background-color var(--panelui-subview-transition-duration),
+              color var(--panelui-subview-transition-duration);
+}
+
+@item@[verdict="Malware"]:hover,
+@item@[verdict="Malware"]:hover:active,
+@item@[verdict="Malware"][showingsubview] {
+  background-color: #aa1b08;
+  color: white;
+}
+
+@item@[verdict="Malware"]:hover > .downloadButtonArea > .downloadButton {
+  color: inherit;
+}
+
+/*** Button icons ***/
+
+.downloadIconCancel > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#cancel");
+}
+
+.downloadIconShow > .button-box > .button-icon {
+%ifdef XP_MACOSX
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#magnifier");
+%else
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#folder");
+%endif
+}
+
+.downloadIconRetry > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#retry");
+}
+
+.downloadShowBlockedInfo > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-right");
+}
+
+.downloadShowBlockedInfo > .button-box > .button-icon:-moz-locale-dir(rtl) {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-left");
+}
 
 /*** Blocked subview ***/
 
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=main] > .panel-subviews {
   /* When the main view is showing, the shadow on the left edge of the subview is
      barely visible on the right edge of the main view, so set it to none. */
   box-shadow: none;
 }
 
 /* When the subview is showing, turn the download button into an arrow pointing
    back to the main view. */
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton {
-  list-style-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted.png");
+  color: HighlightText;
 }
 
-#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton:-moz-locale-dir(rtl) {
-  list-style-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl.png");
+#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton > .button-box > .button-icon {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-left");
+}
+
+#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state[showingsubview] .downloadButton > .button-box > .button-icon:-moz-locale-dir(rtl) {
+  list-style-image: url("chrome://browser/skin/panel-icons.svg#arrow-right");
 }
 
 #downloadsPanel-blockedSubview {
   background-image: url("chrome://browser/skin/warning.svg");
   background-size: 32px 32px;
   background-position: 16px 16px;
   background-repeat: no-repeat;
 }
--- a/browser/themes/shared/panel-icons.svg
+++ b/browser/themes/shared/panel-icons.svg
@@ -1,8 +1,18 @@
 <?xml version="1.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/. -->
 <svg xmlns="http://www.w3.org/2000/svg"
      width="32" height="32" viewBox="0 0 32 32">
+  <style>
+    path:not(:target) {
+      display: none;
+    }
+  </style>
   <path id="cancel" d="m 6,9.5 6.5,6.5 -6.5,6.5 3.5,3.5 6.5,-6.5 6.5,6.5 3.5,-3.5 -6.5,-6.5 6.5,-6.5 -3.5,-3.5 -6.5,6.5 -6.5,-6.5 z" />
+  <path id="folder" d="M17.3,9.4c0,0,1.1,0,3.7,0c1.7,0,2,0,5.6,0c0.6,0,0.6,0,1.1,0V9.2c0-1.5-0.9-2.6-2-2.6h-5.8V6.3c0-0.6-1.5-2-2.8-2h-7.1 H7.6H4.9v2.4v2.4v2.2c2.8,0,8.5,0,8.5,0C16.4,11.3,17.3,9.4,17.3,9.4z M29,13c0-0.6-0.6-1.1-1.5-1.7l0,0c-0.2,0-0.6,0-0.9,0 c-2.8,0-3,0-4.8,0c-1.9,0-3.3,0-3.3,0s-1.5,2.4-3.7,2.4c0,0-6.5,0-9.1,0H5.4C3,13.7,3,15.9,3,15.9l1.1,9.7C4.1,27.1,5,28,6.5,28 h19.1c1.5,0,2.4-0.9,2.4-2.4L29,13.7l0,0l0,0C29,13.7,29,13,29,13z" />
+  <path id="magnifier" d="M12.9,2c6,0,11,5,11,11c0,2.2-0.6,4.2-1.8,6l7.2,7c0.8,0.8,0.8,2.4,0,3.2c-0.6,0.6-1.2,0.8-1.6,0.8s-1.2-0.2-1.6-0.6l-7-7 c-1.8,1.2-3.8,1.8-6,1.8c-6,0-11-5-11-11C2.1,7.2,6.9,2,12.9,2z M12.9,20c3.8,0,7-3.2,7-7s-3.2-7-7-7s-7,3.2-7,7S9.1,20,12.9,20z" />
+  <path id="retry" d="M28,16.5v-14l-5,4.8c-1.8-1.4-4.4-2.4-7-2.4c-6.4,0-11.8,5.2-11.8,11.8c0,6.4,5.2,11.8,11.8,11.8c3.4,0,6.2-1.4,8.2-3.6 l-3.4-3.4c-1.2,1.2-3,1.8-5,1.8c-3.6,0.2-6.8-2.8-6.8-6.8c0-3.8,3-7.2,7-7.2c1.4,0,2.6,0.4,3.6,1l-6,6.2H28z"/>
+  <path id="arrow-left" d="M23.5,25l-9-9l9-9l-3-3l-12,12l12,12L23.5,25z" />
+  <path id="arrow-right" d="M11.6,28l12-12l-12-12l-3,3l9,9l-9,9L11.6,28z" />
 </svg>
--- a/browser/themes/windows/downloads/allDownloadsViewOverlay.css
+++ b/browser/themes/windows/downloads/allDownloadsViewOverlay.css
@@ -36,90 +36,8 @@
       background-clip: content-box;
       border-radius: 6px;
       outline: 1px solid rgb(124,163,206);
       -moz-outline-radius: 3px;
       outline-offset: -2px;
     }
   }
 }
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@item@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@item@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@item@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-
-@media (-moz-os-version: windows-xp) {
-  @itemFocused@ .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 80px, 16px, 64px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel {
-    -moz-image-region: rect(0px, 96px, 16px, 80px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel:hover {
-    -moz-image-region: rect(0px, 112px, 16px, 96px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconCancel:active {
-    -moz-image-region: rect(0px, 128px, 16px, 112px);
-  }
-
-  @itemFocused@ .downloadButton.downloadIconShow {
-    -moz-image-region: rect(16px, 80px, 32px, 64px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(16px, 96px, 32px, 80px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(16px, 112px, 32px, 96px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(16px, 128px, 32px, 112px);
-  }
-
-  @itemFocused@ .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(32px, 80px, 48px, 64px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry {
-    -moz-image-region: rect(32px, 96px, 48px, 80px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry:hover {
-    -moz-image-region: rect(32px, 112px, 48px, 96px);
-  }
-  @itemFocused@:hover .downloadButton.downloadIconRetry:active {
-    -moz-image-region: rect(32px, 128px, 48px, 112px);
-  }
-}
deleted file mode 100644
index b8a97d3750ab5220f53134acab45f6fb6c88c911..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 9611a8f8ca8e3c13cff5d2b92137f8a9fc1a9faf..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/windows/downloads/downloads.css
+++ b/browser/themes/windows/downloads/downloads.css
@@ -54,182 +54,34 @@
 @keyfocus@ #downloadsSummary:focus {
   outline-offset: -5px;
 }
 
 /*** List items and similar elements in the summary ***/
 
 :root {
   --downloads-item-height: 7em;
-  --downloads-item-border-top-color: hsla(0,0%,100%,.3);
-  --downloads-item-border-bottom-color: hsla(220,18%,51%,.25);
   --downloads-item-font-size-factor: 0.9;
   --downloads-item-target-margin-bottom: 6px;
   --downloads-item-details-margin-top: 0;
   --downloads-item-details-opacity: 0.6;
 }
 
-@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista),
-       (-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
-  richlistitem[type="download"] {
-    border: 1px solid transparent;
-    border-bottom: 1px solid hsl(213,40%,90%);
-  }
-}
-
 .downloadButton > .button-box {
   border: 1px solid transparent;
 }
 
 @keyfocus@ .downloadButton:focus > .button-box {
   border: 1px dotted ThreeDDarkShadow;
 }
 
+@media (-moz-windows-default-theme) {
+  @item@[verdict="Malware"] {
+    color: #aa1b08;
+  }
+}
+
 /*** Highlighted list items ***/
 
 @keyfocus@ @itemFocused@ {
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -1px;
 }
-
-@notKeyfocus@ @itemFinished@[exists]:hover {
-  background-color: hsla(210,4%,10%,.08);
-  outline: 1px solid hsla(210,4%,10%,.1);
-  outline-offset: -1px;
-  cursor: pointer;
-}
-
-@notKeyfocus@ @itemFinished@[exists]:hover:active {
-  background-color: hsla(210,4%,10%,.15);
-  outline: 1px solid hsla(210,4%,10%,.15);
-  box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
-}
-
-@item@[showingsubview] {
-  background-color: Highlight;
-  color: HighlightText;
-  transition: background-color var(--panelui-subview-transition-duration),
-              color var(--panelui-subview-transition-duration);
-}
-
-@notKeyfocus@ @itemFinished@[exists][verdict="Malware"]:hover,
-@notKeyfocus@ @itemFinished@[exists][verdict="Malware"]:hover:active,
-@item@[showingsubview][verdict="Malware"] {
-  background-color: hsl(4, 82%, 47%);
-  color: white;
-}
-
-@media (-moz-os-version: windows-xp),
-       (-moz-os-version: windows-vista),
-       (-moz-os-version: windows-win7) {
-  @notKeyfocus@ @itemFinished@[exists]:hover {
-    border-radius: 3px;
-    outline: 0;
-    border-top: 1px solid hsla(0,0%,100%,.2);
-    border-bottom: 1px solid hsla(0,0%,0%,.2);
-    background-color: Highlight;
-    color: HighlightText;
-  }
-
-  @notKeyfocus@ @itemFinished@[exists]:hover:active {
-    background-color: Highlight;
-    outline: 0;
-    box-shadow: none;
-  }
-}
-
-@media (-moz-windows-default-theme) and (-moz-os-version: windows-vista),
-       (-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
-  @notKeyfocus@ @itemFinished@[exists]:hover {
-    border: 1px solid hsl(213,45%,65%);
-    box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset,
-                0 1px 0 hsla(0,0%,100%,.3) inset;
-    background-image: linear-gradient(hsl(212,86%,92%), hsl(212,91%,86%));
-    color: black;
-  }
-}
-
-/*** Button icons ***/
-
-.downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-@item@:hover .downloadButton.downloadIconCancel {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-@item@:hover .downloadButton.downloadIconCancel:hover {
-  -moz-image-region: rect(0px, 48px, 16px, 32px);
-}
-@item@:hover .downloadButton.downloadIconCancel:active {
-  -moz-image-region: rect(0px, 64px, 16px, 48px);
-}
-
-.downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 16px, 32px, 0px);
-}
-@item@:hover .downloadButton.downloadIconShow {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-@item@:hover .downloadButton.downloadIconShow:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
-}
-@item@:hover .downloadButton.downloadIconShow:active {
-  -moz-image-region: rect(16px, 64px, 32px, 48px);
-}
-@media (-moz-os-version: windows-xp) {
-  @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(16px, 32px, 32px, 16px);
-  }
-  @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(16px, 48px, 32px, 32px);
-  }
-  @keyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(16px, 64px, 32px, 48px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow {
-    -moz-image-region: rect(16px, 96px, 32px, 80px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:hover {
-    -moz-image-region: rect(16px, 112px, 32px, 96px);
-  }
-  @notKeyfocus@ @itemFinished@:hover .downloadButton.downloadIconShow:active {
-    -moz-image-region: rect(16px, 128px, 32px, 112px);
-  }
-}
-
-.downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 16px, 48px, 0px);
-}
-@item@:hover .downloadButton.downloadIconRetry {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
-}
-@item@:hover .downloadButton.downloadIconRetry:hover {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-@item@:hover .downloadButton.downloadIconRetry:active {
-  -moz-image-region: rect(32px, 64px, 48px, 48px);
-}
-
-.downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 16px, 64px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo {
-  -moz-image-region: rect(48px, 32px, 64px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover {
-  -moz-image-region: rect(48px, 48px, 64px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active {
-  -moz-image-region: rect(48px, 64px, 64px, 48px);
-}
-
-.downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 16px, 80px, 0px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 32px, 80px, 16px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:hover:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 48px, 80px, 32px);
-}
-@item@:hover .downloadButton.downloadShowBlockedInfo:active:-moz-locale-dir(rtl) {
-  -moz-image-region: rect(64px, 64px, 80px, 48px);
-}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -72,18 +72,16 @@ browser.jar:
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
 * skin/classic/browser/customizableui/panelUI.css       (customizableui/panelUI.css)
 * skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
-  skin/classic/browser/downloads/buttons.png                   (downloads/buttons.png)
-  skin/classic/browser/downloads/buttons-XP.png                (downloads/buttons-XP.png)
   skin/classic/browser/downloads/download-glow-menuPanel.png   (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel-XPVista7.png   (downloads/download-glow-menuPanel-XPVista7.png)
   skin/classic/browser/downloads/download-notification-finish.png (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-start.png (downloads/download-notification-start.png)
 * skin/classic/browser/downloads/downloads.css                 (downloads/downloads.css)
   skin/classic/browser/feeds/feedIcon.png                      (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
   skin/classic/browser/feeds/feedIcon-XP.png                   (feeds/feedIcon-XP.png)
@@ -209,17 +207,16 @@ browser.jar:
 % override chrome://browser/skin/aboutSessionRestore-window-icon.png  chrome://browser/skin/preferences/application.png                 os!=WINNT
 % override chrome://browser/skin/aboutSessionRestore-window-icon.png  chrome://browser/skin/preferences/application.png                 os=WINNT osversion<6
 
 % override chrome://browser/skin/Info.png                             chrome://browser/skin/Info-XP.png                                 os=WINNT osversion<6
 % override chrome://browser/skin/livemark-folder.png                  chrome://browser/skin/livemark-folder-XP.png                      os=WINNT osversion<6
 % override chrome://browser/skin/menu-back.png                        chrome://browser/skin/menu-back-XP.png                            os=WINNT osversion<6
 % override chrome://browser/skin/menu-forward.png                     chrome://browser/skin/menu-forward-XP.png                         os=WINNT osversion<6
 % override chrome://browser/skin/pageInfo.png                         chrome://browser/skin/pageInfo-XP.png                             os=WINNT osversion<6
-% override chrome://browser/skin/downloads/buttons.png                chrome://browser/skin/downloads/buttons-XP.png                    os=WINNT osversion<6
 % override chrome://browser/skin/feeds/feedIcon.png                   chrome://browser/skin/feeds/feedIcon-XP.png                       os=WINNT osversion<6
 % override chrome://browser/skin/feeds/feedIcon16.png                 chrome://browser/skin/feeds/feedIcon16-XP.png                     os=WINNT osversion<6
 % override chrome://browser/skin/places/query.png                     chrome://browser/skin/places/query-XP.png                         os=WINNT osversion<6
 % override chrome://browser/skin/places/bookmarksMenu.png             chrome://browser/skin/places/bookmarksMenu-XP.png                 os=WINNT osversion<6
 % override chrome://browser/skin/places/bookmarksToolbar.png          chrome://browser/skin/places/bookmarksToolbar-XP.png              os=WINNT osversion<6
 % override chrome://browser/skin/places/bookmarksToolbar-menuPanel.png  chrome://browser/skin/places/bookmarksToolbar-menuPanel-XP.png  os=WINNT osversion<6
 % override chrome://browser/skin/places/calendar.png                  chrome://browser/skin/places/calendar-XP.png                      os=WINNT osversion<6
 % override chrome://browser/skin/places/toolbarDropMarker.png         chrome://browser/skin/places/toolbarDropMarker-XP.png             os=WINNT osversion<6
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -14,16 +14,17 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Sema/Sema.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 #include <memory>
+#include <iterator>
 
 #define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
 
 using namespace llvm;
 using namespace clang;
 
 #if CLANG_VERSION_FULL >= 306
 typedef std::unique_ptr<ASTConsumer> ASTConsumerPtr;
@@ -256,17 +257,18 @@ bool isInIgnoredNamespaceForImplicitCtor
          Name.substr(0, 4) == "icu_" || // icu
          Name == "google" ||            // protobuf
          Name == "google_breakpad" ||   // breakpad
          Name == "soundtouch" ||        // libsoundtouch
          Name == "stagefright" ||       // libstagefright
          Name == "MacFileUtilities" ||  // MacFileUtilities
          Name == "dwarf2reader" ||      // dwarf2reader
          Name == "arm_ex_to_module" ||  // arm_ex_to_module
-         Name == "testing";             // gtest
+         Name == "testing" ||           // gtest
+         Name == "Json";                // jsoncpp
 }
 
 bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) {
   std::string Name = getDeclarationNamespace(Declaration);
   if (Name == "") {
     return false;
   }
 
@@ -828,23 +830,35 @@ AST_MATCHER(QualType, isFloat) { return 
 
 /// This matcher will match locations in system headers.  This is adopted from
 /// isExpansionInSystemHeader in newer clangs, but modified in order to work
 /// with old clangs that we use on infra.
 AST_MATCHER(BinaryOperator, isInSystemHeader) {
   return ASTIsInSystemHeader(Finder->getASTContext(), Node);
 }
 
-/// This matcher will match locations in SkScalar.h.  This header contains a
-/// known NaN-testing expression which we would like to whitelist.
-AST_MATCHER(BinaryOperator, isInSkScalarDotH) {
+/// This matcher will match a list of files.  These files contain
+/// known NaN-testing expressions which we would like to whitelist.
+AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) {
+  const char* whitelist[] = {
+    "SkScalar.h",
+    "json_writer.cpp"
+  };
+
   SourceLocation Loc = Node.getOperatorLoc();
   auto &SourceManager = Finder->getASTContext().getSourceManager();
   SmallString<1024> FileName = SourceManager.getFilename(Loc);
-  return llvm::sys::path::rbegin(FileName)->equals("SkScalar.h");
+
+  for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) {
+    if (llvm::sys::path::rbegin(FileName)->equals(*itr)) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 /// This matcher will match all accesses to AddRef or Release methods.
 AST_MATCHER(MemberExpr, isAddRefOrRelease) {
   ValueDecl *Member = Node.getMemberDecl();
   CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
   if (Method) {
     const auto &Name = getNameChecked(Method);
@@ -1192,17 +1206,17 @@ DiagnosticsMatcher::DiagnosticsMatcher()
 
   AstMatcher.addMatcher(
       binaryOperator(
           allOf(binaryEqualityOperator(),
                 hasLHS(hasIgnoringParenImpCasts(
                     declRefExpr(hasType(qualType((isFloat())))).bind("lhs"))),
                 hasRHS(hasIgnoringParenImpCasts(
                     declRefExpr(hasType(qualType((isFloat())))).bind("rhs"))),
-                unless(anyOf(isInSystemHeader(), isInSkScalarDotH()))))
+                unless(anyOf(isInSystemHeader(), isInWhitelistForNaNExpr()))))
           .bind("node"),
       &NaNExpr);
 
   // First, look for direct parents of the MemberExpr.
   AstMatcher.addMatcher(
       callExpr(
           callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")),
           hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr()))
--- a/caps/tests/mochitest/chrome.ini
+++ b/caps/tests/mochitest/chrome.ini
@@ -2,11 +2,8 @@
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   file_disableScript.html
   !/caps/tests/mochitest/file_disableScript.html
 
 [test_bug995943.xul]
 [test_addonMayLoad.html]
 [test_disableScript.xul]
-[test_principal_jarprefix_origin_appid_appstatus.html]
-# jarPrefix test doesn't work on Windows, see bug 776296.
-skip-if = true ### Bug 1255339: blacklist because no more mozApps
deleted file mode 100644
--- a/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
+++ /dev/null
@@ -1,503 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=758258
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for nsIPrincipal jarPrefix, origin, appStatus and appId</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=758258">Mozilla Bug 758258</a>
-<p id="display"></p>
-<div id="content">
-
-</div>
-<pre id="test">
-<script type="application/javascript;version=1.7">
-
-/** Test for Bug 758258 **/
-
-var Ci = Components.interfaces;
-SimpleTest.waitForExplicitFinish();
-if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1) {
-  SimpleTest.expectAssertions(0, 4);
-}
-
-/*
- * gData is an array of objects. Each object represents a test case.
- * - app: gives the app manifest URL, will set mozapp to it on the iframe;
- * - origin: gives the origin of the iframe. This is the URL thas is going to
- *           to be passed as iframe.src but also the expected principal's
- *           origin.
- * - isapp: tells if the iframe is really a mozapp. If the manifest url isn't
- *          valid, the iframe will not be considered as a mozapp.
- * - browser: say if the iframe should be a mozbrowser. This is implicit when
- *            app is set.
- * - isolated: if origin isolation is enabled with browser frames.  Defaults to
- *             true if unset.
- * - test: an array of tests to run for this test case:
- *   - eo-unique: the extendedOrigin of the prinicpal must be unique in the
- *                current list.
- *   - eo-as-last: the extendedOrigin of the principal must be the same as the
- *                 last added to the list.
- */
-var gData = [
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "http://example.org/",
-    isapp: true,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "https://example.com/manifest.webapp",
-    src: "https://example.com/",
-    isapp: true,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "http://test1.example.org/manifest.webapp",
-    src: "http://test1.example.org/",
-    isapp: true,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "http://test1.example.org:8000/manifest.webapp",
-    src: "http://test1.example.org:8000/",
-    isapp: true,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "http://sub1.test1.example.org/manifest.webapp",
-    src: "http://sub1.test1.example.org/",
-    isapp: true,
-    test: [ "eo-unique" ],
-  },
-// WebApps implementation doesn't allow apps with the same origin. Sad...
-//  {
-//    app: "http://example.org/foo/manifest.webapp",
-//    src: "http://example.org/",
-//    isapp: true,
-//    test: [ "eo-unique" ],
-//  },
-//  {
-//    app: "http://example.org/bar/manifest.webapp",
-//    src: "http://example.org/",
-//    isapp: true,
-//    test: [ "eo-unique" ],
-//  },
-  {
-    src: "http://example.org/",
-    isapp: false,
-    test: [ "eo-unique" ],
-  },
-  {
-    browser: true,
-    src: "http://example.org/",
-    isapp: false,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "http://example.org/wedonthaveanyappinthatdirectory/manifest.webapp",
-    src: "http://example.org/",
-    isapp: false,
-    // TODO: this is a browser because we need apps to be browser and it's not
-    // an app because the manifest is invalid. Ideally, it should not be a
-    // browser.
-    browser: true,
-    test: [ "eo-as-last" ],
-  },
-//  {
-//    app: "http://example.org/manifest.webapp",
-//    src: "data:text/html,foobar",
-//    test: [ "todo-src" ],
-//  },
-//  {
-//    app: "http://example.org/manifest.webapp",
-//    src: "data:text/html,foobar2",
-//    test: [ "todo-src" ],
-//  },
-  {
-    src: "file:///",
-    isapp: false,
-    test: [ "eo-unique" ],
-  },
-  {
-    src: "file:///tmp/",
-    isapp: false,
-    test: [ "eo-unique" ],
-  },
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "file:///",
-    isapp: true,
-    test: [ "eo-unique", "in-app-not-installed" ],
-  },
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "file:///tmp/",
-    isapp: true,
-    test: [ "eo-unique", "in-app-not-installed" ],
-  },
-  // iframe inside an app is part of the app.
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "http://example.org/",
-    isapp: true,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: true
-    },
-    test: [ "child-has-same-eo" ],
-  },
-  // app A inside app B aren't the same app.
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "http://example.org/",
-    isapp: true,
-    child: {
-      app: "https://example.com/manifest.webapp",
-      src: "https://example.com/chrome/",
-      isapp: true
-    },
-    test: [ "child-has-different-eo", "child-has-same-appstatus", "child-has-different-appid" ],
-  },
-  // app A inside app A are the same app.
-  {
-    app: "http://example.org/manifest.webapp",
-    src: "http://example.org/",
-    isapp: true,
-    child: {
-      app: "http://example.org/manifest.webapp",
-      src: "http://example.org/chrome/",
-      isapp: true
-    },
-    test: [ "child-has-same-eo" ],
-  },
-  // app inside a regular iframe is an app.
-  {
-    src: "http://example.org/",
-    isapp: false,
-    child: {
-      app: "http://example.org/manifest.webapp",
-      src: "http://example.org/chrome/",
-      isapp: true
-    },
-    test: [ "child-has-different-eo", "child-has-different-appstatus", "child-has-different-appid" ],
-  },
-  // browser inside app is a browser, has appid but isn't installed.
-  {
-    src: "http://example.org/",
-    app: "http://example.org/manifest.webapp",
-    isapp: true,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      browser: true,
-    },
-    test: [ "child-has-different-eo", "child-has-different-appstatus", "child-has-same-appid" ],
-  },
-  // browser inside a browser are two browsers
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: true,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      browser: true,
-    },
-    test: [ "child-has-same-eo" ],
-  },
-  // browser inside a browser are two browsers
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: true,
-    child: {
-      src: "https://example.com/chrome/",
-      isapp: false,
-      browser: true,
-    },
-    test: [ "child-has-different-eo", "child-has-same-appstatus", "child-has-same-appid" ],
-  },
-  // iframe containing a browser
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: false,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      browser: true,
-    },
-    test: [ "child-has-different-eo", "child-has-same-appstatus", "child-has-same-appid" ],
-  },
-  // browser containing an iframe that is part of the browser
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: true,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      inIsolatedMozBrowser: true,
-    },
-    test: [ "child-has-same-eo" ],
-  },
-  // iframe containing a browser with isolation disabled
-  // (only chrome documents can disable isolation)
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: false,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      browser: true,
-      isolated: false,
-      inIsolatedMozBrowser: true,
-    },
-    test: [ "child-has-different-eo" ],
-  },
-  // browser with isolation disabled containing an iframe that is part of the browser
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: true,
-    isolated: false,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      inIsolatedMozBrowser: false,
-    },
-    test: [ "child-has-same-eo" ],
-  },
-  // iframe with isolation enabled containing an iframe with isolation disabled
-  // (isolated only has an effect on browsers)
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: false,
-    isolated: true,
-    child: {
-      src: "http://example.org/chrome/",
-      isapp: false,
-      browser: false,
-      isolated: false,
-    },
-    test: [ "child-has-same-eo" ],
-  },
-];
-
-// The list of all data ids generated by this test.
-var eoList = [];
-
-var content = document.getElementById('content');
-var checkedCount = 0;
-var checksTodo = gData.length;
-
-function checkIFrame(aFrame, data) {
-  var principal = aFrame.contentDocument.nodePrincipal;
-
-  if (!data.test) {
-    data.test = [];
-  }
-
-  if (navigator.platform.indexOf("Mac") != -1) {
-    is(principal.URI.spec,
-       data.src.replace('file:///tmp/', 'file:///private/tmp/'),
-       'the correct URL should have been loaded');
-  } else {
-    is(principal.URI.spec, data.src,
-       'the correct URL should have been loaded');
-  }
-
-  if (data.isapp) {
-    if (data.test.indexOf("in-app-not-installed") != -1) {
-      is(principal.appStatus, Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED, 'this should not be an installed app');
-    } else {
-      is(principal.appStatus, Ci.nsIPrincipal.APP_STATUS_INSTALLED, 'this should be an installed app');
-    }
-  } else {
-    is(principal.appStatus, Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED,
-       'this should not be an installed app');
-    is(principal.appId, Ci.nsIScriptSecurityManager.NO_APP_ID,
-       "principals from non-installed app should have NO_APP_ID");
-  }
-
-  if (!data.isapp && !data.browser ||
-      (data.browser && data.isolated === false)) {
-    is(principal.jarPrefix, "",
-       "jarPrefix should return an empty string for non-app, non-browsers, " +
-       "and browsers with isolation disabled");
-  } else {
-    isnot(principal.jarPrefix, "",
-          "jarPrefix should not return an empty string for apps or browsers " +
-          "with isolation enabled");
-  }
-
-  if (data.test.indexOf("eo-unique") != -1) {
-    is(eoList.indexOf(principal.jarPrefix + principal.origin), -1,
-       "extended origin should be unique");
-  }
-  if (data.test.indexOf("eo-as-last") != -1) {
-    is(principal.jarPrefix + principal.origin, eoList[eoList.length-1],
-       "extended origin should be the same as the last inserted one");
-  }
-
-  let isolationExpected = false;
-  if (data.isolated !== false) {
-    isolationExpected = !!data.browser;
-  }
-  is(principal.isInIsolatedMozBrowserElement, isolationExpected,
-     "check principal.isInIsolatedMozBrowserElement");
-
-  if (data.child) {
-    let childPrincipal = aFrame.contentWindow.frames[0].document.nodePrincipal;
-
-    if (data.child.isapp) {
-      is(childPrincipal.appStatus, Ci.nsIPrincipal.APP_STATUS_INSTALLED,
-         "child should be an installed app");
-    }
-
-    let childIsolationExpected = false;
-    if (data.child.isolated !== false) {
-      childIsolationExpected = !!data.child.browser;
-    }
-    if (data.child.inIsolatedMozBrowser !== undefined) {
-      childIsolationExpected = data.child.inIsolatedMozBrowser;
-    }
-    is(childPrincipal.isInIsolatedMozBrowserElement, childIsolationExpected,
-       "check childPrincipal.isInIsolatedMozBrowserElement");
-
-    if (data.test.indexOf("child-has-same-eo") != -1) {
-      is(childPrincipal.jarPrefix + childPrincipal.origin,
-         principal.jarPrefix + principal.origin,
-         "child should have the same extended origin as parent");
-      is(childPrincipal.appStatus, principal.appStatus,
-         "child should have the same appStatus if it has the same extended origin");
-      is(childPrincipal.appId, principal.appId,
-         "child should have the same appId if it has the same extended origin");
-    }
-
-    if (data.test.indexOf("child-has-different-eo") != -1) {
-      isnot(childPrincipal.jarPrefix + childPrincipal.origin,
-            principal.jarPrefix + principal.origin,
-            "child should not have the same extended origin as parent");
-    }
-
-    if (data.test.indexOf("child-has-same-appstatus") != -1) {
-      is(childPrincipal.appStatus, principal.appStatus,
-         "childPrincipal and parent principal should have the same appStatus");
-    }
-
-    if (data.test.indexOf("child-has-different-appstatus") != -1) {
-      isnot(childPrincipal.appStatus, principal.appStatus,
-            "childPrincipal and parent principal should not have the same appStatus");
-    }
-
-    if (data.test.indexOf("child-has-same-appid") != -1) {
-      is(childPrincipal.appId, principal.appId,
-         "childPrincipal and parent principal should have the same appId");
-    }
-
-    if (data.test.indexOf("child-has-different-appid") != -1) {
-      isnot(childPrincipal.appId, principal.appId,
-            "childPrincipal and parent principal should have different appId");
-    }
-  }
-
-  eoList.push(principal.jarPrefix + principal.origin);
-
-  checkedCount++;
-  if (checkedCount == checksTodo) {
-    SpecialPowers.removePermission("browser", "http://example.org");
-    SpecialPowers.removePermission("embed-apps", "http://example.org");
-    SimpleTest.finish();
-  } else {
-    gTestRunner.next();
-  }
-}
-
-is('appStatus' in document.nodePrincipal, true,
-   'appStatus should be present in nsIPrincipal');
-is('jarPrefix' in document.nodePrincipal, true,
-   'jarPrefix should be present in nsIPrincipal');
-is('origin' in document.nodePrincipal, true,
-   'origin should be present in nsIPrincipal');
-is('appId' in document.nodePrincipal, true,
-   'appId should be present in nsIPrincipal');
-
-function runTest() {
-  // We want to use a generator. Those only work in a one level stack so we
-  // can't use .forEach() here.
-  for (var i=0; i<gData.length; ++i) {
-    let data = gData[i];
-
-    var iframe = document.createElement('iframe');
-    iframe.check = function() {
-      checkIFrame(this, data);
-    };
-    iframe.addChild = function() {
-      SpecialPowers.addPermission("browser", true, iframe.contentDocument);
-      SpecialPowers.addPermission("embed-apps", true, iframe.contentDocument);
-
-      var childFrame = document.createElement('iframe');
-
-      if (data.child.app) {
-        childFrame.setAttribute('mozapp', data.child.app)
-        childFrame.setAttribute('mozbrowser', '');
-      } else if (data.child.browser) {
-        childFrame.setAttribute('mozbrowser', '');
-      }
-
-      if (data.child.isolated === false) {
-        childFrame.setAttribute("noisolation", "");
-      }
-
-      childFrame.src = data.child.src;
-
-      this.removeEventListener('load', this.addChild.bind(this));
-      childFrame.addEventListener('load', this.check.bind(this));
-
-      this.contentDocument.body.appendChild(childFrame);
-    };
-
-    if (data.app) {
-      iframe.setAttribute('mozapp', data.app);
-      iframe.setAttribute('mozbrowser', '');
-    } else if (data.browser) {
-      iframe.setAttribute('mozbrowser', '');
-    }
-
-    if (data.isolated === false) {
-      iframe.setAttribute("noisolation", "");
-    }
-
-    iframe.src = data.src;
-
-    if (data.child) {
-      iframe.addEventListener('load', iframe.addChild.bind(iframe));
-    } else {
-      iframe.addEventListener('load', iframe.check.bind(iframe));
-    }
-
-    content.appendChild(iframe);
-
-    yield;
-  }
-}
-
-var gTestRunner = runTest();
-
-SpecialPowers.pushPrefEnv({"set": [
-  ["dom.mozBrowserFramesEnabled", true],
-]}, function() { gTestRunner.next(); });
-
-</script>
-</pre>
-</body>
-</html>
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -462,16 +462,37 @@ Tools.firebugTheme = {
 };
 
 exports.defaultThemes = [
   Tools.darkTheme,
   Tools.lightTheme,
   Tools.firebugTheme,
 ];
 
+// White-list buttons that can be toggled to prevent adding prefs for
+// addons that have manually inserted toolbarbuttons into DOM.
+// (By default, supported target is only local tab)
+exports.ToolboxButtons = [
+  { id: "command-button-frames",
+    isTargetSupported: target => {
+      return target.activeTab && target.activeTab.traits.frames;
+    }
+  },
+  { id: "command-button-splitconsole",
+    isTargetSupported: target => !target.isAddon },
+  { id: "command-button-responsive" },
+  { id: "command-button-paintflashing" },
+  { id: "command-button-scratchpad" },
+  { id: "command-button-screenshot" },
+  { id: "command-button-rulers" },
+  { id: "command-button-measure" },
+  { id: "command-button-noautohide",
+    isTargetSupported: target => target.chrome },
+];
+
 /**
  * Lookup l10n string from a string bundle.
  *
  * @param {string} name
  *        The key to lookup.
  * @param {string} arg
  *        Optional format argument.
  * @returns A localized version of the given key.
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -56,43 +56,24 @@ loader.lazyRequireGetter(this, "system",
   "devtools/shared/system");
 loader.lazyRequireGetter(this, "getPreferenceFront",
   "devtools/shared/fronts/preference", true);
 loader.lazyRequireGetter(this, "KeyShortcuts",
   "devtools/client/shared/key-shortcuts", true);
 loader.lazyRequireGetter(this, "ZoomKeys",
   "devtools/client/shared/zoom-keys");
 loader.lazyRequireGetter(this, "settleAll",
-  "devtools/shared/ThreadSafeDevToolsUtils", "settleAll");
+  "devtools/shared/ThreadSafeDevToolsUtils", true);
+loader.lazyRequireGetter(this, "ToolboxButtons",
+  "devtools/client/definitions", true);
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/har/toolbox-overlay").register;
 });
 
-// White-list buttons that can be toggled to prevent adding prefs for
-// addons that have manually inserted toolbarbuttons into DOM.
-// (By default, supported target is only local tab)
-const ToolboxButtons = exports.ToolboxButtons = [
-  { id: "command-button-frames",
-    isTargetSupported: target => {
-      return target.activeTab && target.activeTab.traits.frames;
-    }
-  },
-  { id: "command-button-splitconsole",
-    isTargetSupported: target => !target.isAddon },
-  { id: "command-button-responsive" },
-  { id: "command-button-paintflashing" },
-  { id: "command-button-scratchpad" },
-  { id: "command-button-screenshot" },
-  { id: "command-button-rulers" },
-  { id: "command-button-measure" },
-  { id: "command-button-noautohide",
-    isTargetSupported: target => target.chrome },
-];
-
 /**
  * A "Toolbox" is the component that holds all the tools for one specific
  * target. Visually, it's a document that includes the tools tabs and all
  * the iframes where the tool panels will be living in.
  *
  * @param {object} target
  *        The object the toolbox is debugging.
  * @param {string} selectedTool
--- a/devtools/client/locales/en-US/responsive.properties
+++ b/devtools/client/locales/en-US/responsive.properties
@@ -53,8 +53,14 @@ responsive.screenshot=Take a screenshot 
 # The first argument (%1$S) is the date string in yyyy-mm-dd format and the
 # second argument (%2$S) is the time string in HH.MM.SS format.
 responsive.screenshotGeneratedFilename=Screen Shot %1$S at %2$S
 
 # LOCALIZATION NOTE (responsive.remoteOnly): Message displayed in the tab's
 # notification box if a user tries to open Responsive Design Mode in a
 # non-remote tab.
 responsive.remoteOnly=Responsive Design Mode is only available for remote browser tabs, such as those used for web content in multi-process Firefox.
+
+# LOCALIZATION NOTE (responsive.noThrottling): UI option in a menu to configure
+# network throttling.  This option is the default and disables throttling so you
+# just have normal network conditions.  There is not very much room in the UI
+# so a short string would be best if possible.
+responsive.noThrottling=No throttling
--- a/devtools/client/responsive.html/actions/index.js
+++ b/devtools/client/responsive.html/actions/index.js
@@ -19,43 +19,46 @@ createEnum([
   "ADD_DEVICE_TYPE",
 
   // Add an additional viewport to display the document.
   "ADD_VIEWPORT",
 
   // Change the device displayed in the viewport.
   "CHANGE_DEVICE",
 
-  // The location of the page has changed.  This may be triggered by the user
+  // Change the location of the page.  This may be triggered by the user
   // directly entering a new URL, navigating with links, etc.
   "CHANGE_LOCATION",
 
+  // Change the network throttling profile.
+  "CHANGE_NETWORK_THROTTLING",
+
+  // Indicates that the device list is being loaded
+  "LOAD_DEVICE_LIST_START",
+
+  // Indicates that the device list loading action threw an error
+  "LOAD_DEVICE_LIST_ERROR",
+
+  // Indicates that the device list has been loaded successfully
+  "LOAD_DEVICE_LIST_END",
+
   // Resize the viewport.
   "RESIZE_VIEWPORT",
 
   // Rotate the viewport.
   "ROTATE_VIEWPORT",
 
   // Take a screenshot of the viewport.
   "TAKE_SCREENSHOT_START",
 
   // Indicates when the screenshot action ends.
   "TAKE_SCREENSHOT_END",
 
   // Update the device display state in the device selector.
   "UPDATE_DEVICE_DISPLAYED",
 
-  // Indicates that the device list is being loaded
-  "LOAD_DEVICE_LIST_START",
-
-  // Indicates that the device list loading action threw an error
-  "LOAD_DEVICE_LIST_ERROR",
-
-  // Indicates that the device list has been loaded successfully
-  "LOAD_DEVICE_LIST_END",
-
   // Update the device modal open state.
   "UPDATE_DEVICE_MODAL_OPEN",
 
   // Update the touch simulation enabled state.
   "UPDATE_TOUCH_SIMULATION_ENABLED",
 
 ], module.exports);
--- a/devtools/client/responsive.html/actions/moz.build
+++ b/devtools/client/responsive.html/actions/moz.build
@@ -3,12 +3,13 @@
 # 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/.
 
 DevToolsModules(
     'devices.js',
     'index.js',
     'location.js',
+    'network-throttling.js',
     'screenshot.js',
     'touch-simulation.js',
     'viewports.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/actions/network-throttling.js
@@ -0,0 +1,21 @@
+/* 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 {
+  CHANGE_NETWORK_THROTTLING,
+} = require("./index");
+
+module.exports = {
+
+  changeNetworkThrottling(enabled, profile) {
+    return {
+      type: CHANGE_NETWORK_THROTTLING,
+      enabled,
+      profile,
+    };
+  },
+
+};
--- a/devtools/client/responsive.html/app.js
+++ b/devtools/client/responsive.html/app.js
@@ -10,43 +10,54 @@ const { createClass, createFactory, Prop
   require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const {
   updateDeviceDisplayed,
   updateDeviceModalOpen,
   updatePreferredDevices,
 } = require("./actions/devices");
+const { changeNetworkThrottling } = require("./actions/network-throttling");
+const { takeScreenshot } = require("./actions/screenshot");
+const { updateTouchSimulationEnabled } = require("./actions/touch-simulation");
 const {
   changeDevice,
   resizeViewport,
   rotateViewport
 } = require("./actions/viewports");
-const { takeScreenshot } = require("./actions/screenshot");
-const { updateTouchSimulationEnabled } = require("./actions/touch-simulation");
 const DeviceModal = createFactory(require("./components/device-modal"));
 const GlobalToolbar = createFactory(require("./components/global-toolbar"));
 const Viewports = createFactory(require("./components/viewports"));
 const Types = require("./types");
 
 let App = createClass({
   displayName: "App",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
     location: Types.location.isRequired,
+    networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
     viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
   },
 
   onBrowserMounted() {
     window.postMessage({ type: "browser-mounted" }, "*");
   },
 
+  onChangeNetworkThrottling(enabled, profile) {
+    window.postMessage({
+      type: "change-network-throtting",
+      enabled,
+      profile,
+    }, "*");
+    this.props.dispatch(changeNetworkThrottling(enabled, profile));
+  },
+
   onChangeViewportDevice(id, device) {
     window.postMessage({
       type: "change-viewport-device",
       device,
     }, "*");
     this.props.dispatch(changeDevice(id, device.name));
     this.props.dispatch(updateTouchSimulationEnabled(device.touch));
   },
@@ -95,23 +106,25 @@ let App = createClass({
 
     this.props.dispatch(updateTouchSimulationEnabled(isEnabled));
   },
 
   render() {
     let {
       devices,
       location,
+      networkThrottling,
       screenshot,
       touchSimulation,
       viewports,
     } = this.props;
 
     let {
       onBrowserMounted,
+      onChangeNetworkThrottling,
       onChangeViewportDevice,
       onContentResize,
       onDeviceListUpdate,
       onExit,
       onResizeViewport,
       onRotateViewport,
       onScreenshot,
       onUpdateDeviceDisplayed,
@@ -119,18 +132,20 @@ let App = createClass({
       onUpdateTouchSimulation,
     } = this;
 
     return dom.div(
       {
         id: "app",
       },
       GlobalToolbar({
+        networkThrottling,
         screenshot,
         touchSimulation,
+        onChangeNetworkThrottling,
         onExit,
         onScreenshot,
         onUpdateTouchSimulation,
       }),
       Viewports({
         devices,
         location,
         screenshot,
--- a/devtools/client/responsive.html/components/global-toolbar.js
+++ b/devtools/client/responsive.html/components/global-toolbar.js
@@ -1,36 +1,42 @@
 /* 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 { DOM: dom, createClass, createFactory, PropTypes, addons } =
+  require("devtools/client/shared/vendor/react");
+
 const { getStr } = require("../utils/l10n");
-const { DOM: dom, createClass, PropTypes, addons } =
-  require("devtools/client/shared/vendor/react");
 const Types = require("../types");
+const NetworkThrottlingSelector = createFactory(require("./network-throttling-selector"));
 
 module.exports = createClass({
   displayName: "GlobalToolbar",
 
   propTypes: {
+    networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
     screenshot: PropTypes.shape(Types.screenshot).isRequired,
     touchSimulation: PropTypes.shape(Types.touchSimulation).isRequired,
+    onChangeNetworkThrottling: PropTypes.func.isRequired,
     onExit: PropTypes.func.isRequired,
     onScreenshot: PropTypes.func.isRequired,
     onUpdateTouchSimulation: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   render() {
     let {
+      networkThrottling,
       screenshot,
       touchSimulation,
+      onChangeNetworkThrottling,
       onExit,
       onScreenshot,
       onUpdateTouchSimulation
     } = this.props;
 
     let touchButtonClass = "toolbar-button devtools-button";
     if (touchSimulation.enabled) {
       touchButtonClass += " active";
@@ -40,17 +46,22 @@ module.exports = createClass({
       {
         id: "global-toolbar",
         className: "container",
       },
       dom.span(
         {
           className: "title",
         },
-        getStr("responsive.title")),
+        getStr("responsive.title")
+      ),
+      NetworkThrottlingSelector({
+        networkThrottling,
+        onChangeNetworkThrottling,
+      }),
       dom.button({
         id: "global-touch-simulation-button",
         className: touchButtonClass,
         title: (touchSimulation.enabled ?
           getStr("responsive.disableTouch") : getStr("responsive.enableTouch")),
         onClick: () => onUpdateTouchSimulation(!touchSimulation.enabled),
       }),
       dom.button({
--- a/devtools/client/responsive.html/components/moz.build
+++ b/devtools/client/responsive.html/components/moz.build
@@ -4,14 +4,15 @@
 # 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/.
 
 DevToolsModules(
     'browser.js',
     'device-modal.js',
     'device-selector.js',
     'global-toolbar.js',
+    'network-throttling-selector.js',
     'resizable-viewport.js',
     'viewport-dimension.js',
     'viewport-toolbar.js',
     'viewport.js',
     'viewports.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/components/network-throttling-selector.js
@@ -0,0 +1,92 @@
+/* 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 { DOM: dom, createClass, PropTypes, addons } =
+  require("devtools/client/shared/vendor/react");
+
+const Types = require("../types");
+const { getStr } = require("../utils/l10n");
+const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
+
+module.exports = createClass({
+
+  displayName: "NetworkThrottlingSelector",
+
+  propTypes: {
+    networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
+    onChangeNetworkThrottling: PropTypes.func.isRequired,
+  },
+
+  mixins: [ addons.PureRenderMixin ],
+
+  onSelectChange({ target }) {
+    let {
+      onChangeNetworkThrottling,
+    } = this.props;
+
+    if (target.value == getStr("responsive.noThrottling")) {
+      onChangeNetworkThrottling(false, "");
+      return;
+    }
+
+    for (let profile of throttlingProfiles) {
+      if (profile.id === target.value) {
+        onChangeNetworkThrottling(true, profile.id);
+        return;
+      }
+    }
+  },
+
+  render() {
+    let {
+      networkThrottling,
+    } = this.props;
+
+    let selectClass = "";
+    let selectedProfile;
+    if (networkThrottling.enabled) {
+      selectClass += " selected";
+      selectedProfile = networkThrottling.profile;
+    } else {
+      selectedProfile = getStr("responsive.noThrottling");
+    }
+
+    let listContent = [
+      dom.option(
+        {
+          key: "disabled",
+        },
+        getStr("responsive.noThrottling")
+      ),
+      dom.option(
+        {
+          key: "divider",
+          className: "divider",
+          disabled: true,
+        }
+      ),
+      throttlingProfiles.map(profile => {
+        return dom.option(
+          {
+            key: profile.id,
+          },
+          profile.id
+        );
+      }),
+    ];
+
+    return dom.select(
+      {
+        id: "global-network-throttling-selector",
+        className: selectClass,
+        value: selectedProfile,
+        onChange: this.onSelectChange,
+      },
+      ...listContent
+    );
+  },
+
+});
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -50,17 +50,17 @@ html, body {
   flex-direction: column;
   padding-top: 15px;
   padding-bottom: 1%;
   position: relative;
   height: 100%;
 }
 
 /**
- * Common style for containers and toolbar buttons
+ * Common styles for shared components
  */
 
 .container {
   background-color: var(--theme-toolbar-background);
   border: 1px solid var(--theme-splitter-color);
 }
 
 .toolbar-button {
@@ -72,16 +72,67 @@ html, body {
   min-height: initial;
   align-self: center;
 }
 
 .toolbar-button:active::before {
   filter: url("chrome://devtools/skin/images/filters.svg#checked-icon-state");
 }
 
+select {
+  -moz-appearance: none;
+  background-color: var(--theme-toolbar-background);
+  background-image: var(--viewport-selection-arrow);
+  background-position: 100% 50%;
+  background-repeat: no-repeat;
+  background-size: 7px;
+  border: none;
+  color: var(--viewport-color);
+  padding: 0 8px;
+  text-align: center;
+  text-overflow: ellipsis;
+  font-size: 11px;
+}
+
+select.selected {
+  background-image: var(--viewport-selection-arrow-selected);
+  color: var(--viewport-active-color);
+}
+
+select:hover {
+  background-image: var(--viewport-selection-arrow-hovered);
+  color: var(--viewport-hover-color);
+}
+
+/* This is (believed to be?) separate from the identical select.selected rule
+   set so that it overrides select:hover because of file ordering once the
+   select is focused.  It's unclear whether the visual effect that results here
+   is intentional and desired. */
+select:focus {
+  background-image: var(--viewport-selection-arrow-selected);
+  color: var(--viewport-active-color);
+}
+
+select > option {
+  text-align: left;
+  padding: 5px 10px;
+}
+
+select > option,
+select > option:hover {
+  color: var(--viewport-active-color);
+}
+
+select > option.divider {
+  border-top: 1px solid var(--theme-splitter-color);
+  height: 0px;
+  padding: 0;
+  font-size: 0px;
+}
+
 /**
  * Global Toolbar
  */
 
 #global-toolbar {
   color: var(--theme-body-color-alt);
   border-radius: 2px;
   box-shadow: var(--rdm-box-shadow);
@@ -126,16 +177,21 @@ html, body {
   margin: -6px 0 0 -6px;
 }
 
 #global-screenshot-button:disabled {
   filter: url("chrome://devtools/skin/images/filters.svg#checked-icon-state");
   opacity: 1 !important;
 }
 
+#global-network-throttling-selector {
+  height: 15px;
+  padding-left: 0;
+  width: 103px;
+}
 
 #viewports {
   /* Make sure left-most viewport is visible when there's horizontal overflow.
      That is, when the horizontal space become smaller than the viewports and a
      scrollbar appears, then the first viewport will still be visible */
   position: sticky;
   left: 0;
   /* Individual viewports are inline elements, make sure they stay on a single
@@ -167,60 +223,16 @@ html, body {
   border-width: 0;
   border-bottom-width: 1px;
   display: flex;
   flex-direction: row;
   justify-content: center;
   height: 18px;
 }
 
-.viewport-device-selector {
-  -moz-appearance: none;
-  background-color: var(--theme-toolbar-background);
-  background-image: var(--viewport-selection-arrow);
-  background-position: 100% 52%;
-  background-repeat: no-repeat;
-  background-size: 7px;
-  border: none;
-  color: var(--viewport-color);
-  height: 100%;
-  padding: 0 8px 0 8px;
-  text-align: center;
-  text-overflow: ellipsis;
-  width: 150px;
-  font-size: 11px;
-  width: -moz-fit-content;
-}
-
-.viewport-device-selector.selected {
-  background-image: var(--viewport-selection-arrow-selected);
-  color: var(--viewport-active-color);
-}
-
-.viewport-device-selector:hover {
-  background-image: var(--viewport-selection-arrow-hovered);
-  color: var(--viewport-hover-color);
-}
-
-.viewport-device-selector:focus {
-  background-image: var(--viewport-selection-arrow-selected);
-  color: var(--viewport-active-color);
-}
-
-.viewport-device-selector > option {
-  text-align: left;
-  padding: 5px 10px;
-}
-
-.viewport-device-selector > option,
-.viewport-device-selector > option:hover,
-.viewport-device-selector:hover > option:hover {
-  color: var(--viewport-active-color);
-}
-
 .viewport-rotate-button {
   position: absolute;
   right: 0;
 }
 
 .viewport-rotate-button::before {
   background-image: url("./images/rotate-viewport.svg");
 }
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -9,25 +9,25 @@ const promise = require("promise");
 const { Task } = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
 const { getOwnerWindow } = require("sdk/tabs/utils");
 const { startup } = require("sdk/window/helpers");
 const message = require("./utils/message");
 const { swapToInnerBrowser } = require("./browser/swap");
 const { EmulationFront } = require("devtools/shared/fronts/emulation");
 const { getStr } = require("./utils/l10n");
-const { TargetFactory } = require("devtools/client/framework/target");
-const { gDevTools } = require("devtools/client/framework/devtools");
 
 const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
 
-loader.lazyRequireGetter(this, "DebuggerClient",
-                         "devtools/shared/client/main", true);
-loader.lazyRequireGetter(this, "DebuggerServer",
-                         "devtools/server/main", true);
+loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
+loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
+loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
+loader.lazyRequireGetter(this, "throttlingProfiles",
+  "devtools/client/shared/network-throttling-profiles");
 
 /**
  * ResponsiveUIManager is the external API for the browser UI, etc. to use when
  * opening and closing the responsive UI.
  *
  * While the HTML UI is in an experimental stage, the older ResponsiveUIManager
  * from devtools/client/responsivedesign/responsivedesign.jsm delegates to this
  * object when the pref "devtools.responsive.html.enabled" is true.
@@ -357,32 +357,31 @@ ResponsiveUI.prototype = {
       yield this.inited;
     }
 
     this.tab.removeEventListener("TabClose", this);
     this.browserWindow.removeEventListener("unload", this);
     this.toolWindow.removeEventListener("message", this);
 
     if (!isTabClosing) {
-      // Stop the touch event simulator if it was running
-      yield this.emulationFront.clearTouchEventsOverride();
-
       // Notify the inner browser to stop the frame script
       yield message.request(this.toolWindow, "stop-frame-script");
     }
 
     // Destroy local state
     let swap = this.swap;
     this.browserWindow = null;
     this.tab = null;
     this.inited = null;
     this.toolWindow = null;
     this.swap = null;
 
-    // Close the debugger client used to speak with emulation actor
+    // Close the debugger client used to speak with emulation actor.
+    // The actor handles clearing any overrides itself, so it's not necessary to clear
+    // anything on shutdown client side.
     let clientClosed = this.client.close();
     if (!isTabClosing) {
       yield clientClosed;
     }
     this.client = this.emulationFront = null;
 
     if (!isWindowClosing) {
       // Undo the swap and return the content back to a normal tab
@@ -417,74 +416,113 @@ ResponsiveUI.prototype = {
         ResponsiveUIManager.closeIfNeeded(browserWindow, tab, {
           reason: event.type,
         });
         break;
     }
   },
 
   handleMessage(event) {
-    let { browserWindow, tab } = this;
-
     if (event.origin !== "chrome://devtools") {
       return;
     }
 
     switch (event.data.type) {
+      case "change-network-throtting":
+        this.onChangeNetworkThrottling(event);
+        break;
       case "change-viewport-device":
-        let { userAgent, pixelRatio, touch } = event.data.device;
-        this.updateUserAgent(userAgent);
-        this.updateDPPX(pixelRatio);
-        this.updateTouchSimulation(touch);
+        this.onChangeViewportDevice(event);
         break;
       case "content-resize":
-        let { width, height } = event.data;
-        this.emit("content-resize", {
-          width,
-          height,
-        });
+        this.onContentResize(event);
         break;
       case "exit":
-        ResponsiveUIManager.closeIfNeeded(browserWindow, tab);
+        this.onExit();
         break;
       case "update-touch-simulation":
-        let { enabled } = event.data;
-        this.updateTouchSimulation(enabled);
+        this.onUpdateTouchSimulation(event);
         break;
     }
   },
 
-  updateTouchSimulation: Task.async(function* (enabled) {
-    if (enabled) {
-      let reloadNeeded = yield this.emulationFront.setTouchEventsOverride(
-        Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED
-      );
-      if (reloadNeeded) {
-        this.getViewportBrowser().reload();
-      }
-    } else {
-      this.emulationFront.clearTouchEventsOverride();
+  onChangeNetworkThrottling: Task.async(function* (event) {
+    let { enabled, profile } = event.data;
+    yield this.updateNetworkThrottling(enabled, profile);
+    // Used by tests
+    this.emit("network-throttling-changed");
+  }),
+
+  onChangeViewportDevice(event) {
+    let { userAgent, pixelRatio, touch } = event.data.device;
+    this.updateUserAgent(userAgent);
+    this.updateDPPX(pixelRatio);
+    this.updateTouchSimulation(touch);
+  },
+
+  onContentResize(event) {
+    let { width, height } = event.data;
+    this.emit("content-resize", {
+      width,
+      height,
+    });
+  },
+
+  onExit() {
+    let { browserWindow, tab } = this;
+    ResponsiveUIManager.closeIfNeeded(browserWindow, tab);
+  },
+
+  onUpdateTouchSimulation(event) {
+    let { enabled } = event.data;
+    this.updateTouchSimulation(enabled);
+  },
+
+  updateDPPX(dppx) {
+    if (!dppx) {
+      this.emulationFront.clearDPPXOverride();
+      return;
     }
+    this.emulationFront.setDPPXOverride(dppx);
+  },
+
+  updateNetworkThrottling: Task.async(function* (enabled, profile) {
+    if (!enabled) {
+      yield this.emulationFront.clearNetworkThrottling();
+      return;
+    }
+    let data = throttlingProfiles.find(({ id }) => id == profile);
+    let { download, upload, latency } = data;
+    yield this.emulationFront.setNetworkThrottling({
+      downloadThroughput: download,
+      uploadThroughput: upload,
+      latency,
+    });
   }),
 
   updateUserAgent(userAgent) {
-    if (userAgent) {
-      this.emulationFront.setUserAgentOverride(userAgent);
-    } else {
+    if (!userAgent) {
       this.emulationFront.clearUserAgentOverride();
+      return;
     }
+    this.emulationFront.setUserAgentOverride(userAgent);
   },
 
-  updateDPPX(dppx) {
-    if (dppx) {
-      this.emulationFront.setDPPXOverride(dppx);
-    } else {
-      this.emulationFront.clearDPPXOverride();
+  updateTouchSimulation: Task.async(function* (enabled) {
+    if (!enabled) {
+      yield this.emulationFront.clearTouchEventsOverride();
+      return;
     }
-  },
+    let reloadNeeded = yield this.emulationFront.setTouchEventsOverride(
+      Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED
+    );
+    if (reloadNeeded) {
+      this.getViewportBrowser().reload();
+    }
+  }),
 
   /**
    * Helper for tests. Assumes a single viewport for now.
    */
   getViewportSize() {
     return this.toolWindow.getViewportSize();
   },
 
--- a/devtools/client/responsive.html/reducers.js
+++ b/devtools/client/responsive.html/reducers.js
@@ -1,11 +1,12 @@
 /* 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";
 
 exports.devices = require("./reducers/devices");
 exports.location = require("./reducers/location");
+exports.networkThrottling = require("./reducers/network-throttling");
 exports.screenshot = require("./reducers/screenshot");
 exports.touchSimulation = require("./reducers/touch-simulation");
 exports.viewports = require("./reducers/viewports");
--- a/devtools/client/responsive.html/reducers/moz.build
+++ b/devtools/client/responsive.html/reducers/moz.build
@@ -2,12 +2,13 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'devices.js',
     'location.js',
+    'network-throttling.js',
     'screenshot.js',
     'touch-simulation.js',
     'viewports.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/reducers/network-throttling.js
@@ -0,0 +1,33 @@
+/* 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 {
+  CHANGE_NETWORK_THROTTLING,
+} = require("../actions/index");
+
+const INITIAL_NETWORK_THROTTLING = {
+  enabled: false,
+  profile: "",
+};
+
+let reducers = {
+
+  [CHANGE_NETWORK_THROTTLING](throttling, { enabled, profile }) {
+    return {
+      enabled,
+      profile,
+    };
+  },
+
+};
+
+module.exports = function (throttling = INITIAL_NETWORK_THROTTLING, action) {
+  let reducer = reducers[action.type];
+  if (!reducer) {
+    return throttling;
+  }
+  return reducer(throttling, action);
+};
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -22,16 +22,17 @@ support-files =
 [browser_device_modal_submit.js]
 [browser_device_width.js]
 [browser_exit_button.js]
 [browser_frame_script_active.js]
 [browser_menu_item_01.js]
 [browser_menu_item_02.js]
 [browser_mouse_resize.js]
 [browser_navigation.js]
+[browser_network_throttling.js]
 [browser_page_state.js]
 [browser_permission_doorhanger.js]
 [browser_resize_cmd.js]
 [browser_screenshot_button.js]
 [browser_shutdown_close_sync.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
 [browser_toolbox_swap_browsers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_network_throttling.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const throttlingProfiles = require("devtools/client/shared/network-throttling-profiles");
+
+// Tests changing network throttling
+const TEST_URL = "data:text/html;charset=utf-8,Network throttling test";
+
+addRDMTask(TEST_URL, function* ({ ui, manager }) {
+  let { store } = ui.toolWindow;
+
+  // Wait until the viewport has been added
+  yield waitUntilState(store, state => state.viewports.length == 1);
+
+  // Test defaults
+  testNetworkThrottlingSelectorLabel(ui, "No throttling");
+  yield testNetworkThrottlingState(ui, null);
+
+  // Test a fast profile
+  yield testThrottlingProfile(ui, "Wi-Fi");
+
+  // Test a slower profile
+  yield testThrottlingProfile(ui, "Regular 3G");
+
+  // Test switching back to no throttling
+  let changed = once(ui, "network-throttling-changed");
+  yield switchNetworkThrottling(ui, "No throttling");
+  yield changed;
+  testNetworkThrottlingSelectorLabel(ui, "No throttling");
+  yield testNetworkThrottlingState(ui, null);
+});
+
+function testNetworkThrottlingSelectorLabel(ui, expected) {
+  let selector = "#global-network-throttling-selector";
+  let select = ui.toolWindow.document.querySelector(selector);
+  is(select.selectedOptions[0].textContent, expected,
+    `Select label should be changed to ${expected}`);
+}
+
+var testNetworkThrottlingState = Task.async(function* (ui, expected) {
+  let state = yield ui.emulationFront.getNetworkThrottling();
+  Assert.deepEqual(state, expected, "Network throttling state should be " +
+                                    JSON.stringify(expected, null, 2));
+});
+
+var testThrottlingProfile = Task.async(function* (ui, profile) {
+  let changed = once(ui, "network-throttling-changed");
+  yield switchNetworkThrottling(ui, profile);
+  yield changed;
+  testNetworkThrottlingSelectorLabel(ui, profile);
+  let data = throttlingProfiles.find(({ id }) => id == profile);
+  let { download, upload, latency } = data;
+  yield testNetworkThrottlingState(ui, {
+    downloadThroughput: download,
+    uploadThroughput: upload,
+    latency,
+  });
+});
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -225,19 +225,18 @@ function openDeviceModal(ui) {
     ui.toolWindow);
   EventUtils.synthesizeMouseAtCenter(editDeviceOption, {type: "mouseup"},
     ui.toolWindow);
 
   ok(modal.classList.contains("opened") && !modal.classList.contains("closed"),
     "The device modal is displayed.");
 }
 
-function switchDevice({ toolWindow }, value) {
+function switchSelector({ toolWindow }, selector, value) {
   return new Promise(resolve => {
-    let selector = ".viewport-device-selector";
     let select = toolWindow.document.querySelector(selector);
     isnot(select, null, `selector "${selector}" should match an existing element.`);
 
     let option = [...select.options].find(o => o.value === String(value));
     isnot(option, undefined, `value "${value}" should match an existing option.`);
 
     let event = new toolWindow.UIEvent("change", {
       view: toolWindow,
@@ -251,16 +250,24 @@ function switchDevice({ toolWindow }, va
       resolve();
     }, { once: true });
 
     select.value = value;
     select.dispatchEvent(event);
   });
 }
 
+function switchDevice(ui, value) {
+  return switchSelector(ui, ".viewport-device-selector", value);
+}
+
+function switchNetworkThrottling(ui, value) {
+  return switchSelector(ui, "#global-network-throttling-selector", value);
+}
+
 function getSessionHistory(browser) {
   return ContentTask.spawn(browser, {}, function* () {
     /* eslint-disable no-undef */
     let { interfaces: Ci } = Components;
     let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
     let sessionHistory = webNav.sessionHistory;
     let result = {
       index: sessionHistory.index,
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/unit/test_change_network_throttling.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test changing the network throttling state
+
+const {
+  changeNetworkThrottling,
+} = require("devtools/client/responsive.html/actions/network-throttling");
+
+add_task(function* () {
+  let store = Store();
+  const { getState, dispatch } = store;
+
+  ok(!getState().networkThrottling.enabled,
+    "Network throttling is disabled by default.");
+  equal(getState().networkThrottling.profile, "",
+    "Network throttling profile is empty by default.");
+
+  dispatch(changeNetworkThrottling(true, "Bob"));
+
+  ok(getState().networkThrottling.enabled,
+    "Network throttling is enabled.");
+  equal(getState().networkThrottling.profile, "Bob",
+    "Network throttling profile is set.");
+});
--- a/devtools/client/responsive.html/test/unit/xpcshell.ini
+++ b/devtools/client/responsive.html/test/unit/xpcshell.ini
@@ -3,13 +3,14 @@ tags = devtools
 head = head.js ../../../framework/test/shared-redux-head.js
 tail =
 firefox-appdir = browser
 
 [test_add_device.js]
 [test_add_device_type.js]
 [test_add_viewport.js]
 [test_change_location.js]
+[test_change_network_throttling.js]
 [test_change_viewport_device.js]
 [test_resize_viewport.js]
 [test_rotate_viewport.js]
 [test_update_device_displayed.js]
 [test_update_touch_simulation_enabled.js]
--- a/devtools/client/responsive.html/types.js
+++ b/devtools/client/responsive.html/types.js
@@ -90,37 +90,50 @@ exports.devices = {
  */
 exports.location = PropTypes.string;
 
 /**
  * The progression of the screenshot
  */
 exports.screenshot = {
 
-  isCapturing: PropTypes.bool.isRequired,
+  isCapturing: PropTypes.bool,
 
 };
 
 /**
  * Touch simulation.
  */
 exports.touchSimulation = {
 
-  // Whether or not the touch simulation is enabled
-  enabled: PropTypes.bool.isRequired,
+  // Whether or not touch simulation is enabled
+  enabled: PropTypes.bool,
+
+};
+
+/**
+ * Network throttling.
+ */
+exports.networkThrottling = {
+
+  // Whether or not network throttling is enabled
+  enabled: PropTypes.bool,
+
+  // Name of the selected throttling profile
+  profile: PropTypes.string,
 
 };
 
 /**
  * A single viewport displaying a document.
  */
 exports.viewport = {
 
   // The id of the viewport
-  id: PropTypes.number.isRequired,
+  id: PropTypes.number,
 
   // The currently selected device applied to the viewport.
   device: PropTypes.string,
 
   // The width of the viewport
   width: PropTypes.number,
 
   // The height of the viewport
--- a/devtools/client/shared/components/test/mochitest/test_reps_event.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_event.html
@@ -244,17 +244,17 @@ window.onload = Task.async(function* () 
                 "ownPropertyLength": 760,
                 "preview": {
                   "kind": "ObjectWithURL",
                   "url": ""
                 }
               },
               "ports": {
                 "type": "object",
-                "class": "MessagePortList",
+                "class": "Array",
                 "actor": "server1.conn3.obj37",
                 "extensible": true,
                 "frozen": false,
                 "sealed": false,
                 "ownPropertyLength": 0
               },
               "currentTarget": {
                 "type": "object",
--- a/devtools/client/shared/moz.build
+++ b/devtools/client/shared/moz.build
@@ -31,16 +31,17 @@ DevToolsModules(
     'file-watcher-worker.js',
     'file-watcher.js',
     'frame-script-utils.js',
     'getjson.js',
     'inplace-editor.js',
     'Jsbeautify.jsm',
     'key-shortcuts.js',
     'keycodes.js',
+    'network-throttling-profiles.js',
     'node-attribute-parser.js',
     'options-view.js',
     'output-parser.js',
     'poller.js',
     'prefs.js',
     'scroll.js',
     'source-utils.js',
     'SplitView.jsm',
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/network-throttling-profiles.js
@@ -0,0 +1,68 @@
+/* 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 K = 1024;
+const M = 1024 * 1024;
+const Bps = 1 / 8;
+const KBps = K * Bps;
+const MBps = M * Bps;
+
+/**
+ * Predefined network throttling profiles.
+ * Speeds are in bytes per second.  Latency is in ms.
+ */
+/* eslint-disable key-spacing */
+module.exports = [
+  {
+    id:          "GPRS",
+    download:    50 * KBps,
+    upload:      20 * KBps,
+    latency:     500,
+  },
+  {
+    id:          "Regular 2G",
+    download:    250 * KBps,
+    upload:      50 * KBps,
+    latency:     300,
+  },
+  {
+    id:          "Good 2G",
+    download:    450 * KBps,
+    upload:      150 * KBps,
+    latency:     150,
+  },
+  {
+    id:          "Regular 3G",
+    download:    750 * KBps,
+    upload:      250 * KBps,
+    latency:     100,
+  },
+  {
+    id:          "Good 3G",
+    download:    1.5 * MBps,
+    upload:      750 * KBps,
+    latency:     40,
+  },
+  {
+    id:          "Regular 4G / LTE",
+    download:    4 * MBps,
+    upload:      3 * MBps,
+    latency:     20,
+  },
+  {
+    id:          "DSL",
+    download:    2 * MBps,
+    upload:      1 * MBps,
+    latency:     5,
+  },
+  {
+    id:          "Wi-Fi",
+    download:    30 * MBps,
+    upload:      15 * MBps,
+    latency:     2,
+  },
+];
+/* eslint-enable key-spacing */
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -44,16 +44,24 @@
             DebuggerServer.addBrowserActors();
           }
 
           win = yield openWebIDE();
           let docRuntime = getRuntimeDocument(win);
           let docProject = getProjectDocument(win);
           let winProject = getProjectWindow(win);
 
+          let packagedAppLocation = getTestFilePath("app");
+
+          let onValidated = waitForUpdate(win, "project-validated");
+          let onDetails = waitForUpdate(win, "details");
+          yield winProject.projectList.importPackagedApp(packagedAppLocation);
+          yield onValidated;
+          yield onDetails;
+
           win.AppManager.runtimeList.usb.push({
             connect: function(connection) {
               is(connection, win.AppManager.connection, "connection is valid");
               connection.host = null; // force connectPipe
               connection.connect();
               return promise.resolve();
             },
 
@@ -83,65 +91,58 @@
 
             get name() {
               return "prolongedRuntime";
             }
           });
 
           win.AppManager.update("runtime-list");
 
-          let packagedAppLocation = getTestFilePath("app");
-
-          let onValidated = waitForUpdate(win, "project-validated");
-          let onDetails = waitForUpdate(win, "details");
-          yield winProject.projectList.importPackagedApp(packagedAppLocation);
-          yield onValidated;
-          yield onDetails;
-
           let panelNode = docRuntime.querySelector("#runtime-panel");
           let items = panelNode.querySelectorAll(".runtime-panel-item-usb");
           is(items.length, 3, "Found 3 runtime buttons");
 
           let connectionsChanged = waitForConnectionChange("opened", 2);
           items[0].click();
 
           ok(win.document.querySelector("window").className, "busy", "UI is busy");
           yield win.UI._busyPromise;
 
           yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 2, "Connected");
 
           yield waitForUpdate(win, "runtime-global-actors");
 
-          ok(isPlayActive(), "play button is enabled 1");
-          ok(!isStopActive(), "stop button is disabled 1");
+          // Play button always disabled now, webapps actor removed
+          ok(!isPlayActive(), "play button is disabled");
+          ok(!isStopActive(), "stop button is disabled");
           let oldProject = win.AppManager.selectedProject;
           win.AppManager.selectedProject = null;
 
           yield nextTick();
 
-          ok(!isPlayActive(), "play button is disabled 2");
-          ok(!isStopActive(), "stop button is disabled 2");
+          ok(!isPlayActive(), "play button is disabled");
+          ok(!isStopActive(), "stop button is disabled");
           win.AppManager._selectedProject = oldProject;
           win.UI.updateCommands();
 
           yield nextTick();
 
-          ok(isPlayActive(), "play button is enabled 3");
-          ok(!isStopActive(), "stop button is disabled 3");
+          ok(!isPlayActive(), "play button is enabled");
+          ok(!isStopActive(), "stop button is disabled");
 
           connectionsChanged = waitForConnectionChange("closed", 2);
           yield win.Cmds.disconnectRuntime();
 
           yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 0, "Disconnected");
 
           ok(win.AppManager.selectedProject, "A project is still selected");
-          ok(!isPlayActive(), "play button is disabled 4");
-          ok(!isStopActive(), "stop button is disabled 4");
+          ok(!isPlayActive(), "play button is disabled");
+          ok(!isStopActive(), "stop button is disabled");
 
           connectionsChanged = waitForConnectionChange("opened", 2);
           docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
 
           yield waitForUpdate(win, "runtime-targets");
 
           yield connectionsChanged;
           is(Object.keys(DebuggerServer._connections).length, 2, "Locally connected");
--- a/devtools/server/actors/emulation.js
+++ b/devtools/server/actors/emulation.js
@@ -4,33 +4,193 @@
 
 "use strict";
 
 const { Ci } = require("chrome");
 const protocol = require("devtools/shared/protocol");
 const { emulationSpec } = require("devtools/shared/specs/emulation");
 const { SimulatorCore } = require("devtools/shared/touch/simulator-core");
 
+/**
+ * This actor overrides various browser features to simulate different environments to
+ * test how pages perform under various conditions.
+ *
+ * The design below, which saves the previous value of each property before setting, is
+ * needed because it's possible to have multiple copies of this actor for a single page.
+ * When some instance of this actor changes a property, we want it to be able to restore
+ * that property to the way it was found before the change.
+ *
+ * A subtle aspect of the code below is that all get* methods must return non-undefined
+ * values, so that the absence of a previous value can be distinguished from the value for
+ * "no override" for each of the properties.
+ */
 let EmulationActor = protocol.ActorClassWithSpec(emulationSpec, {
+
   initialize(conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
+    this.tabActor = tabActor;
     this.docShell = tabActor.docShell;
     this.simulatorCore = new SimulatorCore(tabActor.chromeEventHandler);
   },
 
+  disconnect() {
+    this.destroy();
+  },
+
+  destroy() {
+    this.clearDPPXOverride();
+    this.clearNetworkThrottling();
+    this.clearTouchEventsOverride();
+    this.clearUserAgentOverride();
+    this.tabActor = null;
+    this.docShell = null;
+    this.simulatorCore = null;
+    protocol.Actor.prototype.destroy.call(this);
+  },
+
+  /**
+   * Retrieve the console actor for this tab.  This allows us to expose network throttling
+   * as part of emulation settings, even though it's internally connected to the network
+   * monitor, which for historical reasons is part of the console actor.
+   */
+  get _consoleActor() {
+    if (this.tabActor.exited) {
+      return null;
+    }
+    let form = this.tabActor.form();
+    return this.conn._getOrCreateActor(form.consoleActor);
+  },
+
+  /* DPPX override */
+
+  _previousDPPXOverride: undefined,
+
+  setDPPXOverride(dppx) {
+    if (this.getDPPXOverride() === dppx) {
+      return false;
+    }
+
+    if (this._previousDPPXOverride === undefined) {
+      this._previousDPPXOverride = this.getDPPXOverride();
+    }
+
+    this.docShell.contentViewer.overrideDPPX = dppx;
+
+    return true;
+  },
+
+  getDPPXOverride() {
+    return this.docShell.contentViewer.overrideDPPX;
+  },
+
+  clearDPPXOverride() {
+    if (this._previousDPPXOverride !== undefined) {
+      return this.setDPPXOverride(this._previousDPPXOverride);
+    }
+
+    return false;
+  },
+
+  /* Network Throttling */
+
+  _previousNetworkThrottling: undefined,
+
+  /**
+   * Transform the RDP format into the internal format and then set network throttling.
+   */
+  setNetworkThrottling({ downloadThroughput, uploadThroughput, latency }) {
+    let throttleData = {
+      roundTripTimeMean: latency,
+      roundTripTimeMax: latency,
+      downloadBPSMean: downloadThroughput,
+      downloadBPSMax: downloadThroughput,
+      uploadBPSMean: uploadThroughput,
+      uploadBPSMax: uploadThroughput,
+    };
+    return this._setNetworkThrottling(throttleData);
+  },
+
+  _setNetworkThrottling(throttleData) {
+    let current = this._getNetworkThrottling();
+    // Check if they are both objects or both null
+    let match = throttleData == current;
+    // If both objects, check all entries
+    if (match && current && throttleData) {
+      match = Object.entries(current).every(([ k, v ]) => {
+        return throttleData[k] === v;
+      });
+    }
+    if (match) {
+      return false;
+    }
+
+    if (this._previousNetworkThrottling === undefined) {
+      this._previousNetworkThrottling = current;
+    }
+
+    let consoleActor = this._consoleActor;
+    if (!consoleActor) {
+      return false;
+    }
+    consoleActor.onStartListeners({
+      listeners: [ "NetworkActivity" ],
+    });
+    consoleActor.onSetPreferences({
+      preferences: {
+        "NetworkMonitor.throttleData": throttleData,
+      }
+    });
+    return true;
+  },
+
+  /**
+   * Get network throttling and then transform the internal format into the RDP format.
+   */
+  getNetworkThrottling() {
+    let throttleData = this._getNetworkThrottling();
+    if (!throttleData) {
+      return null;
+    }
+    let { downloadBPSMax, uploadBPSMax, roundTripTimeMax } = throttleData;
+    return {
+      downloadThroughput: downloadBPSMax,
+      uploadThroughput: uploadBPSMax,
+      latency: roundTripTimeMax,
+    };
+  },
+
+  _getNetworkThrottling() {
+    let consoleActor = this._consoleActor;
+    if (!consoleActor) {
+      return null;
+    }
+    let prefs = consoleActor.onGetPreferences({
+      preferences: [ "NetworkMonitor.throttleData" ],
+    });
+    return prefs.preferences["NetworkMonitor.throttleData"] || null;
+  },
+
+  clearNetworkThrottling() {
+    if (this._previousNetworkThrottling !== undefined) {
+      return this._setNetworkThrottling(this._previousNetworkThrottling);
+    }
+
+    return false;
+  },
+
   /* Touch events override */
 
-  _previousTouchEventsOverride: null,
+  _previousTouchEventsOverride: undefined,
 
   setTouchEventsOverride(flag) {
-    if (this.docShell.touchEventsOverride == flag) {
+    if (this.getTouchEventsOverride() == flag) {
       return false;
     }
-    if (this._previousTouchEventsOverride === null) {
-      this._previousTouchEventsOverride = this.docShell.touchEventsOverride;
+    if (this._previousTouchEventsOverride === undefined) {
+      this._previousTouchEventsOverride = this.getTouchEventsOverride();
     }
 
     // Start or stop the touch simulator depending on the override flag
     if (flag == Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED) {
       this.simulatorCore.start();
     } else {
       this.simulatorCore.stop();
     }
@@ -39,87 +199,43 @@ let EmulationActor = protocol.ActorClass
     return true;
   },
 
   getTouchEventsOverride() {
     return this.docShell.touchEventsOverride;
   },
 
   clearTouchEventsOverride() {
-    if (this._previousTouchEventsOverride !== null) {
+    if (this._previousTouchEventsOverride !== undefined) {
       return this.setTouchEventsOverride(this._previousTouchEventsOverride);
     }
     return false;
   },
 
   /* User agent override */
 
-  _previousUserAgentOverride: null,
+  _previousUserAgentOverride: undefined,
 
   setUserAgentOverride(userAgent) {
-    if (this.docShell.customUserAgent == userAgent) {
+    if (this.getUserAgentOverride() == userAgent) {
       return false;
     }
-    if (this._previousUserAgentOverride === null) {
-      this._previousUserAgentOverride = this.docShell.customUserAgent;
+    if (this._previousUserAgentOverride === undefined) {
+      this._previousUserAgentOverride = this.getUserAgentOverride();
     }
     this.docShell.customUserAgent = userAgent;
     return true;
   },
 
   getUserAgentOverride() {
     return this.docShell.customUserAgent;
   },
 
   clearUserAgentOverride() {
-    if (this._previousUserAgentOverride !== null) {
+    if (this._previousUserAgentOverride !== undefined) {
       return this.setUserAgentOverride(this._previousUserAgentOverride);
     }
     return false;
   },
 
-  /* DPPX override */
-
-  _previousDPPXOverride: null,
-
-  setDPPXOverride(dppx) {
-    let { contentViewer } = this.docShell;
-
-    if (contentViewer.overrideDPPX === dppx) {
-      return false;
-    }
-
-    if (this._previousDPPXOverride === null) {
-      this._previousDPPXOverride = contentViewer.overrideDPPX;
-    }
-
-    contentViewer.overrideDPPX = dppx;
-
-    return true;
-  },
-
-  getDPPXOverride() {
-    return this.docShell.contentViewer.overrideDPPX;
-  },
-
-  clearDPPXOverride() {
-    if (this._previousDPPXOverride !== null) {
-      return this.setDPPXOverride(this._previousDPPXOverride);
-    }
-
-    return false;
-  },
-
-  disconnect() {
-    this.destroy();
-  },
-
-  destroy() {
-    this.clearTouchEventsOverride();
-    this.clearUserAgentOverride();
-    this.clearDPPXOverride();
-    this.docShell = null;
-    this.simulatorCore = null;
-    protocol.Actor.prototype.destroy.call(this);
-  },
 });
 
 exports.EmulationActor = EmulationActor;
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -54,16 +54,15 @@ DevToolsModules(
     'settings.js',
     'source.js',
     'storage.js',
     'string.js',
     'styleeditor.js',
     'styles.js',
     'stylesheets.js',
     'timeline.js',
-    'webapps.js',
     'webaudio.js',
     'webbrowser.js',
     'webconsole.js',
     'webextension.js',
     'webgl.js',
     'worker.js',
 )
deleted file mode 100644
--- a/devtools/server/actors/webapps.js
+++ /dev/null
@@ -1,1116 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-var { Cu, Cc, Ci } = require("chrome");
-
-var { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
-var { OS } = require("resource://gre/modules/osfile.jsm");
-var { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
-
-var promise = require("promise");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var { ActorPool } = require("devtools/server/actors/common");
-var { DebuggerServer } = require("devtools/server/main");
-var Services = require("Services");
-var FileReader = require("FileReader");
-
-// Load actor dependencies lazily as this actor require extra environnement
-// preparation to work (like have a profile setup in xpcshell tests)
-loader.lazyRequireGetter(this, "DOMApplicationRegistry", "resource://gre/modules/Webapps.jsm", true);
-loader.lazyRequireGetter(this, "AppsUtils", "resource://gre/modules/AppsUtils.jsm", true);
-loader.lazyRequireGetter(this, "ManifestHelper", "resource://gre/modules/AppsUtils.jsm", true);
-loader.lazyRequireGetter(this, "MessageBroadcaster", "resource://gre/modules/MessageBroadcaster.jsm", true);
-loader.lazyRequireGetter(this, "UserCustomizations", "resource://gre/modules/UserCustomizations.jsm", true);
-
-// Comma separated list of permissions that a sideloaded app can't ask for
-const UNSAFE_PERMISSIONS = Services.prefs.getCharPref("devtools.apps.forbidden-permissions");
-
-var FramesMock = null;
-
-exports.setFramesMock = function (mock) {
-  FramesMock = mock;
-};
-
-DevToolsUtils.defineLazyGetter(this, "Frames", () => {
-  // Offer a way for unit test to provide a mock
-  if (FramesMock) {
-    return FramesMock;
-  }
-  try {
-    return Cu.import("resource://gre/modules/Frames.jsm", {}).Frames;
-  } catch (e) {}
-  return null;
-});
-
-function debug(aMsg) {
-  /*
-  Cc["@mozilla.org/consoleservice;1"]
-    .getService(Ci.nsIConsoleService)
-    .logStringMessage("--*-- WebappsActor : " + aMsg);
-  */
-}
-
-function PackageUploadActor(file) {
-  this._file = file;
-  this._path = file.path;
-}
-
-PackageUploadActor.fromRequest = function (request, file) {
-  if (request.bulk) {
-    return new PackageUploadBulkActor(file);
-  }
-  return new PackageUploadJSONActor(file);
-};
-
-PackageUploadActor.prototype = {
-
-  /**
-   * This method isn't exposed to the client.
-   * It is meant to be called by server code, in order to get
-   * access to the temporary file out of the actor ID.
-   */
-  get filePath() {
-    return this._path;
-  },
-
-  get openedFile() {
-    if (this._openedFile) {
-      return this._openedFile;
-    }
-    this._openedFile = this._openFile();
-    return this._openedFile;
-  },
-
-  /**
-   * This method allows you to delete the temporary file,
-   * when you are done using it.
-   */
-  remove: function () {
-    this._cleanupFile();
-    return {};
-  },
-
-  _cleanupFile: function () {
-    try {
-      this._closeFile();
-    } catch (e) {}
-    try {
-      OS.File.remove(this._path);
-    } catch (e) {}
-  }
-
-};
-
-/**
- * Create a new JSON package upload actor.
- * @param file nsIFile temporary file to write to
- */
-function PackageUploadJSONActor(file) {
-  PackageUploadActor.call(this, file);
-  this._size = 0;
-}
-
-PackageUploadJSONActor.prototype = Object.create(PackageUploadActor.prototype);
-
-PackageUploadJSONActor.prototype.actorPrefix = "packageUploadJSONActor";
-
-PackageUploadJSONActor.prototype._openFile = function () {
-  return OS.File.open(this._path, { write: true, truncate: true });
-};
-
-PackageUploadJSONActor.prototype._closeFile = function () {
-  this.openedFile.then(file => file.close());
-};
-
-/**
- * This method allows you to upload a piece of file.
- * It expects a chunk argument that is the a string to write to the file.
- */
-PackageUploadJSONActor.prototype.chunk = function (aRequest) {
-  let chunk = aRequest.chunk;
-  if (!chunk || chunk.length <= 0) {
-    return {error: "parameterError",
-            message: "Missing or invalid chunk argument"};
-  }
-  // Translate the string used to transfer the chunk over JSON
-  // back to a typed array
-  let data = new Uint8Array(chunk.length);
-  for (let i = 0, l = chunk.length; i < l; i++) {
-    data[i] = chunk.charCodeAt(i);
-  }
-  return this.openedFile
-             .then(file => file.write(data))
-             .then((written) => {
-               this._size += written;
-               return {
-                 written: written,
-                 _size: this._size
-               };
-             });
-};
-
-/**
- * This method needs to be called, when you are done uploading
- * chunks, before trying to access/use the temporary file.
- * Otherwise, the file may be partially written
- * and also be locked.
- */
-PackageUploadJSONActor.prototype.done = function () {
-  this._closeFile();
-  return {};
-};
-
-/**
- * The request types this actor can handle.
- */
-PackageUploadJSONActor.prototype.requestTypes = {
-  "chunk": PackageUploadJSONActor.prototype.chunk,
-  "done": PackageUploadJSONActor.prototype.done,
-  "remove": PackageUploadJSONActor.prototype.remove
-};
-
-/**
- * Create a new bulk package upload actor.
- * @param file nsIFile temporary file to write to
- */
-function PackageUploadBulkActor(file) {
-  PackageUploadActor.call(this, file);
-}
-
-PackageUploadBulkActor.prototype = Object.create(PackageUploadActor.prototype);
-
-PackageUploadBulkActor.prototype.actorPrefix = "packageUploadBulkActor";
-
-PackageUploadBulkActor.prototype._openFile = function () {
-  return FileUtils.openSafeFileOutputStream(this._file);
-};
-
-PackageUploadBulkActor.prototype._closeFile = function () {
-  FileUtils.closeSafeFileOutputStream(this.openedFile);
-};
-
-PackageUploadBulkActor.prototype.stream = function ({copyTo}) {
-  return copyTo(this.openedFile).then(() => {
-    this._closeFile();
-    return {};
-  });
-};
-
-/**
- * The request types this actor can handle.
- */
-PackageUploadBulkActor.prototype.requestTypes = {
-  "stream": PackageUploadBulkActor.prototype.stream,
-  "remove": PackageUploadBulkActor.prototype.remove
-};
-
-/**
- * Creates a WebappsActor. WebappsActor provides remote access to
- * install apps.
- */
-function WebappsActor(aConnection) {
-  debug("init");
-  this.appsChild = {};
-  Cu.import("resource://gre/modules/AppsServiceChild.jsm", this.appsChild);
-
-  // Keep reference of already connected app processes.
-  // values: app frame message manager
-  this._connectedApps = new Set();
-
-  this.conn = aConnection;
-  this._uploads = [];
-  this._actorPool = new ActorPool(this.conn);
-  this.conn.addActorPool(this._actorPool);
-}
-
-WebappsActor.prototype = {
-  actorPrefix: "webapps",
-
-  // For now, launch and close requests are only supported on B2G products
-  // like devices, mulet/simulators, and graphene.
-  // We set that attribute on the prototype in order to allow test
-  // to enable this feature.
-  supportsLaunch: require("devtools/shared/system").constants.MOZ_B2G,
-
-  disconnect: function () {
-    try {
-      this.unwatchApps();
-    } catch (e) {}
-
-    // When we stop using this actor, we should ensure removing all files.
-    for (let upload of this._uploads) {
-      upload.remove();
-    }
-    this._uploads = null;
-
-    this.conn.removeActorPool(this._actorPool);
-    this._actorPool = null;
-    this.conn = null;
-  },
-
-  _registerApp: function wa_actorRegisterApp(aDeferred, aApp, aId, aDir) {
-    debug("registerApp");
-    let reg = DOMApplicationRegistry;
-    let self = this;
-
-    if (aId in reg.webapps && !reg.webapps[aId].sideloaded &&
-        !this._isUnrestrictedAccessAllowed()) {
-      throw new Error("Replacing non-sideloaded apps is not permitted.");
-    }
-
-    // Clean up the deprecated manifest cache if needed.
-    if (aId in reg._manifestCache) {
-      delete reg._manifestCache[aId];
-    }
-
-    aApp.installTime = Date.now();
-    aApp.installState = "installed";
-    aApp.removable = true;
-    aApp.id = aId;
-    aApp.basePath = reg.getWebAppsBasePath();
-    aApp.localId = (aId in reg.webapps) ? reg.webapps[aId].localId
-                                        : reg._nextLocalId();
-    aApp.sideloaded = true;
-    aApp.enabled = true;
-    aApp.blockedStatus = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
-
-    reg.webapps[aId] = aApp;
-    reg.updatePermissionsForApp(aId);
-
-    reg._readManifests([{ id: aId }]).then((aResult) => {
-      let manifest = aResult[0].manifest;
-      aApp.name = manifest.name;
-      aApp.csp = manifest.csp || "";
-      aApp.role = manifest.role || "";
-      reg.updateAppHandlers(null, manifest, aApp);
-
-      reg._saveApps().then(() => {
-        aApp.manifest = manifest;
-
-        // We need the manifest to set the app kind for hosted apps,
-        // because of appcache.
-        if (aApp.kind == undefined) {
-          aApp.kind = manifest.appcache_path ? reg.kHostedAppcache
-                                             : reg.kHosted;
-        }
-
-        // Needed to evict manifest cache on content side
-        // (has to be dispatched first, otherwise other messages like
-        // Install:Return:OK are going to use old manifest version)
-        MessageBroadcaster.broadcastMessage("Webapps:UpdateState", {
-          app: aApp,
-          manifest: manifest,
-          id: aApp.id
-        });
-        MessageBroadcaster.broadcastMessage("Webapps:FireEvent", {
-          eventType: ["downloadsuccess", "downloadapplied"],
-          manifestURL: aApp.manifestURL
-        });
-        MessageBroadcaster.broadcastMessage("Webapps:AddApp", { id: aId, app: aApp });
-        MessageBroadcaster.broadcastMessage("Webapps:Install:Return:OK", {
-          app: aApp,
-          oid: "foo",
-          requestID: "bar"
-        });
-
-        Services.obs.notifyObservers(null, "webapps-installed",
-          JSON.stringify({ manifestURL: aApp.manifestURL }));
-
-        delete aApp.manifest;
-        aDeferred.resolve({ appId: aId, path: aDir.path });
-
-        // We can't have appcache for packaged apps.
-        if (!aApp.origin.startsWith("app://")) {
-          reg.startOfflineCacheDownload(
-            new ManifestHelper(manifest, aApp.origin, aApp.manifestURL), aApp);
-        }
-      });
-      // Cleanup by removing the temporary directory.
-      if (aDir.exists())
-        aDir.remove(true);
-    });
-  },
-
-  _sendError: function wa_actorSendError(aDeferred, aMsg, aId) {
-    debug("Sending error: " + aMsg);
-    aDeferred.resolve({
-      error: "installationFailed",
-      message: aMsg,
-      appId: aId
-    });
-  },
-
-  _getAppType: function wa_actorGetAppType(aType) {
-    let type = Ci.nsIPrincipal.APP_STATUS_INSTALLED;
-
-    if (aType) {
-      type = aType == "privileged" ? Ci.nsIPrincipal.APP_STATUS_PRIVILEGED
-           : aType == "certified" ? Ci.nsIPrincipal.APP_STATUS_CERTIFIED
-           : Ci.nsIPrincipal.APP_STATUS_INSTALLED;
-    }
-
-    return type;
-  },
-
-  _createTmpPackage: function () {
-    let tmpDir = FileUtils.getDir("TmpD", ["file-upload"], true, false);
-    if (!tmpDir.exists() || !tmpDir.isDirectory()) {
-      return {
-        error: "fileAccessError",
-        message: "Unable to create temporary folder"
-      };
-    }
-    let tmpFile = tmpDir;
-    tmpFile.append("package.zip");
-    tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
-    if (!tmpFile.exists() || !tmpDir.isFile()) {
-      return {
-        error: "fileAccessError",
-        message: "Unable to create temporary file"
-      };
-    }
-    return tmpFile;
-  },
-
-  uploadPackage: function (request) {
-    debug("uploadPackage");
-
-    let tmpFile = this._createTmpPackage();
-    if ("error" in tmpFile) {
-      return tmpFile;
-    }
-
-    let actor = PackageUploadActor.fromRequest(request, tmpFile);
-    this._actorPool.addActor(actor);
-    this._uploads.push(actor);
-    return { actor: actor.actorID };
-  },
-
-  installHostedApp: function wa_actorInstallHosted(aDir, aId, aReceipts,
-                                                   aManifest, aMetadata) {
-    debug("installHostedApp");
-    let self = this;
-    let deferred = promise.defer();
-
-    function readManifest() {
-      if (aManifest) {
-        return promise.resolve(aManifest);
-      } else {
-        let manFile = OS.Path.join(aDir.path, "manifest.webapp");
-        return AppsUtils.loadJSONAsync(manFile);
-      }
-    }
-    function writeManifest(resolution) {
-      // Move manifest.webapp to the destination directory.
-      // The destination directory for this app.
-      let installDir = DOMApplicationRegistry._getAppDir(aId);
-      if (aManifest) {
-        let manFile = OS.Path.join(installDir.path, "manifest.webapp");
-        return DOMApplicationRegistry._writeFile(manFile, JSON.stringify(aManifest)).then(() => {
-          return resolution;
-        });
-      } else {
-        let manFile = aDir.clone();
-        manFile.append("manifest.webapp");
-        manFile.moveTo(installDir, "manifest.webapp");
-      }
-      return promise.resolve(resolution);
-    }
-    function readMetadata(aAppType) {
-      if (aMetadata) {
-        return { metadata: aMetadata, appType: aAppType };
-      }
-      // Read the origin and manifest url from metadata.json
-      let metaFile = OS.Path.join(aDir.path, "metadata.json");
-      return AppsUtils.loadJSONAsync(metaFile).then((aMetadata) => {
-        if (!aMetadata) {
-          throw ("Error parsing metadata.json.");
-        }
-        if (!aMetadata.origin) {
-          throw ("Missing 'origin' property in metadata.json.");
-        }
-        return { metadata: aMetadata, appType: aAppType };
-      });
-    }
-    let runnable = {
-      run: function run() {
-        try {
-          let metadata, appType;
-          readManifest().
-            then(readMetadata).
-            then(function ({ metadata, appType }) {
-              let origin = metadata.origin;
-              let manifestURL = metadata.manifestURL ||
-                                origin + "/manifest.webapp";
-              // Create a fake app object with the minimum set of properties we need.
-              let app = {
-                origin: origin,
-                installOrigin: metadata.installOrigin || origin,
-                manifestURL: manifestURL,
-                appStatus: appType,
-                receipts: aReceipts,
-              };
-
-              return writeManifest(app);
-            }).then(function (app) {
-              self._registerApp(deferred, app, aId, aDir);
-            }, function (error) {
-              self._sendError(deferred, error, aId);
-            });
-        } catch (e) {
-          // If anything goes wrong, just send it back.
-          self._sendError(deferred, e.toString(), aId);
-        }
-      }
-    };
-
-    Services.tm.currentThread.dispatch(runnable,
-                                       Ci.nsIThread.DISPATCH_NORMAL);
-    return deferred.promise;
-  },
-
-  installPackagedApp: function wa_actorInstallPackaged(aDir, aId, aReceipts) {
-    debug("installPackagedApp");
-    let self = this;
-    let deferred = promise.defer();
-
-    let runnable = {
-      run: function run() {
-        try {
-          // Open the app zip package
-          let zipFile = aDir.clone();
-          zipFile.append("application.zip");
-          let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
-                            .createInstance(Ci.nsIZipReader);
-          zipReader.open(zipFile);
-
-          // Prefer manifest.webapp when available
-          let hasWebappManifest = zipReader.hasEntry("manifest.webapp");
-          let hasJsonManifest = zipReader.hasEntry("manifest.json");
-
-          if (!hasWebappManifest && !hasJsonManifest) {
-            self._sendError(deferred, "Missing manifest.webapp or manifest.json", aId);
-            return;
-          }
-
-          let manifestName = hasWebappManifest ? "manifest.webapp" : "manifest.json";
-
-          // Read app manifest from `application.zip`
-          let istream = zipReader.getInputStream(manifestName);
-          let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
-                            .createInstance(Ci.nsIScriptableUnicodeConverter);
-          converter.charset = "UTF-8";
-          let jsonString = converter.ConvertToUnicode(
-            NetUtil.readInputStreamToString(istream, istream.available())
-          );
-          zipReader.close();
-
-          let manifest;
-          try {
-            manifest = JSON.parse(jsonString);
-          } catch (e) {
-            self._sendError(deferred, "Error Parsing " + manifestName + ": " + e, aId);
-            return;
-          }
-
-          if (manifestName === "manifest.json") {
-            if (!UserCustomizations.checkExtensionManifest(manifest)) {
-              self._sendError(deferred, "Invalid manifest", aId);
-              return;
-            }
-            manifest = UserCustomizations.convertManifest(manifest);
-          }
-
-          // Completely forbid pushing apps asking for unsafe permissions
-          if ("permissions" in manifest) {
-            let list = UNSAFE_PERMISSIONS.split(",");
-            let hasOne = list.some(p => p.trim() in manifest.permissions);
-            if (hasOne) {
-              self._sendError(deferred, "Installing apps with any of these " +
-                                        "permissions is forbidden: " +
-                                        UNSAFE_PERMISSIONS, aId);
-              return;
-            }
-          }
-
-          let appType = self._getAppType(manifest.type);
-
-          // Privileged and certified packaged apps can setup a custom origin
-          // via `origin` manifest property
-          let id = aId;
-          if (appType >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED &&
-              manifest.origin !== undefined) {
-            let uri;
-            try {
-              uri = Services.io.newURI(manifest.origin, null, null);
-            } catch (e) {
-              self._sendError(deferred, "Invalid origin in webapp's manifest", aId);
-            }
-
-            if (uri.scheme != "app") {
-              self._sendError(deferred, "Invalid origin in webapp's manifest", aId);
-            }
-            id = uri.prePath.substring(6);
-          }
-
-          // Prevent overriding preinstalled apps
-          if (id in DOMApplicationRegistry.webapps &&
-              DOMApplicationRegistry.webapps[id].removable === false &&
-              !self._isUnrestrictedAccessAllowed()) {
-            self._sendError(deferred, "The application " + id + " can't be overridden.");
-            return;
-          }
-
-          // Only after security checks are made and after final app id is computed
-          // we can move application.zip to the destination directory, and
-          // write manifest.webapp there.
-          let installDir = DOMApplicationRegistry._getAppDir(id);
-          zipFile.moveTo(installDir, "application.zip");
-
-          let manFile = installDir.clone();
-          manFile.append("manifest.webapp");
-          DOMApplicationRegistry._writeFile(manFile.path, JSON.stringify(manifest))
-            .then(() => {
-              let origin = "app://" + id;
-              let manifestURL = origin + "/manifest.webapp";
-
-              // Refresh application.zip content (e.g. reinstall app), as done here:
-              // http://hg.mozilla.org/mozilla-central/annotate/aaefec5d34f8/dom/apps/src/Webapps.jsm#l1125
-              // Do it in parent process for the simulator
-              let jar = installDir.clone();
-              jar.append("application.zip");
-              Services.obs.notifyObservers(jar, "flush-cache-entry", null);
-
-              // And then in app content process
-              // This function will be evaluated in the scope of the content process
-              // frame script. That will flush the jar cache for this app and allow
-              // loading fresh updated resources if we reload its document.
-              let FlushFrameScript = function (path) {
-                let jar = Cc["@mozilla.org/file/local;1"]
-                            .createInstance(Ci.nsILocalFile);
-                jar.initWithPath(path);
-                let obs = Cc["@mozilla.org/observer-service;1"]
-                            .getService(Ci.nsIObserverService);
-                obs.notifyObservers(jar, "flush-cache-entry", null);
-              };
-              for (let frame of self._appFrames()) {
-                if (frame.getAttribute("mozapp") == manifestURL) {
-                  let mm = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
-                  mm.loadFrameScript("data:," +
-                    encodeURIComponent("(" + FlushFrameScript.toString() + ")" +
-                                       "('" + jar.path + "')"), false);
-                }
-              }
-
-              // Create a fake app object with the minimum set of properties we need.
-              let app = {
-                origin: origin,
-                installOrigin: origin,
-                manifestURL: manifestURL,
-                appStatus: appType,
-                receipts: aReceipts,
-                kind: DOMApplicationRegistry.kPackaged,
-              };
-
-              self._registerApp(deferred, app, id, aDir);
-            });
-        } catch (e) {
-          // If anything goes wrong, just send it back.
-          self._sendError(deferred, e.toString(), aId);
-        }
-      }
-    };
-
-    Services.tm.currentThread.dispatch(runnable,
-                                       Ci.nsIThread.DISPATCH_NORMAL);
-    return deferred.promise;
-  },
-
-  /**
-    * @param appId   : The id of the app we want to install. We will look for
-    *                  the files for the app in $TMP/b2g/$appId :
-    *                  For packaged apps: application.zip
-    *                  For hosted apps:   metadata.json and manifest.webapp
-    */
-  install: function wa_actorInstall(aRequest) {
-    debug("install");
-
-    let appId = aRequest.appId;
-    let reg = DOMApplicationRegistry;
-    if (!appId) {
-      appId = reg.makeAppId();
-    }
-
-    // Check that we are not overriding a preinstalled application.
-    if (appId in reg.webapps &&
-        reg.webapps[appId].removable === false &&
-        !this._isUnrestrictedAccessAllowed()) {
-      return { error: "installationFailed",
-               message: "The application " + appId + " can't be overridden."
-             };
-    }
-
-    let appDir = FileUtils.getDir("TmpD", ["b2g", appId], false, false);
-
-    if (aRequest.upload) {
-      // Ensure creating the directory (recursively)
-      appDir = FileUtils.getDir("TmpD", ["b2g", appId], true, false);
-      let actor = this.conn.getActor(aRequest.upload);
-      if (!actor) {
-        return { error: "badParameter",
-                 message: "Unable to find upload actor '" + aRequest.upload
-                          + "'" };
-      }
-      let appFile = FileUtils.File(actor.filePath);
-      if (!appFile.exists()) {
-        return { error: "badParameter",
-                 message: "The uploaded file doesn't exist on device" };
-      }
-      appFile.moveTo(appDir, "application.zip");
-    } else if ((!appDir || !appDir.exists()) &&
-               !aRequest.manifest && !aRequest.metadata) {
-      return { error: "badParameterType",
-               message: "missing directory " + appDir.path
-             };
-    }
-
-    let testFile = appDir.clone();
-    testFile.append("application.zip");
-
-    let receipts = (aRequest.receipts && Array.isArray(aRequest.receipts))
-                    ? aRequest.receipts
-                    : [];
-
-    if (testFile.exists()) {
-      return this.installPackagedApp(appDir, appId, receipts);
-    }
-
-    let manifest, metadata;
-    let missing =
-      ["manifest.webapp", "metadata.json"]
-      .some(function (aName) {
-        testFile = appDir.clone();
-        testFile.append(aName);
-        return !testFile.exists();
-      });
-    if (missing) {
-      if (aRequest.manifest && aRequest.metadata &&
-          aRequest.metadata.origin) {
-        manifest = aRequest.manifest;
-        metadata = aRequest.metadata;
-      } else {
-        try {
-          appDir.remove(true);
-        } catch (e) {}
-        return { error: "badParameterType",
-                 message: "hosted app file and manifest/metadata fields " +
-                          "are missing"
-        };
-      }
-    }
-
-    return this.installHostedApp(appDir, appId, receipts, manifest, metadata);
-  },
-
-  getAll: function wa_actorGetAll(aRequest) {
-    debug("getAll");
-
-    let deferred = promise.defer();
-    this.appsChild.DOMApplicationRegistry.getAll(apps => {
-      deferred.resolve({ apps: this._filterAllowedApps(apps) });
-    });
-
-    return deferred.promise;
-  },
-
-  getApp: function wa_actorGetApp(aRequest) {
-    debug("getApp");
-
-    let manifestURL = aRequest.manifestURL;
-    if (!manifestURL) {
-      return { error: "missingParameter",
-               message: "missing parameter manifestURL" };
-    }
-
-    let reg = DOMApplicationRegistry;
-    let app = reg.getAppByManifestURL(manifestURL);
-    if (!app) {
-      return { error: "appNotFound" };
-    }
-
-    if (!this._isAppAllowedForURL(app.manifestURL)) {
-      return { error: "forbidden" };
-    }
-
-    return reg.getManifestFor(manifestURL).then(function (manifest) {
-      app.manifest = manifest;
-      return { app: app };
-    });
-  },
-
-  _isUnrestrictedAccessAllowed: function () {
-    let pref = "devtools.debugger.forbid-certified-apps";
-    return !Services.prefs.getBoolPref(pref);
-  },
-
-  _isAppAllowed: function (aApp) {
-    if (this._isUnrestrictedAccessAllowed()) {
-      return true;
-    }
-    return aApp.sideloaded;
-  },
-
-  _filterAllowedApps: function wa__filterAllowedApps(aApps) {
-    return aApps.filter(app => this._isAppAllowed(app));
-  },
-
-  _isAppAllowedForURL: function wa__isAppAllowedForURL(aManifestURL) {
-    let reg = DOMApplicationRegistry;
-    let app = reg.getAppByManifestURL(aManifestURL);
-    return this._isAppAllowed(app);
-  },
-
-  uninstall: function wa_actorUninstall(aRequest) {
-    debug("uninstall");
-
-    let manifestURL = aRequest.manifestURL;
-    if (!manifestURL) {
-      return { error: "missingParameter",
-               message: "missing parameter manifestURL" };
-    }
-
-    if (!this._isAppAllowedForURL(manifestURL)) {
-      return { error: "forbidden" };
-    }
-
-    return DOMApplicationRegistry.uninstall(manifestURL);
-  },
-
-  _findManifestByURL: function wa__findManifestByURL(aManifestURL) {
-    let deferred = promise.defer();
-
-    let reg = DOMApplicationRegistry;
-    let id = reg._appIdForManifestURL(aManifestURL);
-
-    reg._readManifests([{ id: id }]).then((aResults) => {
-      deferred.resolve(aResults[0].manifest);
-    });
-
-    return deferred.promise;
-  },
-
-  getIconAsDataURL: function (aRequest) {
-    debug("getIconAsDataURL");
-
-    let manifestURL = aRequest.manifestURL;
-    if (!manifestURL) {
-      return { error: "missingParameter",
-               message: "missing parameter manifestURL" };
-    }
-
-    let reg = DOMApplicationRegistry;
-    let app = reg.getAppByManifestURL(manifestURL);
-    if (!app) {
-      return { error: "wrongParameter",
-               message: "No application for " + manifestURL };
-    }
-
-    let deferred = promise.defer();
-
-    this._findManifestByURL(manifestURL).then(jsonManifest => {
-      let manifest = new ManifestHelper(jsonManifest, app.origin, manifestURL);
-      let iconURL = manifest.iconURLForSize(aRequest.size || 128);
-      if (!iconURL) {
-        deferred.resolve({
-          error: "noIcon",
-          message: "This app has no icon"
-        });
-        return;
-      }
-
-      // Download the URL as a blob
-      // bug 899177: there is a bug with xhr and app:// and jar:// uris
-      // that ends up forcing the content type to application/xml.
-      let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-                  .createInstance(Ci.nsIXMLHttpRequest);
-      req.open("GET", iconURL, false);
-      req.responseType = "blob";
-
-      try {
-        req.send(null);
-      } catch (e) {
-        deferred.resolve({
-          error: "noIcon",
-          message: "The icon file '" + iconURL + "' doesn't exist"
-        });
-        return;
-      }
-
-      // Convert the blog to a base64 encoded data URI
-      let reader = new FileReader();
-      reader.onload = function () {
-        deferred.resolve({
-          url: reader.result
-        });
-      };
-      reader.onerror = function () {
-        deferred.resolve({
-          error: reader.error.name,
-          message: String(reader.error)
-        });
-      };
-      reader.readAsDataURL(req.response);
-    });
-
-    return deferred.promise;
-  },
-
-  launch: function wa_actorLaunch(aRequest) {
-    debug("launch");
-
-    let manifestURL = aRequest.manifestURL;
-    if (!manifestURL) {
-      return { error: "missingParameter",
-               message: "missing parameter manifestURL" };
-    }
-
-    let deferred = promise.defer();
-
-    if (!this.supportsLaunch) {
-      return { error: "notSupported",
-               message: "Not B2G. Can't launch app." };
-    }
-
-    DOMApplicationRegistry.launch(
-      aRequest.manifestURL,
-      aRequest.startPoint || "",
-      Date.now(),
-      function onsuccess() {
-        deferred.resolve({});
-      },
-      function onfailure(reason) {
-        deferred.resolve({ error: reason });
-      });
-
-    return deferred.promise;
-  },
-
-  close: function wa_actorLaunch(aRequest) {
-    debug("close");
-
-    let manifestURL = aRequest.manifestURL;
-    if (!manifestURL) {
-      return { error: "missingParameter",
-               message: "missing parameter manifestURL" };
-    }
-
-    let reg = DOMApplicationRegistry;
-    let app = reg.getAppByManifestURL(manifestURL);
-    if (!app) {
-      return { error: "missingParameter",
-               message: "No application for " + manifestURL };
-    }
-
-    reg.close(app);
-
-    return {};
-  },
-
-  _appFrames: function () {
-    // Try to filter on b2g and mulet
-    if (Frames) {
-      return Frames.list().filter(frame => {
-        return frame.getAttribute("mozapp");
-      });
-    } else {
-      return [];
-    }
-  },
-
-  listRunningApps: function (aRequest) {
-    debug("listRunningApps\n");
-
-    let appPromises = [];
-    let apps = [];
-
-    for (let frame of this._appFrames()) {
-      let manifestURL = frame.getAttribute("mozapp");
-
-      // _appFrames can return more than one frame with the same manifest url
-      if (apps.indexOf(manifestURL) != -1) {
-        continue;
-      }
-      if (this._isAppAllowedForURL(manifestURL)) {
-        apps.push(manifestURL);
-      }
-    }
-
-    return { apps: apps };
-  },
-
-  getAppActor: function ({ manifestURL }) {
-    debug("getAppActor\n");
-
-    // Connects to the main app frame, whose `name` attribute
-    // is set to 'main' by gaia. If for any reason, gaia doesn't set any
-    // frame as main, no frame matches, then we connect arbitrary
-    // to the first app frame...
-    let appFrame = null;
-    let frames = [];
-    for (let frame of this._appFrames()) {
-      if (frame.getAttribute("mozapp") == manifestURL) {
-        if (frame.name == "main") {
-          appFrame = frame;
-          break;
-        }
-        frames.push(frame);
-      }
-    }
-    if (!appFrame && frames.length > 0) {
-      appFrame = frames[0];
-    }
-
-    let notFoundError = {
-      error: "appNotFound",
-      message: "Unable to find any opened app whose manifest " +
-               "is '" + manifestURL + "'"
-    };
-
-    if (!appFrame) {
-      return notFoundError;
-    }
-
-    if (!this._isAppAllowedForURL(manifestURL)) {
-      return notFoundError;
-    }
-
-    // Only create a new actor, if we haven't already
-    // instanciated one for this connection.
-    let set = this._connectedApps;
-    let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner)
-                     .frameLoader
-                     .messageManager;
-    if (!set.has(mm)) {
-      let onConnect = actor => {
-        set.add(mm);
-        return { actor: actor };
-      };
-      let onDisconnect = mm => {
-        set.delete(mm);
-      };
-      return DebuggerServer.connectToChild(this.conn, appFrame, onDisconnect)
-                           .then(onConnect);
-    }
-
-    // We have to update the form as it may have changed
-    // if we detached the TabActor
-    let deferred = promise.defer();
-    let onFormUpdate = msg => {
-      mm.removeMessageListener("debug:form", onFormUpdate);
-      deferred.resolve({ actor: msg.json });
-    };
-    mm.addMessageListener("debug:form", onFormUpdate);
-    mm.sendAsyncMessage("debug:form");
-
-    return deferred.promise;
-  },
-
-  watchApps: function () {
-    // For now, app open/close events are only implement on b2g
-    if (Frames) {
-      Frames.addObserver(this);
-    }
-    Services.obs.addObserver(this, "webapps-installed", false);
-    Services.obs.addObserver(this, "webapps-uninstall", false);
-
-    return {};
-  },
-
-  unwatchApps: function () {
-    if (Frames) {
-      Frames.removeObserver(this);
-    }
-    Services.obs.removeObserver(this, "webapps-installed", false);
-    Services.obs.removeObserver(this, "webapps-uninstall", false);
-
-    return {};
-  },
-
-  onFrameCreated: function (frame, isFirstAppFrame) {
-    let mozapp = frame.getAttribute("mozapp");
-    if (!mozapp || !isFirstAppFrame) {
-      return;
-    }
-
-    let manifestURL = frame.appManifestURL;
-    // Only track app frames
-    if (!manifestURL) {
-      return;
-    }
-
-    if (this._isAppAllowedForURL(manifestURL)) {
-      this.conn.send({ from: this.actorID,
-                       type: "appOpen",
-                       manifestURL: manifestURL
-                     });
-    }
-  },
-
-  onFrameDestroyed: function (frame, isLastAppFrame) {
-    let mozapp = frame.getAttribute("mozapp");
-    if (!mozapp || !isLastAppFrame) {
-      return;
-    }
-
-    let manifestURL = frame.appManifestURL;
-    // Only track app frames
-    if (!manifestURL) {
-      return;
-    }
-
-    if (this._isAppAllowedForURL(manifestURL)) {
-      this.conn.send({ from: this.actorID,
-                       type: "appClose",
-                       manifestURL: manifestURL
-                     });
-    }
-  },
-
-  observe: function (subject, topic, data) {
-    let app = JSON.parse(data);
-    if (topic == "webapps-installed") {
-      this.conn.send({ from: this.actorID,
-                       type: "appInstall",
-                       manifestURL: app.manifestURL
-                     });
-    } else if (topic == "webapps-uninstall") {
-      this.conn.send({ from: this.actorID,
-                       type: "appUninstall",
-                       manifestURL: app.manifestURL
-                     });
-    }
-  }
-};
-
-/**
- * The request types this actor can handle.
- */
-WebappsActor.prototype.requestTypes = {
-  "install": WebappsActor.prototype.install,
-  "uploadPackage": WebappsActor.prototype.uploadPackage,
-  "getAll": WebappsActor.prototype.getAll,
-  "getApp": WebappsActor.prototype.getApp,
-  "launch": WebappsActor.prototype.launch,
-  "close": WebappsActor.prototype.close,
-  "uninstall": WebappsActor.prototype.uninstall,
-  "listRunningApps": WebappsActor.prototype.listRunningApps,
-  "getAppActor": WebappsActor.prototype.getAppActor,
-  "watchApps": WebappsActor.prototype.watchApps,
-  "unwatchApps": WebappsActor.prototype.unwatchApps,
-  "getIconAsDataURL": WebappsActor.prototype.getIconAsDataURL
-};
-
-exports.WebappsActor = WebappsActor;
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -1054,17 +1054,17 @@ WebConsoleActor.prototype =
    *        The request message - which preferences need to be retrieved.
    * @return object
    *         The response message - a { key: value } object map.
    */
   onGetPreferences: function WCA_onGetPreferences(aRequest)
   {
     let prefs = Object.create(null);
     for (let key of aRequest.preferences) {
-      prefs[key] = !!this._prefs[key];
+      prefs[key] = this._prefs[key];
     }
     return { preferences: prefs };
   },
 
   /**
    * The "setPreferences" request handler.
    *
    * @param object aRequest
--- a/devtools/server/docs/actor-registration.md
+++ b/devtools/server/docs/actor-registration.md
@@ -18,19 +18,19 @@ DebuggerServer.registerModule("devtools/
   constructor: "WebConsoleActor",
   type: { tab: true }
 });
 ```
 
 To register a global actor:
 
 ```
-DebuggerServer.registerModule("devtools/server/actors/webapps", {
-  prefix: "webapps",
-  constructor: "WebappsActor",
+DebuggerServer.registerModule("devtools/server/actors/addons", {
+  prefix: "addons",
+  constructor: "AddonsActor",
   type: { global: true }
 });
 ```
 
 If you are adding a new built-in devtools actor, you should be registering it using `DebuggerServer.registerModule` in `addBrowserActors` or `addTabActors` in `/devtools/server/main.js`.
 
 If you are adding a new actor from an add-on, you should call `DebuggerServer.registerModule` directly from your add-on code.
 
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -397,21 +397,16 @@ var DebuggerServer = {
         type: { global: true }
       });
     }
     this.registerModule("devtools/server/actors/addons", {
       prefix: "addons",
       constructor: "AddonsActor",
       type: { global: true }
     });
-    this.registerModule("devtools/server/actors/webapps", {
-      prefix: "webapps",
-      constructor: "WebappsActor",
-      type: { global: true }
-    });
     this.registerModule("devtools/server/actors/device", {
       prefix: "device",
       constructor: "DeviceActor",
       type: { global: true }
     });
     this.registerModule("devtools/server/actors/director-registry", {
       prefix: "directorRegistry",
       constructor: "DirectorRegistryActor",
--- a/devtools/shared/apps/moz.build
+++ b/devtools/shared/apps/moz.build
@@ -1,23 +1,10 @@
 # vim: set filetype=python:
 # 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/.
 
-MOCHITEST_MANIFESTS += [
-    'tests/mochitest.ini',
-]
-
-TEST_HARNESS_FILES.testing.mochitest.tests.devtools.shared.apps.tests.data += [
-    'tests/data/app-certified.zip',
-    'tests/data/app-overload.zip',
-    'tests/data/app-redirect.zip',
-    'tests/data/app-system.zip',
-    'tests/data/app-updated.zip',
-    'tests/data/app.zip',
-]
-
 DevToolsModules(
     'app-actor-front.js',
     'Devices.jsm',
     'Simulator.jsm'
 )
deleted file mode 100644
index 3dbdedb8cafbc21377cecd74db25d8bea24cdda6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ab0a190de825a50c53aa157b612337a9b9f7d679..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 30b2bebccd906b275c15355b95c9c28212f05ce3..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3dbdedb8cafbc21377cecd74db25d8bea24cdda6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index b40f8e7c3248fbae1dac0fd8eb174bd57039aeb0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 9eaff3bc4c130fed28121f3f96163efdf0d1c352..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/devtools/shared/apps/tests/debugger-protocol-helper.js
+++ /dev/null
@@ -1,181 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
-const { DebuggerClient } = require("devtools/shared/client/main");
-const { DebuggerServer } = require("devtools/server/main");
-const Services = require("Services");
-const { FileUtils } = require("resource://gre/modules/FileUtils.jsm");
-const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
-
-var gClient, gActor;
-
-function connect(onDone) {
-
-  if (Services.appinfo.name == "B2G") {
-    // On b2g, we try to exercice the code that launches the production debugger server
-    let settingsService = Cc["@mozilla.org/settingsService;1"].getService(Ci.nsISettingsService);
-    settingsService.createLock().set("devtools.debugger.remote-enabled", true, null);
-    // We can't use `set` callback as it is fired before shell.js code listening for this setting
-    // is actually called. Same thing applies to mozsettings-changed obs notification.
-    // So listen to a custom event until bug 942756 lands
-    let observer = {
-      observe: function (subject, topic, data) {
-        Services.obs.removeObserver(observer, "debugger-server-started");
-        DebuggerClient.socketConnect({
-          host: "127.0.0.1",
-          port: 6000
-        }).then(transport => {
-          startClient(transport, onDone);
-        }, e => dump("Connection failed: " + e + "\n"));
-      }
-    };
-    Services.obs.addObserver(observer, "debugger-server-started", false);
-  } else {
-    // Initialize a loopback remote protocol connection
-    DebuggerServer.init();
-    // We need to register browser actors to have `listTabs` working
-    // and also have a root actor
-    DebuggerServer.addBrowserActors();
-    let transport = DebuggerServer.connectPipe();
-    startClient(transport, onDone);
-  }
-}
-
-function startClient(transport, onDone) {
-  // Setup client and actor used in all tests
-  gClient = new DebuggerClient(transport);
-  gClient.connect()
-    .then(() => gClient.listTabs())
-    .then(aResponse => {
-      gActor = aResponse.webappsActor;
-      if (gActor)
-        webappActorRequest({type: "watchApps"}, onDone);
-    });
-
-  gClient.addListener("appInstall", function (aState, aType, aPacket) {
-    sendAsyncMessage("installed-event", { manifestURL: aType.manifestURL });
-  });
-
-  gClient.addListener("appUninstall", function (aState, aType, aPacket) {
-    sendAsyncMessage("uninstalled-event", { manifestURL: aType.manifestURL });
-  });
-
-  addMessageListener("appActorRequest", request => {
-    webappActorRequest(request, response => {
-      sendAsyncMessage("appActorResponse", response);
-    });
-  });
-}
-
-function webappActorRequest(request, onResponse) {
-  if (!gActor) {
-    connect(webappActorRequest.bind(null, request, onResponse));
-    return;
-  }
-
-  request.to = gActor;
-  gClient.request(request, onResponse);
-}
-
-
-function downloadURL(url, file) {
-  let channel = NetUtil.newChannel({
-    uri: url,
-    loadUsingSystemPrincipal: true
-  });
-  let istream = channel.open2();
-  let bstream = Cc["@mozilla.org/binaryinputstream;1"]
-                  .createInstance(Ci.nsIBinaryInputStream);
-  bstream.setInputStream(istream);
-  let data = bstream.readBytes(bstream.available());
-
-  let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
-                  .createInstance(Ci.nsIFileOutputStream);
-  ostream.init(file, 0x04 | 0x08 | 0x20, 0o600, 0);
-  ostream.write(data, data.length);
-  ostream.QueryInterface(Ci.nsISafeOutputStream).finish();
-}
-
-// Install a test packaged webapp from data folder
-addMessageListener("install", function (aMessage) {
-  let url = aMessage.url;
-  let appId = aMessage.appId;
-
-  try {
-    // Download its content from mochitest http server
-    // Copy our package to tmp folder, where the actor retrieves it
-    let zip = FileUtils.getDir("TmpD", ["b2g", appId], true, true);
-    zip.append("application.zip");
-    downloadURL(url, zip);
-
-    let request = {type: "install", appId: appId};
-    webappActorRequest(request, function (aResponse) {
-      sendAsyncMessage("installed", aResponse);
-    });
-  } catch (e) {
-    dump("installTestApp exception: " + e + "\n");
-  }
-});
-
-addMessageListener("getAppActor", function (aMessage) {
-  let { manifestURL } = aMessage;
-  let request = {type: "getAppActor", manifestURL: manifestURL};
-  webappActorRequest(request, function (aResponse) {
-    sendAsyncMessage("appActor", aResponse);
-  });
-});
-
-var Frames = [];
-addMessageListener("addFrame", function (aMessage) {
-  let win = Services.wm.getMostRecentWindow("navigator:browser");
-  let doc = win.document;
-  let frame = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
-  frame.setAttribute("mozbrowser", "true");
-  if (aMessage.mozapp) {
-    frame.setAttribute("mozapp", aMessage.mozapp);
-  }
-  if (aMessage.remote) {
-    frame.setAttribute("remote", aMessage.remote);
-  }
-  if (aMessage.src) {
-    frame.setAttribute("src", aMessage.src);
-  }
-  doc.documentElement.appendChild(frame);
-  Frames.push(frame);
-  sendAsyncMessage("frameAdded");
-});
-
-addMessageListener("tweak-app-object", function (aMessage) {
-  let appId = aMessage.appId;
-  let { DOMApplicationRegistry } = Cu.import("resource://gre/modules/Webapps.jsm", {});
-  let reg = DOMApplicationRegistry;
-  if ("removable" in aMessage) {
-    reg.webapps[appId].removable = aMessage.removable;