Merge m-i to a CLOSED TREE m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 16 Sep 2013 23:12:03 -0700
changeset 147371 1d27c4c9871f357612afd79c452ffcde5cbeee69
parent 147348 2f0e38f796101dd2d7ca173030b59e542e50bb89 (current diff)
parent 147370 e50c18dc2122f55ed362675eee10d6a80bbce60d (diff)
child 147372 2520866d58740851d862c7c59246a4e3f8b4a176
child 147384 ac995f2dc72e6b874d44c0004429451c2b432eb8
child 147416 cfb20a0a985754bc1b8da0e2f2332b7745e0bd29
child 147552 802f8b85b97391e652c16d06f70b29ce1e6f3ae3
child 155752 80bab2c86828b3a60c2614b442b9a175f733c72f
push id25300
push userphilringnalda@gmail.com
push dateTue, 17 Sep 2013 06:20:44 +0000
treeherdermozilla-central@1d27c4c9871f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
1d27c4c9871f / 26.0a1 / 20130917030214 / files
nightly linux64
1d27c4c9871f / 26.0a1 / 20130917030214 / files
nightly mac
1d27c4c9871f / 26.0a1 / 20130917030214 / files
nightly win32
1d27c4c9871f / 26.0a1 / 20130917030214 / files
nightly win64
1d27c4c9871f / 26.0a1 / 20130917030214 / 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 m-i to a CLOSED TREE m-c
browser/base/content/browser.js
browser/components/downloads/src/DownloadsTaskbar.jsm
browser/modules/test/unit/moz.build
browser/modules/test/unit/social/Makefile.in
browser/modules/test/unit/social/blocklist.xml
browser/modules/test/unit/social/head.js
browser/modules/test/unit/social/moz.build
browser/modules/test/unit/social/test_social.js
browser/modules/test/unit/social/test_socialDisabledStartup.js
browser/modules/test/unit/social/xpcshell.ini
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -154,21 +154,20 @@ SocialUI = {
         case "social:ambient-notification-changed":
           SocialStatus.updateNotification(data);
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateButton();
             SocialMenu.populate();
           }
           break;
         case "social:profile-changed":
-          // make sure anything that happens here only affects the provider for
-          // which the profile is changing, and that anything we call actually
-          // needs to change based on profile data.
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateProvider();
+            SocialMarks.update();
+            SocialChatBar.update();
           }
           break;
         case "social:frameworker-error":
           if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage();
           }
           break;
 
@@ -211,30 +210,23 @@ SocialUI = {
 
     let toggleCommand = document.getElementById("Social:Toggle");
     toggleCommand.setAttribute("hidden", enabled ? "false" : "true");
 
     if (enabled) {
       // enabled == true means we at least have a defaultProvider
       let provider = Social.provider || Social.defaultProvider;
       // We only need to update the command itself - all our menu items use it.
-      let label;
-      if (Social.providers.length == 1) {
-        label = gNavigatorBundle.getFormattedString(Social.provider
-                                                    ? "social.turnOff.label"
-                                                    : "social.turnOn.label",
-                                                    [provider.name]);
-      } else {
-        label = gNavigatorBundle.getString(Social.provider
-                                           ? "social.turnOffAll.label"
-                                           : "social.turnOnAll.label");
-      }
-      let accesskey = gNavigatorBundle.getString(Social.provider
-                                                 ? "social.turnOff.accesskey"
-                                                 : "social.turnOn.accesskey");
+      let label = gNavigatorBundle.getFormattedString(Social.provider ?
+                                                        "social.turnOff.label" :
+                                                        "social.turnOn.label",
+                                                      [provider.name]);
+      let accesskey = gNavigatorBundle.getString(Social.provider ?
+                                                   "social.turnOff.accesskey" :
+                                                   "social.turnOn.accesskey");
       toggleCommand.setAttribute("label", label);
       toggleCommand.setAttribute("accesskey", accesskey);
     }
   },
 
   _updateMenuItems: function () {
     let provider = Social.provider || Social.defaultProvider;
     if (!provider)
@@ -871,29 +863,27 @@ SocialToolbar = {
     for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
       for (let element of document.getElementsByClassName(className))
         element.hidden = !socialEnabled;
     }
     let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
     toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
 
     let parent = document.getElementById("social-notification-panel");
+    while (parent.hasChildNodes()) {
+      let frame = parent.firstChild;
+      SharedFrame.forgetGroup(frame.id);
+      parent.removeChild(frame);
+    }
+
     let tbi = document.getElementById("social-provider-button");
     if (tbi) {
-      // buttons after social-provider-button are ambient icons, remove the
-      // button and the attached shared frame.
+      // buttons after social-provider-button are ambient icons
       while (tbi.nextSibling) {
-        let tb = tbi.nextSibling;
-        let nid = tb.getAttribute("notificationFrameId");
-        let frame = document.getElementById(nid);
-        if (frame) {
-          SharedFrame.forgetGroup(frame.id);
-          parent.removeChild(frame);
-        }
-        tbi.parentNode.removeChild(tb);
+        tbi.parentNode.removeChild(tbi.nextSibling);
       }
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1114,35 +1114,28 @@ var gBrowserInit = {
     gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
     // active downloads) and speeds up the first-load of the download manager UI.
     // If the user manually opens the download manager before the timeout, the
     // downloads will start right away, and getting the service again won't hurt.
     setTimeout(function() {
-      try {
-        let DownloadsCommon =
-          Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
-        if (DownloadsCommon.useJSTransfer) {
-          // Open the data link without initalizing nsIDownloadManager.
-          DownloadsCommon.initializeAllDataLinks();
-          let DownloadsTaskbar =
-            Cu.import("resource:///modules/DownloadsTaskbar.jsm", {}).DownloadsTaskbar;
-          DownloadsTaskbar.registerIndicator(window);
-        } else {
-          // Initalizing nsIDownloadManager will trigger the data link.
-          Services.downloads;
-          let DownloadTaskbarProgress =
-            Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
-          DownloadTaskbarProgress.onBrowserWindowLoad(window);
-        }
-      } catch (ex) {
-        Cu.reportError(ex);
+      let DownloadsCommon =
+        Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
+      if (DownloadsCommon.useJSTransfer) {
+        // Open the data link without initalizing nsIDownloadManager.
+        DownloadsCommon.initializeAllDataLinks();
+      } else {
+        // Initalizing nsIDownloadManager will trigger the data link.
+        Services.downloads;
       }
+      let DownloadTaskbarProgress =
+        Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
+      DownloadTaskbarProgress.onBrowserWindowLoad(window);
     }, 10000);
 
     // The object handling the downloads indicator is also initialized here in the
     // delayed startup function, but the actual indicator element is not loaded
     // unless there are downloads to be displayed.
     DownloadsButton.initializeIndicator();
 
 #ifndef XP_MACOSX
--- a/browser/base/content/test/social/browser_social_multiworker.js
+++ b/browser/base/content/test/social/browser_social_multiworker.js
@@ -2,17 +2,16 @@
  * 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/. */
 
 function test() {
   waitForExplicitFinish();
 
   Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
   runSocialTestWithProvider(gProviders, function (finishcb) {
-    Social.enabled = true;
     runSocialTests(tests, undefined, undefined, function() {
       Services.prefs.clearUserPref("social.allowMultipleWorkers");
       finishcb();
     });
   });
 }
 
 let gProviders = [
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -1,42 +1,24 @@
 /* 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/. */
 
-let manifests = [{ // normal provider
-  name: "provider example.com",
+let manifest = { // normal provider
+  name: "provider 1",
   origin: "https://example.com",
   workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
   iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
-}, { // used for testing install
-  name: "provider test1",
-  origin: "https://test1.example.com",
-  statusURL: "https://test1.example.com/browser/browser/base/content/test/social/social_panel.html",
-  iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png",
-}];
+};
 
 function test() {
   waitForExplicitFinish();
 
-  // required to test status button in combination with the toolbaritem
-  Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
-
-  // Preset the currentSet so the statusbutton is in the toolbar on addition. We
-  // bypass the SocialStatus class here since it requires the manifest already
-  // be installed.
-  let tbh = SocialStatus._toolbarHelper;
-  tbh.setPersistentPosition(tbh.idFromOrgin(manifests[1].origin));
-
-  runSocialTestWithProvider(manifests, function (finishcb) {
-    runSocialTests(tests, undefined, undefined, function() {
-      Services.prefs.clearUserPref("social.allowMultipleWorkers");
-      SocialStatus.removePosition(manifests[1].origin);
-      finishcb();
-    });
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    runSocialTests(tests, undefined, undefined, finishcb);
   });
 }
 
 var tests = {
   testProfileNone: function(next, useNull) {
     let profile = useNull ? null : {};
     Social.provider.updateUserProfile(profile);
     // check dom values
@@ -50,17 +32,17 @@ var tests = {
     is(userButton.getAttribute("label"), notLoggedInStatusValue, "label reflects not being logged in");
     next();
   },
   testProfileNull: function(next) {
     this.testProfileNone(next, true);
   },
   testProfileSet: function(next) {
     let statusIcon = document.getElementById("social-provider-button").style.listStyleImage;
-    is(statusIcon, "url(\"" + manifests[0].iconURL + "\")", "manifest iconURL is showing");
+    is(statusIcon, "url(\"" + manifest.iconURL + "\")", "manifest iconURL is showing");
     let profile = {
       portrait: "https://example.com/portrait.jpg",
       userName: "trickster",
       displayName: "Kuma Lisa",
       profileURL: "http://example.com/Kuma_Lisa",
       iconURL: "https://example.com/browser/browser/base/content/test/social/moz.png"
     }
     Social.provider.updateUserProfile(profile);
@@ -180,42 +162,24 @@ var tests = {
         is(menuitem.getAttribute("label"), "Test Ambient 1 \u2046", "Keyboard accessible ambient menuitem should have specified label");
         toolsPopup.hidePopup();
         next();
       }, false);
       document.getElementById("menu_ToolsPopup").openPopup();
     }, "statusIcon was never found");
   },
   testProfileUnset: function(next) {
-    let panel = document.getElementById("social-notification-panel");
-    // load the status button for provider 2
-    let provider = Social._getProviderFromOrigin(manifests[1].origin);
-    let id = SocialStatus._toolbarHelper.idFromOrgin(provider.origin);
-    let btn = document.getElementById(id)
-    ok(btn, "got a status button");
-    // cheat a little, we want the iframe for the status button to be created,
-    // not testing the statusbutton itself here.
-    SocialStatus._attachNotificatonPanel(btn, provider);
-
-    let numIcons = Object.keys(Social.provider.ambientNotificationIcons).length;
-    let ambientIcons = document.querySelectorAll("#social-toolbar-item > toolbarbutton[type='badged']");
-    is(numIcons, ambientIcons.length, "all ambient icons exist");
-    is(panel.childNodes.length, ambientIcons.length + 1, "frames all exist");
+    Social.provider.updateUserProfile({});
+    // check dom values
+    let ambientIcons = document.querySelectorAll("#social-toolbar-item > box");
+    for (let ambientIcon of ambientIcons) {
+      ok(ambientIcon.collapsed, "ambient icon (" + ambientIcon.id + ") is collapsed");
+    }
     
-    // we need to wait until after social:profile-changed has completed
-    waitForNotification("social:profile-changed", function() {
-      // let the notifications finish first
-      executeSoon(function() {
-        let icons = document.querySelectorAll("#social-toolbar-item > toolbarbutton[type='badged']");
-        is(icons.length, 0, "ambient icons have been removed");
-        is(panel.childNodes.length, 1, "frame still exists");
-        next();
-      });
-    });
-    Social.provider.updateUserProfile({});
+    next();
   },
   testMenuitemsExist: function(next) {
     let toggleSidebarMenuitems = document.getElementsByClassName("social-toggle-sidebar-menuitem");
     is(toggleSidebarMenuitems.length, 2, "Toggle Sidebar menuitems exist");
     let toggleDesktopNotificationsMenuitems = document.getElementsByClassName("social-toggle-notifications-menuitem");
     is(toggleDesktopNotificationsMenuitems.length, 2, "Toggle notifications menuitems exist");
     let toggleSocialMenuitems = document.getElementsByClassName("social-toggle-menuitem");
     is(toggleSocialMenuitems.length, 2, "Toggle Social menuitems exist");
@@ -225,10 +189,10 @@ var tests = {
     let enabled = Services.prefs.getBoolPref("social.toast-notifications.enabled");
     let cmd = document.getElementById("Social:ToggleNotifications");
     is(cmd.getAttribute("checked"), enabled ? "true" : "false");
     enabled = !enabled;
     Services.prefs.setBoolPref("social.toast-notifications.enabled", enabled);
     is(cmd.getAttribute("checked"), enabled ? "true" : "false");
     Services.prefs.clearUserPref("social.toast-notifications.enabled");
     next();
-  }
+  },
 }
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -293,24 +293,16 @@ function checkSocialUI(win) {
   isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
 
   // broadcasters.
   isbool(!doc.getElementById("socialActiveBroadcaster").hidden, active, "socialActiveBroadcaster hidden?");
   // and report on overall success of failure of the various checks here.
   is(numGoodTests, numTests, "The Social UI tests succeeded.")
 }
 
-function waitForNotification(topic, cb) {
-  function observer(subject, topic, data) {
-    Services.obs.removeObserver(observer, topic);
-    cb();
-  }
-  Services.obs.addObserver(observer, topic, false);
-}
-
 // blocklist testing
 function updateBlocklist(aCallback) {
   var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
                           .getService(Ci.nsITimerCallback);
   var observer = function() {
     Services.obs.removeObserver(observer, "blocklist-updated");
     if (aCallback)
       executeSoon(aCallback);
deleted file mode 100644
--- a/browser/components/downloads/src/DownloadsTaskbar.jsm
+++ /dev/null
@@ -1,180 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
-/* 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/. */
-
-/**
- * Handles the download progress indicator in the taskbar.
- */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = [
-  "DownloadsTaskbar",
-];
-
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
-                                  "resource://gre/modules/Downloads.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
-                                  "resource:///modules/RecentWindow.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
-  if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
-    return null;
-  }
-  let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
-                     .getService(Ci.nsIWinTaskbar);
-  return winTaskbar.available && winTaskbar;
-});
-
-XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
-  return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
-         Cc["@mozilla.org/widget/macdocksupport;1"]
-           .getService(Ci.nsITaskbarProgress);
-});
-
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsTaskbar
-
-/**
- * Handles the download progress indicator in the taskbar.
- */
-this.DownloadsTaskbar = {
-  /**
-   * Underlying DownloadSummary providing the aggregate download information, or
-   * null if the indicator has never been initialized.
-   */
-  _summary: null,
-
-  /**
-   * nsITaskbarProgress object to which download information is dispatched.
-   * This can be null if the indicator has never been initialized or if the
-   * indicator is currently hidden on Windows.
-   */
-  _taskbarProgress: null,
-
-  /**
-   * This method is called after a new browser window is opened, and ensures
-   * that the download progress indicator is displayed in the taskbar.
-   *
-   * On Windows, the indicator is attached to the first browser window that
-   * calls this method.  When the window is closed, the indicator is moved to
-   * another browser window, if available, in no particular order.  When there
-   * are no browser windows visible, the indicator is hidden.
-   *
-   * On Mac OS X, the indicator is initialized globally when this method is
-   * called for the first time.  Subsequent calls have no effect.
-   *
-   * @param aBrowserWindow
-   *        nsIDOMWindow object of the newly opened browser window to which the
-   *        indicator may be attached.
-   */
-  registerIndicator: function (aBrowserWindow)
-  {
-    if (!this._taskbarProgress) {
-      if (gMacTaskbarProgress) {
-        // On Mac OS X, we have to register the global indicator only once.
-        this._taskbarProgress = gMacTaskbarProgress;
-        // Free the XPCOM reference on shutdown, to prevent detecting a leak.
-        Services.obs.addObserver(() => {
-          this._taskbarProgress = null;
-          gMacTaskbarProgress = null;
-        }, "quit-application-granted", false);
-      } else if (gWinTaskbar) {
-        // On Windows, the indicator is currently hidden because we have no
-        // previous browser window, thus we should attach the indicator now.
-        this._attachIndicator(aBrowserWindow);
-      } else {
-        // The taskbar indicator is not available on this platform.
-        return;
-      }
-    }
-
-    // Ensure that the DownloadSummary object will be created asynchronously.
-    if (!this._summary) {
-      Downloads.getSummary(Downloads.ALL).then(summary => {
-        // In case the method is re-entered, we simply ignore redundant
-        // invocations of the callback, instead of keeping separate state.
-        if (this._summary) {
-          return;
-        }
-        this._summary = summary;
-        this._summary.addView(this);
-      });
-    }
-  },
-
-  /**
-   * On Windows, attaches the taskbar indicator to the specified browser window.
-   */
-  _attachIndicator: function (aWindow)
-  {
-    // Activate the indicator on the specified window.
-    let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIWebNavigation)
-                          .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIXULWindow).docShell;
-    this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
-
-    // If the DownloadSummary object has already been created, we should update
-    // the state of the new indicator, otherwise it will be updated as soon as
-    // the DownloadSummary view is registered.
-    if (this._summary) {
-      this.onSummaryChanged();
-    }
-
-    aWindow.addEventListener("unload", () => {
-      // Locate another browser window, excluding the one being closed.
-      let browserWindow = RecentWindow.getMostRecentBrowserWindow();
-      if (browserWindow) {
-        // Move the progress indicator to the other browser window.
-        this._attachIndicator(browserWindow);
-      } else {
-        // The last browser window has been closed.  We remove the reference to
-        // the taskbar progress object so that the indicator will be registered
-        // again on the next browser window that is opened.
-        this._taskbarProgress = null;
-      }
-    }, false);
-  },
-
-  //////////////////////////////////////////////////////////////////////////////
-  //// DownloadSummary view
-
-  onSummaryChanged: function ()
-  {
-    // If the last browser window has been closed, we have no indicator anymore.
-    if (!this._taskbarProgress) {
-      return;
-    }
-
-    if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
-      this._taskbarProgress.setProgressState(
-                               Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
-    } else {
-      // For a brief moment before completion, some download components may
-      // report more transferred bytes than the total number of bytes.  Thus,
-      // ensure that we never break the expectations of the progress indicator.
-      let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
-                                          this._summary.progressCurrentBytes);
-      this._taskbarProgress.setProgressState(
-                               Ci.nsITaskbarProgress.STATE_NORMAL,
-                               progressCurrentBytes,
-                               this._summary.progressTotalBytes);
-    }
-  },
-};
--- a/browser/components/downloads/src/moz.build
+++ b/browser/components/downloads/src/moz.build
@@ -8,11 +8,10 @@ EXTRA_COMPONENTS += [
     'BrowserDownloads.manifest',
     'DownloadsStartup.js',
     'DownloadsUI.js',
 ]
 
 EXTRA_JS_MODULES += [
     'DownloadsCommon.jsm',
     'DownloadsLogger.jsm',
-    'DownloadsTaskbar.jsm',
 ]
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -423,18 +423,16 @@ service.install.ok.accesskey=E
 service.install.learnmore=Learn Moreā€¦
 
 # LOCALIZATION NOTE (social.turnOff.label): %S is the name of the social provider
 social.turnOff.label=Turn off %S
 social.turnOff.accesskey=T
 # LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
 social.turnOn.label=Turn on %S
 social.turnOn.accesskey=T
-social.turnOffAll.label=Turn off all Services
-social.turnOnAll.label=Turn on all Services
 
 # LOCALIZATION NOTE (social.markpageMenu.label): %S is the name of the social provider
 social.markpageMenu.label=Save Page to %S
 # LOCALIZATION NOTE (social.marklinkMenu.label): %S is the name of the social provider
 social.marklinkMenu.label=Save Link to %S
 
 # LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
 social.error.message=%1$S is unable to connect with %2$S right now.
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -29,18 +29,19 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIScriptableUnescapeHTML");
 
 // Add a pref observer for the enabled state
 function prefObserver(subject, topic, data) {
   let enable = Services.prefs.getBoolPref("social.enabled");
   if (enable && !Social.provider) {
     // this will result in setting Social.provider
     SocialService.getOrderedProviderList(function(providers) {
+      Social._updateProviderCache(providers);
       Social.enabled = true;
-      Social._updateProviderCache(providers);
+      Services.obs.notifyObservers(null, "social:providers-changed", null);
     });
   } else if (!enable && Social.provider) {
     Social.provider = null;
   }
 }
 
 Services.prefs.addObserver("social.enabled", prefObserver, false);
 Services.obs.addObserver(function xpcomShutdown() {
@@ -162,64 +163,58 @@ this.Social = {
     }
     this.initialized = true;
     // if SocialService.hasEnabledProviders, retreive the providers so the
     // front-end can generate UI
     if (SocialService.hasEnabledProviders) {
       // Retrieve the current set of providers, and set the current provider.
       SocialService.getOrderedProviderList(function (providers) {
         Social._updateProviderCache(providers);
-        Social._updateWorkerState(SocialService.enabled);
+        Social._updateWorkerState(true);
       });
     }
 
     // Register an observer for changes to the provider list
     SocialService.registerProviderListener(function providerListener(topic, origin, providers) {
       // An engine change caused by adding/removing a provider should notify.
       // any providers we receive are enabled in the AddonsManager
       if (topic == "provider-installed" || topic == "provider-uninstalled") {
         // installed/uninstalled do not send the providers param
         Services.obs.notifyObservers(null, "social:" + topic, origin);
         return;
       }
-      if (topic == "provider-enabled") {
+      if (topic == "provider-enabled" || topic == "provider-disabled") {
         Social._updateProviderCache(providers);
-        Social._updateWorkerState(Social.enabled);
-        Services.obs.notifyObservers(null, "social:" + topic, origin);
-        return;
-      }
-      if (topic == "provider-disabled") {
-        // a provider was removed from the list of providers, that does not
-        // affect worker state for other providers
-        Social._updateProviderCache(providers);
+        Social._updateWorkerState(true);
+        Services.obs.notifyObservers(null, "social:providers-changed", null);
         Services.obs.notifyObservers(null, "social:" + topic, origin);
         return;
       }
       if (topic == "provider-update") {
         // a provider has self-updated its manifest, we need to update our cache
         // and reload the provider.
         Social._updateProviderCache(providers);
         let provider = Social._getProviderFromOrigin(origin);
         provider.reload();
+        Services.obs.notifyObservers(null, "social:providers-changed", null);
       }
     });
   },
 
   _updateWorkerState: function(enable) {
     // ensure that our providers are all disabled, and enabled if we allow
     // multiple workers
     if (enable && !Social.allowMultipleWorkers)
       return;
     [p.enabled = enable for (p of Social.providers) if (p.enabled != enable)];
   },
 
   // Called to update our cache of providers and set the current provider
   _updateProviderCache: function (providers) {
     this.providers = providers;
-    Services.obs.notifyObservers(null, "social:providers-changed", null);
 
     // If social is currently disabled there's nothing else to do other than
     // to notify about the lack of a provider.
     if (!SocialService.enabled) {
       Services.obs.notifyObservers(null, "social:provider-set", null);
       return;
     }
     // Otherwise set the provider.
--- a/browser/modules/test/moz.build
+++ b/browser/modules/test/moz.build
@@ -1,7 +1,7 @@
 # -*- Mode: python; c-basic-offset: 4; 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/.
 
-DIRS += ['chrome', 'unit']
+DIRS += ['chrome']
deleted file mode 100644
--- a/browser/modules/test/unit/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; 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/.
-
-DIRS += ['social']
-
-XPCSHELL_TESTS_MANIFESTS += ['social/xpcshell.ini']
deleted file mode 100644
--- a/browser/modules/test/unit/social/Makefile.in
+++ /dev/null
@@ -1,11 +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/.
-
-XPCSHELL_RESOURCES = \
-  xpcshell.ini \
-  head.js \
-  blocklist.xml \
-  test_social.js \
-  test_socialDisabledStartup.js \
-  $(NULL)
deleted file mode 100644
--- a/browser/modules/test/unit/social/blocklist.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
-  <emItems>
-    <emItem  blockID="s1" id="bad.com@services.mozilla.org"></emItem>
-  </emItems>
-</blocklist>
deleted file mode 100644
--- a/browser/modules/test/unit/social/head.js
+++ /dev/null
@@ -1,146 +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/. */
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-var Social, SocialService;
-
-let manifests = [
-  {
-    name: "provider 1",
-    origin: "https://example1.com",
-    sidebarURL: "https://example1.com/sidebar/",
-  },
-  {
-    name: "provider 2",
-    origin: "https://example2.com",
-    sidebarURL: "https://example1.com/sidebar/",
-  }
-];
-
-const MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
-
-// SocialProvider class relies on blocklisting being enabled.  To enable
-// blocklisting, we have to setup an app and initialize the blocklist (see
-// initApp below).
-const gProfD = do_get_profile();
-
-const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
-const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
-
-function createAppInfo(id, name, version, platformVersion) {
-  gAppInfo = {
-    // nsIXULAppInfo
-    vendor: "Mozilla",
-    name: name,
-    ID: id,
-    version: version,
-    appBuildID: "2007010101",
-    platformVersion: platformVersion ? platformVersion : "1.0",
-    platformBuildID: "2007010101",
-
-    // nsIXULRuntime
-    inSafeMode: false,
-    logConsoleErrors: true,
-    OS: "XPCShell",
-    XPCOMABI: "noarch-spidermonkey",
-    invalidateCachesOnRestart: function invalidateCachesOnRestart() {
-      // Do nothing
-    },
-
-    // nsICrashReporter
-    annotations: {},
-
-    annotateCrashReport: function(key, data) {
-      this.annotations[key] = data;
-    },
-
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo,
-                                           Ci.nsIXULRuntime,
-                                           Ci.nsICrashReporter,
-                                           Ci.nsISupports])
-  };
-
-  var XULAppInfoFactory = {
-    createInstance: function (outer, iid) {
-      if (outer != null)
-        throw Components.results.NS_ERROR_NO_AGGREGATION;
-      return gAppInfo.QueryInterface(iid);
-    }
-  };
-  var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-  registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
-                            XULAPPINFO_CONTRACTID, XULAppInfoFactory);
-}
-
-function initApp() {
-  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
-  // prepare a blocklist file for the blocklist service
-  var blocklistFile = gProfD.clone();
-  blocklistFile.append("blocklist.xml");
-  if (blocklistFile.exists())
-    blocklistFile.remove(false);
-  var source = do_get_file("blocklist.xml");
-  source.copyTo(gProfD, "blocklist.xml");
-}
-
-function setManifestPref(manifest) {
-  let string = Cc["@mozilla.org/supports-string;1"].
-               createInstance(Ci.nsISupportsString);
-  string.data = JSON.stringify(manifest);
-  Services.prefs.setComplexValue("social.manifest." + manifest.origin, Ci.nsISupportsString, string);
-}
-
-function do_wait_observer(topic, cb) {
-  function observer(subject, topic, data) {
-    Services.obs.removeObserver(observer, topic);
-    cb();
-  }
-  Services.obs.addObserver(observer, topic, false);
-}
-
-function do_initialize_social(enabledOnStartup, cb) {
-  initApp();
-
-  manifests.forEach(function (manifest) {
-    setManifestPref(manifest);
-  });
-  // Set both providers active and flag the first one as "current"
-  let activeVal = Cc["@mozilla.org/supports-string;1"].
-             createInstance(Ci.nsISupportsString);
-  let active = {};
-  for (let m of manifests)
-    active[m.origin] = 1;
-  activeVal.data = JSON.stringify(active);
-  Services.prefs.setComplexValue("social.activeProviders",
-                                 Ci.nsISupportsString, activeVal);
-  Services.prefs.setCharPref("social.provider.current", manifests[0].origin);
-  Services.prefs.setBoolPref("social.enabled", enabledOnStartup);
-
-  do_register_cleanup(function() {
-    manifests.forEach(function (manifest) {
-      Services.prefs.clearUserPref("social.manifest." + manifest.origin);
-    });
-    Services.prefs.clearUserPref("social.enabled");
-    Services.prefs.clearUserPref("social.provider.current");
-    Services.prefs.clearUserPref("social.activeProviders");
-  });
-
-  // expecting 2 providers installed
-  do_wait_observer("social:providers-changed", function() {
-    do_check_eq(Social.providers.length, 2, "2 providers installed");
-    cb();
-  });
-
-  // import and initialize everything
-  SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
-  do_check_eq(SocialService.enabled, enabledOnStartup, "service is doing its thing");
-  do_check_true(SocialService.hasEnabledProviders, "Service has enabled providers");
-  Social = Cu.import("resource:///modules/Social.jsm", {}).Social;
-  do_check_false(Social.initialized, "Social is not initialized");
-  Social.init();
-  do_check_true(Social.initialized, "Social is initialized");
-}
deleted file mode 100644
--- a/browser/modules/test/unit/social/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; 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/.
-
deleted file mode 100644
--- a/browser/modules/test/unit/social/test_social.js
+++ /dev/null
@@ -1,34 +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/. */
-
-function run_test() {
-  // we are testing worker startup specifically
-  Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
-  do_register_cleanup(function() {
-    Services.prefs.clearUserPref("social.allowMultipleWorkers");
-  });
-  do_test_pending();
-  add_test(testStartupEnabled);
-  add_test(testDisableAfterStartup);
-  do_initialize_social(true, run_next_test);
-}
-
-function testStartupEnabled() {
-  // wait on startup before continuing
-  do_check_eq(Social.providers.length, 2, "two social providers enabled");
-  do_check_true(Social.providers[0].enabled, "provider is enabled");
-  do_check_true(Social.providers[1].enabled, "provider is enabled");
-  run_next_test();
-}
-
-function testDisableAfterStartup() {
-  do_wait_observer("social:provider-set", function() {
-    do_check_eq(Social.enabled, false, "Social is disabled");
-    do_check_false(Social.providers[0].enabled, "provider is enabled");
-    do_check_false(Social.providers[1].enabled, "provider is enabled");
-    do_test_finished();
-    run_next_test();
-  });
-  Social.enabled = false;
-}
deleted file mode 100644
--- a/browser/modules/test/unit/social/test_socialDisabledStartup.js
+++ /dev/null
@@ -1,35 +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/. */
-
-function run_test() {
-  // we are testing worker startup specifically
-  Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
-  do_register_cleanup(function() {
-    Services.prefs.clearUserPref("social.allowMultipleWorkers");
-  });
-  do_test_pending();
-  add_test(testStartupDisabled);
-  add_test(testEnableAfterStartup);
-  do_initialize_social(false, run_next_test);
-}
-
-function testStartupDisabled() {
-  // wait on startup before continuing
-  do_check_eq(Social.providers.length, 2, "two social providers available");
-  do_check_false(Social.providers[0].enabled, "provider is enabled");
-  do_check_false(Social.providers[1].enabled, "provider is enabled");
-  run_next_test();
-}
-
-function testEnableAfterStartup() {
-  do_wait_observer("social:provider-set", function() {
-    do_check_true(Social.enabled, "Social is enabled");
-    do_check_eq(Social.providers.length, 2, "two social providers available");
-    do_check_true(Social.providers[0].enabled, "provider is enabled");
-    do_check_true(Social.providers[1].enabled, "provider is enabled");
-    do_test_finished();
-    run_next_test();
-  });
-  Social.enabled = true;
-}
deleted file mode 100644
--- a/browser/modules/test/unit/social/xpcshell.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[DEFAULT]
-head = head.js
-tail =
-firefox-appdir = browser
-
-[test_social.js]
-
-[test_socialDisabledStartup.js]
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -711,19 +711,19 @@ public:
 
 } /* anonymous namespace */
 
 nsresult
 nsScriptLoader::ProcessOffThreadRequest(void **aOffThreadToken)
 {
     nsCOMPtr<nsScriptLoadRequest> request = mOffThreadScriptRequest;
     mOffThreadScriptRequest = nullptr;
+    nsresult rv = ProcessRequest(request, aOffThreadToken);
     mDocument->UnblockOnload(false);
-
-    return ProcessRequest(request, aOffThreadToken);
+    return rv;
 }
 
 NS_IMETHODIMP
 NotifyOffThreadScriptLoadCompletedRunnable::Run()
 {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsresult rv = mLoader->ProcessOffThreadRequest(&mToken);
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -42,16 +42,48 @@ HasOpaqueAncestorLayer(Layer* aLayer)
 {
   for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) {
     if (l->GetContentFlags() & Layer::CONTENT_OPAQUE)
       return true;
   }
   return false;
 }
 
+/**
+ * Returns a rectangle of content painted opaquely by aLayer. Very consertative;
+ * bails by returning an empty rect in any tricky situations.
+ */
+static nsIntRect
+GetOpaqueRect(Layer* aLayer)
+{
+  nsIntRect result;
+  // Just bail if there's anything difficult to handle.
+  if (!aLayer->GetEffectiveTransform().IsIdentity() ||
+      aLayer->GetEffectiveOpacity() != 1.0f ||
+      aLayer->GetMaskLayer()) {
+    return result;
+  }
+  if (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) {
+    result = aLayer->GetEffectiveVisibleRegion().GetLargestRectangle();
+  } else {
+    // Drill down into RefLayers because that's what we particularly care about;
+    // layer construction for aLayer will not have known about the opaqueness
+    // of any RefLayer subtrees.
+    RefLayer* refLayer = aLayer->AsRefLayer();
+    if (refLayer && refLayer->GetFirstChild()) {
+      result = GetOpaqueRect(refLayer->GetFirstChild());
+    }
+  }
+  const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
+  if (clipRect) {
+    result.IntersectRect(result, *clipRect);
+  }
+  return result;
+}
+
 template<class ContainerT> void
 ContainerRender(ContainerT* aContainer,
                 const nsIntPoint& aOffset,
                 LayerManagerComposite* aManager,
                 const nsIntRect& aClipRect)
 {
   /**
    * Setup our temporary surface for rendering the contents of this container.
@@ -138,16 +170,33 @@ ContainerRender(ContainerT* aContainer,
   for (uint32_t i = 0; i < children.Length(); i++) {
     LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
 
     if (layerToRender->GetLayer()->GetEffectiveVisibleRegion().IsEmpty() &&
         !layerToRender->GetLayer()->AsContainerLayer()) {
       continue;
     }
 
+    if (i + 1 < children.Length() &&
+        layerToRender->GetLayer()->GetEffectiveTransform().IsIdentity()) {
+      LayerComposite* nextLayer = static_cast<LayerComposite*>(children.ElementAt(i + 1)->ImplData());
+      nsIntRect nextLayerOpaqueRect;
+      if (nextLayer && nextLayer->GetLayer()) {
+        nextLayerOpaqueRect = GetOpaqueRect(nextLayer->GetLayer());
+      }
+      if (!nextLayerOpaqueRect.IsEmpty()) {
+        nsIntRegion visibleRegion;
+        visibleRegion.Sub(layerToRender->GetShadowVisibleRegion(), nextLayerOpaqueRect);
+        layerToRender->SetShadowVisibleRegion(visibleRegion);
+        if (visibleRegion.IsEmpty()) {
+          continue;
+        }
+      }
+    }
+
     nsIntRect clipRect = layerToRender->GetLayer()->
         CalculateScissorRect(aClipRect, &aManager->GetWorldTransform());
     if (clipRect.IsEmpty()) {
       continue;
     }
 
     layerToRender->RenderLayer(childOffset, clipRect);
     // invariant: our GL context should be current here, I don't think we can
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -41,16 +41,17 @@
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 #include "nsViewManager.h"
 #include "ImageLayers.h"
 #include "ImageContainer.h"
 #include "nsCanvasFrame.h"
 #include "StickyScrollContainer.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/Preferences.h"
 
 #include <stdint.h>
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -1,27 +1,25 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxPlatform.h"
 #include "AnimationCommon.h"
 #include "nsRuleData.h"
-#include "nsCSSFrameConstructor.h"
 #include "nsCSSValue.h"
 #include "nsStyleContext.h"
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/Preferences.h"
 #include "RestyleManager.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace css {
 
 /* static */ bool
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -8,17 +8,17 @@
  * stylesheet
  */
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Util.h"
 
 #include "mozilla/css/Declaration.h"
 #include "nsPrintfCString.h"
-#include "mozilla/Preferences.h"
+#include "gfxFontConstants.h"
 
 namespace mozilla {
 namespace css {
 
 // check that we can fit all the CSS properties into a uint16_t
 // for the mOrder array
 static_assert(eCSSProperty_COUNT_no_shorthands - 1 <= UINT16_MAX,
               "CSS longhand property numbers no longer fit in a uint16_t");
--- a/layout/style/StyleRule.h
+++ b/layout/style/StyleRule.h
@@ -7,26 +7,23 @@
  * representation of CSS style rules (selectors+declaration) and CSS
  * selectors
  */
 
 #ifndef mozilla_css_StyleRule_h__
 #define mozilla_css_StyleRule_h__
 
 #include "mozilla/Attributes.h"
-
-#include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/Rule.h"
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSPseudoClasses.h"
-#include "nsAutoPtr.h"
 
 class nsIAtom;
 class nsCSSStyleSheet;
 struct nsCSSSelectorList;
 class nsCSSCompressedDataBlock;
 
 struct nsAtomList {
 public:
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -7,19 +7,17 @@
 
 #include "mozilla/MemoryReporting.h"
 
 #include "nsPresContext.h"
 #include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsCSSRules.h"
 #include "nsStyleAnimation.h"
-#include "nsSMILKeySpline.h"
 #include "nsEventDispatcher.h"
-#include "nsCSSFrameConstructor.h"
 #include "nsLayoutUtils.h"
 #include <math.h>
 
 using namespace mozilla;
 using namespace mozilla::css;
 
 ElementAnimations::ElementAnimations(mozilla::dom::Element *aElement, nsIAtom *aElementProperty,
                                      nsAnimationManager *aAnimationManager)
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -3,25 +3,22 @@
  * 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/. */
 #ifndef nsAnimationManager_h_
 #define nsAnimationManager_h_
 
 #include "mozilla/Attributes.h"
 #include "AnimationCommon.h"
 #include "nsCSSPseudoElements.h"
-#include "nsStyleContext.h"
-#include "nsDataHashtable.h"
 #include "nsGUIEvent.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/Preferences.h"
-#include "nsThreadUtils.h"
 
 class nsCSSKeyframesRule;
+class nsStyleContext;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 }
 }
 
 struct AnimationEventInfo {
--- a/layout/style/nsCSSDataBlock.h
+++ b/layout/style/nsCSSDataBlock.h
@@ -9,16 +9,18 @@
  */
 
 #ifndef nsCSSDataBlock_h__
 #define nsCSSDataBlock_h__
 
 #include "mozilla/MemoryReporting.h"
 #include "nsCSSProps.h"
 #include "nsCSSPropertySet.h"
+#include "nsCSSValue.h"
+#include "imgRequestProxy.h"
 
 struct nsRuleData;
 class nsCSSExpandedDataBlock;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 }
--- a/layout/style/nsCSSKeywords.cpp
+++ b/layout/style/nsCSSKeywords.cpp
@@ -3,17 +3,16 @@
  * 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/. */
 
 /* keywords used within CSS property values */
 
 #include "nsCSSKeywords.h"
 #include "nsString.h"
 #include "nsStaticNameTable.h"
-#include "nsReadableUtils.h"
 
 // required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it
 extern const char* const kCSSRawKeywords[];
 
 // define an array of all CSS keywords
 #define CSS_KEY(_name,_id) #_name,
 const char* const kCSSRawKeywords[] = {
 #include "nsCSSKeywordList.h"
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -20,17 +20,16 @@
 #include "mozilla/css/NameSpaceRule.h"
 #include "nsTArray.h"
 #include "nsCSSStyleSheet.h"
 #include "mozilla/css/Declaration.h"
 #include "nsStyleConsts.h"
 #include "nsNetUtil.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
-#include "nsReadableUtils.h"
 #include "nsIAtom.h"
 #include "nsColor.h"
 #include "nsCSSPseudoClasses.h"
 #include "nsCSSPseudoElements.h"
 #include "nsINameSpaceManager.h"
 #include "nsXMLNameSpaceMap.h"
 #include "nsError.h"
 #include "nsIMediaList.h"
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -14,18 +14,20 @@
 #include "nsCSSKeywords.h"
 #include "nsStyleConsts.h"
 #include "nsIWidget.h"
 #include "nsThemeConstants.h"  // For system widget appearance types
 
 #include "mozilla/LookAndFeel.h" // for system colors
 
 #include "nsString.h"
-#include "nsReadableUtils.h"
 #include "nsStaticNameTable.h"
+#include "nsStyleConsts.h"
+#include "gfxFontConstants.h"
+#include "nsStyleStruct.h"
 
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 
 // required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it
 extern const char* const kCSSRawProperties[];
 
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -7,19 +7,18 @@
  * methods for dealing with CSS properties and tables of the keyword
  * values they accept
  */
 
 #ifndef nsCSSProps_h___
 #define nsCSSProps_h___
 
 #include "nsString.h"
-#include "nsChangeHint.h"
 #include "nsCSSProperty.h"
-#include "nsStyleStruct.h"
+#include "nsStyleStructFwd.h"
 #include "nsCSSKeywords.h"
 
 // Flags for ParseVariant method
 #define VARIANT_KEYWORD         0x000001  // K
 #define VARIANT_LENGTH          0x000002  // L
 #define VARIANT_PERCENT         0x000004  // P
 #define VARIANT_COLOR           0x000008  // C eCSSUnit_Color, eCSSUnit_Ident (e.g.  "red")
 #define VARIANT_URL             0x000010  // U
--- a/layout/style/nsCSSPseudoClasses.cpp
+++ b/layout/style/nsCSSPseudoClasses.cpp
@@ -5,16 +5,17 @@
 
 /* atom list for CSS pseudo-classes */
 
 #include "mozilla/Util.h"
 
 #include "nsCSSPseudoClasses.h"
 #include "nsStaticAtom.h"
 #include "mozilla/Preferences.h"
+#include "nsString.h"
 
 using namespace mozilla;
 
 // define storage for all atoms
 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
   static nsIAtom* sPseudoClass_##_name;
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
--- a/layout/style/nsCSSPseudoClasses.h
+++ b/layout/style/nsCSSPseudoClasses.h
@@ -3,17 +3,17 @@
  * 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/. */
 
 /* atom list for CSS pseudo-classes */
 
 #ifndef nsCSSPseudoClasses_h___
 #define nsCSSPseudoClasses_h___
 
-#include "nsString.h"
+#include "nsStringFwd.h"
 
 class nsIAtom;
 
 class nsCSSPseudoClasses {
 public:
 
   static void AddRefAtoms();
 
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -14,52 +14,43 @@
 // Including plarena.h must come first to avoid it being included by some
 // header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective.
 #define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
 #include "plarena.h"
 
 #include "nsCSSRuleProcessor.h"
 #include "nsRuleProcessorData.h"
 #include <algorithm>
-#include "nsCRT.h"
 #include "nsIAtom.h"
 #include "pldhash.h"
 #include "nsICSSPseudoComparator.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/css/GroupRule.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
-#include "nsEventStateManager.h"
 #include "nsGkAtoms.h"
-#include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsError.h"
 #include "nsRuleWalker.h"
 #include "nsCSSPseudoClasses.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIContent.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsStyleUtil.h"
 #include "nsQuickSort.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsAttrName.h"
-#include "nsServiceManagerUtils.h"
 #include "nsTArray.h"
 #include "nsContentUtils.h"
 #include "nsIMediaList.h"
 #include "nsCSSRules.h"
-#include "nsIPrincipal.h"
 #include "nsStyleSet.h"
-#include "prlog.h"
-#include "nsIObserverService.h"
-#include "nsNetCID.h"
-#include "mozilla/Services.h"
 #include "mozilla/dom/Element.h"
 #include "nsNthIndexCache.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Util.h"
 
 using namespace mozilla;
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -13,26 +13,27 @@
 #define nsCSSRuleProcessor_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsCSSStyleSheet.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
-#include "nsCSSRules.h"
 #include "nsRuleWalker.h"
 #include "nsEventStates.h"
 
 struct CascadeEnumData;
 struct nsCSSSelector;
 struct nsCSSSelectorList;
 struct RuleCascadeData;
 struct TreeMatchContext;
 class nsCSSKeyframesRule;
+class nsCSSPageRule;
+class nsCSSFontFeatureValuesRule;
 
 /**
  * The CSS style rule processor provides a mechanism for sibling style
  * sheets to combine their rule processing in order to allow proper
  * cascading to happen.
  *
  * CSS style rule processors keep a live reference on all style sheets
  * bound to them.  The CSS style sheets keep a weak reference to all the
--- a/layout/style/nsCSSRules.h
+++ b/layout/style/nsCSSRules.h
@@ -18,17 +18,16 @@
 #include "nsIDOMCSSFontFaceRule.h"
 #include "nsIDOMCSSFontFeatureValuesRule.h"
 #include "nsIDOMCSSGroupingRule.h"
 #include "nsIDOMCSSMediaRule.h"
 #include "nsIDOMCSSMozDocumentRule.h"
 #include "nsIDOMCSSSupportsRule.h"
 #include "nsIDOMMozCSSKeyframeRule.h"
 #include "nsIDOMMozCSSKeyframesRule.h"
-#include "nsIDOMCSSStyleDeclaration.h"
 #include "nsAutoPtr.h"
 #include "nsCSSProperty.h"
 #include "nsCSSValue.h"
 #include "nsIDOMCSSCharsetRule.h"
 #include "nsTArray.h"
 #include "nsDOMCSSDeclaration.h"
 #include "Declaration.h"
 #include "nsIDOMCSSPageRule.h"
--- a/layout/style/nsCSSScanner.cpp
+++ b/layout/style/nsCSSScanner.cpp
@@ -3,16 +3,17 @@
  * 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/. */
 
 
 /* tokenization of CSS style sheets */
 
 #include "nsCSSScanner.h"
 #include "nsStyleUtil.h"
+#include "nsTraceRefcnt.h"
 #include "mozilla/css/ErrorReporter.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Util.h"
 #include <algorithm>
 
 using mozilla::ArrayLength;
 
 /* Character class tables and related helper functions. */
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -13,16 +13,19 @@
 #include "nsIPrincipal.h"
 #include "nsCSSProps.h"
 #include "nsStyleUtil.h"
 #include "CSSCalc.h"
 #include "nsNetUtil.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/Likely.h"
+#include "gfxFontConstants.h"
+#include "nsPresContext.h"
+#include "imgRequestProxy.h"
 
 namespace css = mozilla::css;
 
 nsCSSValue::nsCSSValue(int32_t aValue, nsCSSUnit aUnit)
   : mUnit(aUnit)
 {
   NS_ABORT_IF_FALSE(aUnit == eCSSUnit_Integer || aUnit == eCSSUnit_Enumerated ||
                     aUnit == eCSSUnit_EnumColor, "not an int value");
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -24,18 +24,16 @@
 #include "nsTArray.h"
 #include "nsStyleConsts.h"
 
 class imgRequestProxy;
 class nsIDocument;
 class nsIPrincipal;
 class nsPresContext;
 class nsIURI;
-template <class T>
-class nsPtrHashKey;
 
 // Deletes a linked list iteratively to avoid blowing up the stack (bug 456196).
 #define NS_CSS_DELETE_LIST_MEMBER(type_, ptr_, member_)                        \
   {                                                                            \
     type_ *cur = (ptr_)->member_;                                              \
     (ptr_)->member_ = nullptr;                                                 \
     while (cur) {                                                              \
       type_ *dlm_next = cur->member_;                                          \
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -38,17 +38,16 @@
 #include "nsCSSKeywords.h"
 #include "nsStyleCoord.h"
 #include "nsDisplayList.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsStyleTransformMatrix.h"
 #include "mozilla/dom/Element.h"
 #include "prtime.h"
 #include "nsWrapperCacheInlines.h"
-#include "nsUTF8Utils.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #if defined(DEBUG_bzbarsky) || defined(DEBUG_caillon)
 #define DEBUG_ComputedDOMStyle
 #endif
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -10,31 +10,62 @@
 
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 #include "nscore.h"
 #include "nsCSSProperty.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsStyleContext.h"
-#include "nsStyleStruct.h"
 #include "nsIWeakReferenceUtils.h"
+#include "mozilla/gfx/Types.h"
+#include "nsCoord.h"
+#include "nsColor.h"
 #include "nsIContent.h"
 
 namespace mozilla {
 namespace dom {
 class Element;
 }
 }
 
 class nsIFrame;
 class nsIPresShell;
 class nsDOMCSSValueList;
+class nsMargin;
 class nsROCSSPrimitiveValue;
-class nsStyleContext;
+class nsStyleBackground;
+class nsStyleBorder;
+class nsStyleContent;
+class nsStyleColumn;
+class nsStyleColor;
+class nsStyleCoord;
+class nsStyleCorners;
+class nsStyleDisplay;
+class nsStyleFilter;
+class nsStyleFont;
+class nsStyleGradient;
+class nsStyleImage;
+class nsStyleList;
+class nsStyleMargin;
+class nsStyleOutline;
+class nsStylePadding;
+class nsStylePosition;
+class nsStyleQuotes;
+class nsStyleSides;
+class nsStyleSVG;
+class nsStyleSVGReset;
+class nsStyleTable;
+class nsStyleText;
+class nsStyleTextReset;
+class nsStyleTimingFunction;
+class nsStyleUIReset;
+class nsStyleVisibility;
+class nsStyleXUL;
+class nsTimingFunction;
 class gfx3DMatrix;
 
 class nsComputedDOMStyle MOZ_FINAL : public nsDOMCSSDeclaration
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsComputedDOMStyle,
                                                                    nsICSSDeclaration)
--- a/layout/style/nsDOMCSSAttrDeclaration.cpp
+++ b/layout/style/nsDOMCSSAttrDeclaration.cpp
@@ -9,17 +9,16 @@
 
 #include "mozilla/css/Declaration.h"
 #include "mozilla/css/StyleRule.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIURI.h"
 #include "nsNodeUtils.h"
-#include "xpcpublic.h"
 #include "nsWrapperCacheInlines.h"
 
 namespace css = mozilla::css;
 namespace dom = mozilla::dom;
 
 nsDOMCSSAttributeDeclaration::nsDOMCSSAttributeDeclaration(dom::Element* aElement,
                                                            bool aIsSMILOverride)
   : mElement(aElement)
--- a/layout/style/nsDOMCSSDeclaration.h
+++ b/layout/style/nsDOMCSSDeclaration.h
@@ -10,17 +10,16 @@
 
 #include "nsICSSDeclaration.h"
 
 #include "mozilla/Attributes.h"
 #include "nsCOMPtr.h"
 
 class nsIPrincipal;
 class nsIDocument;
-class nsIURI;
 
 namespace mozilla {
 namespace css {
 class Declaration;
 class Loader;
 class Rule;
 }
 }
--- a/layout/style/nsDOMCSSRect.h
+++ b/layout/style/nsDOMCSSRect.h
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* DOM object representing rectangle values in DOM computed style */
 
 #ifndef nsDOMCSSRect_h_
 #define nsDOMCSSRect_h_
 
 #include "mozilla/Attributes.h"
-#include "nsISupportsImpl.h"
 #include "nsIDOMRect.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 class nsROCSSPrimitiveValue;
 
 class nsDOMCSSRect : public nsIDOMRect,
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -22,17 +22,16 @@
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsIContentSecurityPolicy.h"
-#include "nsChannelPolicy.h"
 #include "nsIDocShell.h"
 #include "nsIWebNavigation.h"
 
 #include "nsIConsoleService.h"
 
 #include "nsStyleSet.h"
 #include "nsPrintfCString.h"
 
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -12,22 +12,20 @@
 #include "nsCOMPtr.h"
 #include "nsIStreamLoader.h"
 #include "nsIChannel.h"
 #include "gfxUserFontSet.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "nsCSSRules.h"
 
-class nsISupports;
 class nsPresContext;
 class nsIPrincipal;
 
 class nsFontFaceLoader;
-class nsCSSFontFaceRule;
 
 // nsUserFontSet - defines the loading mechanism for downloadable fonts
 class nsUserFontSet : public gfxUserFontSet
 {
 public:
   nsUserFontSet(nsPresContext *aContext);
   ~nsUserFontSet();
 
--- a/layout/style/nsHTMLCSSStyleSheet.cpp
+++ b/layout/style/nsHTMLCSSStyleSheet.cpp
@@ -7,18 +7,16 @@
  * style sheet and style rule processor representing style attributes
  */
 
 #include "nsHTMLCSSStyleSheet.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsPresContext.h"
-#include "nsIDocument.h"
-#include "nsCOMPtr.h"
 #include "nsRuleWalker.h"
 #include "nsRuleProcessorData.h"
 #include "mozilla/dom/Element.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 
 using namespace mozilla::dom;
 namespace css = mozilla::css;
--- a/layout/style/nsHTMLCSSStyleSheet.h
+++ b/layout/style/nsHTMLCSSStyleSheet.h
@@ -8,19 +8,17 @@
  */
 
 #ifndef nsHTMLCSSStyleSheet_h_
 #define nsHTMLCSSStyleSheet_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 
-#include "nsCOMPtr.h"
 #include "nsDataHashtable.h"
-#include "nsIStyleSheet.h"
 #include "nsIStyleRuleProcessor.h"
 
 struct MiscContainer;
 
 class nsHTMLCSSStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor
 {
 public:
   nsHTMLCSSStyleSheet();
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -27,17 +27,16 @@
 #include "nsStyleConsts.h"
 #include "nsRuleWalker.h"
 #include "nsRuleData.h"
 #include "nsError.h"
 #include "nsRuleProcessorData.h"
 #include "nsCSSRuleProcessor.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Element.h"
-#include "nsCSSFrameConstructor.h"
 #include "nsHashKeys.h"
 #include "RestyleManager.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS1(nsHTMLStyleSheet::HTMLColorRule, nsIStyleRule)
 
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -12,22 +12,22 @@
 #ifndef nsHTMLStyleSheet_h_
 #define nsHTMLStyleSheet_h_
 
 #include "nsAutoPtr.h"
 #include "nsColor.h"
 #include "nsCOMPtr.h"
 #include "nsIStyleRule.h"
 #include "nsIStyleRuleProcessor.h"
-#include "nsIStyleSheet.h"
 #include "pldhash.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsString.h"
 
+class nsIDocument;
 class nsMappedAttributes;
 
 class nsHTMLStyleSheet MOZ_FINAL : public nsIStyleRuleProcessor
 {
 public:
   nsHTMLStyleSheet(nsIDocument* aDocument);
 
   void SetOwningDocument(nsIDocument* aDocument);
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -6,17 +6,19 @@
 /* the features that media queries can test */
 
 #include "nsMediaFeatures.h"
 #include "nsGkAtoms.h"
 #include "nsCSSKeywords.h"
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
 #include "nsCSSValue.h"
+#ifdef XP_WIN
 #include "mozilla/LookAndFeel.h"
+#endif
 #include "nsCSSRuleProcessor.h"
 
 using namespace mozilla;
 
 static const int32_t kOrientationKeywords[] = {
   eCSSKeyword_portrait,                 NS_STYLE_ORIENTATION_PORTRAIT,
   eCSSKeyword_landscape,                NS_STYLE_ORIENTATION_LANDSCAPE,
   eCSSKeyword_UNKNOWN,                  -1
--- a/layout/style/nsRuleData.h
+++ b/layout/style/nsRuleData.h
@@ -7,16 +7,17 @@
  * temporary (expanded) representation of property-value pairs used to
  * hold data from matched rules during style data computation.
  */
 
 #ifndef nsRuleData_h_
 #define nsRuleData_h_
 
 #include "nsCSSProps.h"
+#include "nsCSSValue.h"
 #include "nsStyleStructFwd.h"
 
 class nsPresContext;
 class nsStyleContext;
 struct nsRuleData;
 
 typedef void (*nsPostResolveFunc)(void* aStyleStruct, nsRuleData* aData);
 
--- a/layout/style/nsStyleAnimation.h
+++ b/layout/style/nsStyleAnimation.h
@@ -3,17 +3,17 @@
  * 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/. */
 
 /* Utilities for animation of computed style values */
 
 #ifndef nsStyleAnimation_h_
 #define nsStyleAnimation_h_
 
-#include "nsAString.h"
+#include "nsStringFwd.h"
 #include "nsCRTGlue.h"
 #include "nsStringBuffer.h"
 #include "nsCSSProperty.h"
 #include "nsCoord.h"
 #include "nsColor.h"
 #include "nsCSSValue.h"
 
 class nsStyleContext;
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -28,16 +28,17 @@
 #include "nsTransitionManager.h"
 #include "nsAnimationManager.h"
 #include "nsEventStates.h"
 #include "nsStyleSheetService.h"
 #include "mozilla/dom/Element.h"
 #include "GeckoProfiler.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsHTMLStyleSheet.h"
+#include "nsCSSRules.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS1(nsEmptyStyleRule, nsIStyleRule)
 
 /* virtual */ void
 nsEmptyStyleRule::MapRuleInfoInto(nsRuleData* aRuleData)
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -21,17 +21,16 @@
 #include "nsRuleNode.h"
 #include "nsTArray.h"
 #include "nsCOMArray.h"
 #include "nsAutoPtr.h"
 #include "nsIStyleRule.h"
 #include "nsCSSPseudoElements.h"
 #include "gfxFontFeatures.h"
 
-class nsIURI;
 class nsCSSFontFaceRule;
 class nsCSSKeyframesRule;
 class nsCSSFontFeatureValuesRule;
 class nsCSSPageRule;
 class nsRuleWalker;
 struct ElementDependentRuleProcessorData;
 struct TreeMatchContext;
 
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -8,16 +8,17 @@
  */
 
 #include "nsStyleTransformMatrix.h"
 #include "nsCSSValue.h"
 #include "nsPresContext.h"
 #include "nsRuleNode.h"
 #include "nsCSSKeywords.h"
 #include "nsStyleAnimation.h"
+#include "gfxMatrix.h"
 
 namespace css = mozilla::css;
 
 namespace nsStyleTransformMatrix {
 
 /* Note on floating point precision: The transform matrix is an array
  * of single precision 'float's, and so are most of the input values
  * we get from the style system, but intermediate calculations
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -6,20 +6,18 @@
 /*
  * A class representing three matrices that can be used for style transforms.
  */
 
 #ifndef nsStyleTransformMatrix_h_
 #define nsStyleTransformMatrix_h_
 
 #include "nsCSSValue.h"
-#include "gfxMatrix.h"
 #include "gfx3DMatrix.h"
 
-struct nsCSSValueList;
 class nsStyleContext;
 class nsPresContext;
 struct nsRect;
 
 /**
  * A helper to generate gfxMatrixes from css transform functions.
  */
 namespace nsStyleTransformMatrix {
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -2,17 +2,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsStyleUtil.h"
 #include "nsStyleConsts.h"
 
 #include "nsIContent.h"
-#include "nsReadableUtils.h"
 #include "nsCSSProps.h"
 #include "nsRuleNode.h"
 #include "nsROCSSPrimitiveValue.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIURI.h"
 
 using namespace mozilla;
 
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -2,25 +2,26 @@
 /* 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/. */
 #ifndef nsStyleUtil_h___
 #define nsStyleUtil_h___
 
 #include "nsCoord.h"
 #include "nsCSSProperty.h"
-#include "gfxFontFeatures.h"
-#include "nsIPrincipal.h"
-#include "nsSubstring.h"
+#include "nsStringFwd.h"
 
 class nsCSSValue;
 class nsStringComparator;
 class nsStyleCoord;
 class nsIContent;
+class nsIPrincipal;
+class nsIURI;
 struct gfxFontFeature;
+struct gfxAlternateValue;
 class nsCSSValueList;
 template <class E> class nsTArray;
 
 // Style utility functions
 class nsStyleUtil {
 public:
 
  static bool DashMatchCompare(const nsAString& aAttributeValue,
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -10,27 +10,23 @@
 #include "nsAnimationManager.h"
 #include "nsIContent.h"
 #include "nsStyleContext.h"
 #include "nsCSSProps.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
-#include "nsIStyleRule.h"
 #include "nsRuleWalker.h"
-#include "nsRuleData.h"
-#include "gfxColor.h"
 #include "nsCSSPropertySet.h"
 #include "nsStyleAnimation.h"
 #include "nsEventDispatcher.h"
 #include "nsGUIEvent.h"
 #include "mozilla/dom/Element.h"
 #include "nsIFrame.h"
-#include "nsCSSFrameConstructor.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "nsStyleChangeList.h"
 #include "nsStyleSet.h"
 #include "RestyleManager.h"
 
 using mozilla::TimeStamp;
--- a/nsprpub/pr/src/md/windows/w95cv.c
+++ b/nsprpub/pr/src/md/windows/w95cv.c
@@ -13,16 +13,18 @@
  *  We use a deferred condition notify algorithm.  When PR_NotifyCondVar
  *  or PR_NotifyAllCondVar is called, the condition notifies are simply
  *  recorded in the _MDLock structure.  We defer the condition notifies
  *  until right after we unlock the lock.  This way the awakened threads
  *  have a better chance to reaquire the lock.
  */
  
 #include "primpl.h"
+#include <stdio.h>
+#include <windows.h>
 
 /*
  * AddThreadToCVWaitQueueInternal --
  *
  * Add the thread to the end of the condition variable's wait queue.
  * The CV's lock must be locked when this function is called.
  */
 
@@ -145,16 +147,20 @@ md_UnlockAndPostNotifies(
             thred = notified->cv[index].notifyHead;
             while (thred != NULL) {
                 BOOL rv;
 
                 next = thred->md.next;
                 thred->md.prev = thred->md.next = NULL;
 
                 rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+                if (!rv) {
+                    fprintf(stderr, "ReleaseSemaphore failed, handle: %x, last error: %x\n",
+                            (void*)thred->md.blocked_sema, GetLastError());
+                }
                 PR_ASSERT(rv != 0);
                 thred = next;
             }
         }
         prev = notified;
         notified = notified->link;
         if (&post != prev) PR_DELETE(prev);
     } while (NULL != notified);
@@ -241,16 +247,20 @@ void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock
         md_UnlockAndPostNotifies(lock, thred, cv);
     } else {
         AddThreadToCVWaitQueueInternal(thred, cv);
         LeaveCriticalSection(&lock->mutex);
     }
 
     /* Wait for notification or timeout; don't really care which */
     rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
+    if (rv == WAIT_FAILED) {
+        fprintf(stderr, "WaitForSingleObject failed, handle: %x, last error: %x\n",
+                (void*)thred->md.blocked_sema, GetLastError());
+    }
 
     EnterCriticalSection(&(lock->mutex));
 
     PR_ASSERT(rv != WAIT_ABANDONED);
     PR_ASSERT(rv != WAIT_FAILED);
     PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
 
     if (rv == WAIT_TIMEOUT) {
--- a/nsprpub/pr/src/md/windows/w95thred.c
+++ b/nsprpub/pr/src/md/windows/w95thred.c
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "primpl.h"
 #include <process.h>  /* for _beginthreadex() */
+#include <stdio.h>
+#include <windows.h>
 
 #if defined(_MSC_VER) && _MSC_VER <= 1200
 /*
  * VC++ 6.0 doesn't have DWORD_PTR.
  */
 
 typedef DWORD DWORD_PTR;
 #endif /* _MSC_VER <= 1200 */
@@ -212,16 +214,20 @@ void
     if (thread->md.blocked_sema) {
         rv = CloseHandle(thread->md.blocked_sema);
         PR_ASSERT(rv);
         thread->md.blocked_sema = 0;
     }
 
     if (thread->md.handle) {
         rv = CloseHandle(thread->md.handle);
+        if (!rv) {
+          fprintf(stderr, "CloseHandle failed, handle: %x, last error: %x\n",
+                  (void*)thread->md.handle, GetLastError());
+        }
         PR_ASSERT(rv);
         thread->md.handle = 0;
     }
 }
 
 void
 _PR_MD_EXIT_THREAD(PRThread *thread)
 {
--- a/testing/mochitest/androidx86.json
+++ b/testing/mochitest/androidx86.json
@@ -126,17 +126,21 @@
  "content/html/content/test/test_video_wakelock.html": "bug 871015",
  "content/html/document/test/test_bug199692.html": "bug 811644",
  "content/html/document/test/test_bug369370.html": "",
  "content/html/document/test/test_bug391777.html": "",
  "content/html/document/test/test_bug445004.html": "",
  "content/html/document/test/test_bug446483.html": "",
  "content/html/document/test/test_bug741266.html": "",
  "content/media/test/test_autoplay_contentEditable.html": "x86 only",
+ "content/media/test/test_playback_rate.html": "bug 845162",
+ "content/media/test/test_seek.html": "bug 845162",
+ "content/media/test/test_played.html": "bug 751539",
  "content/media/test/test_buffered.html": "",
+ "content/media/test/test_bug465498.html": "",
  "content/media/test/test_bug448534.html": "x86 only bug 914439",
  "content/media/test/test_bug495145.html": "x86 only bug 914439",
  "content/media/test/test_bug495300.html": "x86 only bug 914439",
  "content/media/test/test_bug686942.html": "x86 only bug 914439",
  "content/media/test/test_can_play_type_mpeg.html": "x86 only bug 914439",
  "content/media/test/test_audio2.html": "x86 only bug 914439",
  "content/media/test/test_load.html": "x86 only bug 914439",
  "content/media/test/test_load_candidates.html": "x86 only bug 914439",
--- a/toolkit/components/jsdownloads/src/DownloadList.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadList.jsm
@@ -8,27 +8,23 @@
  * This file includes the following constructors and global objects:
  *
  * DownloadList
  * Represents a collection of Download objects that can be viewed and managed by
  * the user interface, and persisted across sessions.
  *
  * DownloadCombinedList
  * Provides a unified, unordered list combining public and private downloads.
- *
- * DownloadSummary
- * Provides an aggregated view on the contents of a DownloadList.
  */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "DownloadList",
   "DownloadCombinedList",
-  "DownloadSummary",
 ];
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Globals
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
@@ -331,190 +327,8 @@ DownloadCombinedList.prototype = {
   {
     let index = this._downloads.indexOf(aDownload);
     if (index != -1) {
       this._downloads.splice(index, 1);
     }
     this._notifyAllViews("onDownloadRemoved", aDownload);
   },
 };
-
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadSummary
-
-/**
- * Provides an aggregated view on the contents of a DownloadList.
- */
-function DownloadSummary() {
-  this._downloads = [];
-  this._views = new Set();
-}
-
-DownloadSummary.prototype = {
-  /**
-   * Array of Download objects that are currently part of the summary.
-   */
-  _downloads: null,
-
-  /**
-   * Underlying DownloadList whose contents should be summarized.
-   */
-  _list: null,
-
-  /**
-   * This method may be called once to bind this object to a DownloadList.
-   *
-   * Views on the summarized data can be registered before this object is bound
-   * to an actual list.  This allows the summary to be used without requiring
-   * the initialization of the DownloadList first.
-   *
-   * @param aList
-   *        Underlying DownloadList whose contents should be summarized.
-   */
-  bindToList: function (aList)
-  {
-    if (this._list) {
-      throw new Error("bindToList may be called only once.");
-    }
-
-    aList.addView(this);
-
-    // Set the list reference only after addView has returned, so that we don't
-    // send a notification to our views for each download that is added.
-    this._list = aList;
-    this._onListChanged();
-  },
-
-  /**
-   * Set of currently registered views.
-   */
-  _views: null,
-
-  /**
-   * Adds a view that will be notified of changes to the summary.  The newly
-   * added view will receive an initial onSummaryChanged notification.
-   *
-   * @param aView
-   *        The view object to add.  The following methods may be defined:
-   *        {
-   *          onSummaryChanged: function () {
-   *            // Called after any property of the summary has changed.
-   *          },
-   *        }
-   */
-  addView: function (aView)
-  {
-    this._views.add(aView);
-
-    if ("onSummaryChanged" in aView) {
-      try {
-        aView.onSummaryChanged();
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    }
-  },
-
-  /**
-   * Removes a view that was previously added using addView.  The removed view
-   * will not receive any more notifications after this method returns.
-   *
-   * @param aView
-   *        The view object to remove.
-   */
-  removeView: function (aView)
-  {
-    this._views.delete(aView);
-  },
-
-  /**
-   * Indicates whether all the downloads are currently stopped.
-   */
-  allHaveStopped: true,
-
-  /**
-   * Indicates the total number of bytes to be transferred before completing all
-   * the downloads that are currently in progress.
-   *
-   * For downloads that do not have a known final size, the number of bytes
-   * currently transferred is reported as part of this property.
-   *
-   * This is zero if no downloads are currently in progress.
-   */
-  progressTotalBytes: 0,
-
-  /**
-   * Number of bytes currently transferred as part of all the downloads that are
-   * currently in progress.
-   *
-   * This is zero if no downloads are currently in progress.
-   */
-  progressCurrentBytes: 0,
-
-  /**
-   * This function is called when any change in the list of downloads occurs,
-   * and will recalculate the summary and notify the views in case the
-   * aggregated properties are different.
-   */
-  _onListChanged: function () {
-    let allHaveStopped = true;
-    let progressTotalBytes = 0;
-    let progressCurrentBytes = 0;
-
-    // Recalculate the aggregated state.  See the description of the individual
-    // properties for an explanation of the summarization logic.
-    for (let download of this._downloads) {
-      if (!download.stopped) {
-        allHaveStopped = false;
-        progressTotalBytes += download.hasProgress ? download.totalBytes
-                                                   : download.currentBytes;
-        progressCurrentBytes += download.currentBytes;
-      }
-    }
-
-    // Exit now if the properties did not change.
-    if (this.allHaveStopped == allHaveStopped &&
-        this.progressTotalBytes == progressTotalBytes &&
-        this.progressCurrentBytes == progressCurrentBytes) {
-      return;
-    }
-
-    this.allHaveStopped = allHaveStopped;
-    this.progressTotalBytes = progressTotalBytes;
-    this.progressCurrentBytes = progressCurrentBytes;
-
-    // Notify all the views that our properties changed.
-    for (let view of this._views) {
-      try {
-        if ("onSummaryChanged" in view) {
-          view.onSummaryChanged();
-        }
-      } catch (ex) {
-        Cu.reportError(ex);
-      }
-    }
-  },
-
-  //////////////////////////////////////////////////////////////////////////////
-  //// DownloadList view
-
-  onDownloadAdded: function (aDownload)
-  {
-    this._downloads.push(aDownload);
-    if (this._list) {
-      this._onListChanged();
-    }
-  },
-
-  onDownloadChanged: function (aDownload)
-  {
-    this._onListChanged();
-  },
-
-  onDownloadRemoved: function (aDownload)
-  {
-    let index = this._downloads.indexOf(aDownload);
-    if (index != -1) {
-      this._downloads.splice(index, 1);
-    }
-    this._onListChanged();
-  },
-};
--- a/toolkit/components/jsdownloads/src/Downloads.jsm
+++ b/toolkit/components/jsdownloads/src/Downloads.jsm
@@ -26,18 +26,16 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/DownloadCore.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadCombinedList",
                                   "resource://gre/modules/DownloadList.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
                                   "resource://gre/modules/DownloadIntegration.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadList",
                                   "resource://gre/modules/DownloadList.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "DownloadSummary",
-                                  "resource://gre/modules/DownloadList.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
                                   "resource://gre/modules/DownloadUIHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/commonjs/sdk/core/promise.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -160,96 +158,53 @@ this.Downloads = {
    *        the Downloads.PRIVATE list based on their properties.
    *
    * @return {Promise}
    * @resolves The requested DownloadList or DownloadCombinedList object.
    * @rejects JavaScript exception.
    */
   getList: function (aType)
   {
-    if (!this._promiseListsInitialized) {
-      this._promiseListsInitialized = Task.spawn(function () {
-        let publicList = new DownloadList();
-        let privateList = new DownloadList();
-        let combinedList = new DownloadCombinedList(publicList, privateList);
-
-        try {
-          yield DownloadIntegration.addListObservers(publicList, false);
-          yield DownloadIntegration.addListObservers(privateList, true);
-          yield DownloadIntegration.initializePublicDownloadList(publicList);
-        } catch (ex) {
-          Cu.reportError(ex);
-        }
-
-        let publicSummary = yield this.getSummary(Downloads.PUBLIC);
-        let privateSummary = yield this.getSummary(Downloads.PRIVATE);
-        let combinedSummary = yield this.getSummary(Downloads.ALL);
-        
-        publicSummary.bindToList(publicList);
-        privateSummary.bindToList(privateList);
-        combinedSummary.bindToList(combinedList);
-
-        this._lists[Downloads.PUBLIC] = publicList;
-        this._lists[Downloads.PRIVATE] = privateList;
-        this._lists[Downloads.ALL] = combinedList;
-      }.bind(this));
-    }
-
-    return this._promiseListsInitialized.then(() => this._lists[aType]);
-  },
-
-  /**
-   * Promise resolved when the initialization of the download lists has
-   * completed, or null if initialization has never been requested.
-   */
-  _promiseListsInitialized: null,
-
-  /**
-   * After initialization, this object is populated with one key for each type
-   * of download list that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
-   * or Downloads.ALL).  The values are the DownloadList objects.
-   */
-  _lists: {},
-
-  /**
-   * Retrieves the specified type of DownloadSummary object.  There is one
-   * download summary for each type, and this method always retrieves a
-   * reference to the same download summary when called with the same argument.
-   *
-   * Calling this function does not cause the list of public downloads to be
-   * reloaded from the previous session.  The summary will behave as if no
-   * downloads are present until the getList method is called.
-   *
-   * @param aType
-   *        This can be Downloads.PUBLIC, Downloads.PRIVATE, or Downloads.ALL.
-   *
-   * @return {Promise}
-   * @resolves The requested DownloadList or DownloadCombinedList object.
-   * @rejects JavaScript exception.
-   */
-  getSummary: function (aType)
-  {
     if (aType != Downloads.PUBLIC && aType != Downloads.PRIVATE &&
         aType != Downloads.ALL) {
       throw new Error("Invalid aType argument.");
     }
 
-    if (!(aType in this._summaries)) {
-      this._summaries[aType] = new DownloadSummary();
+    if (!(aType in this._listPromises)) {
+      this._listPromises[aType] = Task.spawn(function () {
+        let list;
+        if (aType == Downloads.ALL) {
+          list = new DownloadCombinedList(
+                       (yield this.getList(Downloads.PUBLIC)),
+                       (yield this.getList(Downloads.PRIVATE)));
+        } else {
+          list = new DownloadList();
+          try {
+            yield DownloadIntegration.addListObservers(
+                                        list, aType == Downloads.PRIVATE);
+            if (aType == Downloads.PUBLIC) {
+              yield DownloadIntegration.initializePublicDownloadList(list);
+            }
+          } catch (ex) {
+            Cu.reportError(ex);
+          }
+        }
+        throw new Task.Result(list);
+      }.bind(this));
     }
 
-    return Promise.resolve(this._summaries[aType]);
+    return this._listPromises[aType];
   },
 
   /**
-   * This object is populated by the getSummary method with one key for each
-   * type of object that can be returned (Downloads.PUBLIC, Downloads.PRIVATE,
-   * or Downloads.ALL).  The values are the DownloadSummary objects.
+   * This object is populated by the getList method with one key for each type
+   * of object that can be returned (Downloads.PUBLIC, Downloads.PRIVATE, or
+   * Downloads.ALL).  The values are the promises returned by the method.
    */
-  _summaries: {},
+  _listPromises: {},
 
   /**
    * Returns the system downloads directory asynchronously.
    *   Mac OSX:
    *     User downloads directory
    *   XP/2K:
    *     My Documents/Downloads
    *   Vista and others:
--- a/toolkit/components/jsdownloads/test/unit/common_test_Download.js
+++ b/toolkit/components/jsdownloads/test/unit/common_test_Download.js
@@ -31,16 +31,46 @@ function promiseStartDownload(aSourceUrl
 
   return promiseNewDownload(aSourceUrl).then(download => {
     download.start();
     return download;
   });
 }
 
 /**
+ * Waits for a download to reach half of its progress, in case it has not
+ * reached the expected progress already.
+ *
+ * @param aDownload
+ *        The Download object to wait upon.
+ *
+ * @return {Promise}
+ * @resolves When the download has reached half of its progress.
+ * @rejects Never.
+ */
+function promiseDownloadMidway(aDownload) {
+  let deferred = Promise.defer();
+
+  // Wait for the download to reach half of its progress.
+  let onchange = function () {
+    if (!aDownload.stopped && !aDownload.canceled && aDownload.progress == 50) {
+      aDownload.onchange = null;
+      deferred.resolve();
+    }
+  };
+
+  // Register for the notification, but also call the function directly in
+  // case the download already reached the expected progress.
+  aDownload.onchange = onchange;
+  onchange();
+
+  return deferred.promise;
+}
+
+/**
  * Waits for a download to finish, in case it has not finished already.
  *
  * @param aDownload
  *        The Download object to wait upon.
  *
  * @return {Promise}
  * @resolves When the download has finished successfully.
  * @rejects JavaScript exception if the download failed.
--- a/toolkit/components/jsdownloads/test/unit/head.js
+++ b/toolkit/components/jsdownloads/test/unit/head.js
@@ -459,64 +459,40 @@ function promiseStartExternalHelperAppSe
       },
     }, null);
   }.bind(this)).then(null, do_report_unexpected_exception);
 
   return deferred.promise;
 }
 
 /**
- * Waits for a download to reach half of its progress, in case it has not
- * reached the expected progress already.
- *
- * @param aDownload
- *        The Download object to wait upon.
- *
- * @return {Promise}
- * @resolves When the download has reached half of its progress.
- * @rejects Never.
- */
-function promiseDownloadMidway(aDownload) {
-  let deferred = Promise.defer();
-
-  // Wait for the download to reach half of its progress.
-  let onchange = function () {
-    if (!aDownload.stopped && !aDownload.canceled && aDownload.progress == 50) {
-      aDownload.onchange = null;
-      deferred.resolve();
-    }
-  };
-
-  // Register for the notification, but also call the function directly in
-  // case the download already reached the expected progress.
-  aDownload.onchange = onchange;
-  onchange();
-
-  return deferred.promise;
-}
-
-/**
  * Returns a new public or private DownloadList object.
  *
  * @param aIsPrivate
  *        True for the private list, false or undefined for the public list.
  *
  * @return {Promise}
  * @resolves The newly created DownloadList object.
  * @rejects JavaScript exception.
  */
 function promiseNewList(aIsPrivate)
 {
-  // We need to clear all the internal state for the list and summary objects,
-  // since all the objects are interdependent internally.
-  Downloads._promiseListsInitialized = null;
-  Downloads._lists = {};
-  Downloads._summaries = {};
+  let type = aIsPrivate ? Downloads.PRIVATE : Downloads.PUBLIC;
+
+  // Force the creation of a new list.
+  if (type in Downloads._listPromises) {
+    delete Downloads._listPromises[type];
+  }
 
-  return Downloads.getList(aIsPrivate ? Downloads.PRIVATE : Downloads.PUBLIC);
+  // Invalidate the combined list, if any.
+  if (Downloads.ALL in Downloads._listPromises) {
+    delete Downloads._listPromises[Downloads.ALL];
+  }
+
+  return Downloads.getList(type);
 }
 
 /**
  * Ensures that the given file contents are equal to the given string.
  *
  * @param aPath
  *        String containing the path of the file whose contents should be
  *        verified.
--- a/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js
+++ b/toolkit/components/jsdownloads/test/unit/test_DownloadIntegration.js
@@ -273,17 +273,17 @@ add_task(function test_no_notifications(
  * and private active downloads at the same time.
  */
 add_task(function test_mix_notifications()
 {
   enableObserversTestMode();
   mustInterruptResponses();
 
   let publicList = yield promiseNewList();
-  let privateList = yield Downloads.getList(Downloads.PRIVATE);
+  let privateList = yield promiseNewList(true);
   let download1 = yield promiseNewDownload(httpUrl("interruptible.txt"));
   let download2 = yield promiseNewDownload(httpUrl("interruptible.txt"));
   let promiseAttempt1 = download1.start();
   let promiseAttempt2 = download2.start();
 
   // Add downloads to lists.
   publicList.add(download1);
   privateList.add(download2);
--- a/toolkit/components/jsdownloads/test/unit/test_DownloadList.js
+++ b/toolkit/components/jsdownloads/test/unit/test_DownloadList.js
@@ -132,17 +132,17 @@ add_task(function test_remove()
 /**
  * Tests that the "add", "remove", and "getAll" methods on the global
  * DownloadCombinedList object combine the contents of the global DownloadList
  * objects for public and private downloads.
  */
 add_task(function test_DownloadCombinedList_add_remove_getAll()
 {
   let publicList = yield promiseNewList();
-  let privateList = yield Downloads.getList(Downloads.PRIVATE);
+  let privateList = yield promiseNewList(true);
   let combinedList = yield Downloads.getList(Downloads.ALL);
 
   let publicDownload = yield promiseNewDownload();
   let privateDownload = yield Downloads.createDownload({
     source: { url: httpUrl("source.txt"), isPrivate: true },
     target: getTempFile(TEST_TARGET_FILE_NAME).path,
   });
 
@@ -443,125 +443,8 @@ add_task(function test_removeFinished()
   downloadFour.hasPartialData = true;
 
   list.removeFinished();
   yield deferred.promise;
 
   let downloads = yield list.getAll()
   do_check_eq(downloads.length, 1);
 });
-
-/**
- * Tests the global DownloadSummary objects for the public, private, and
- * combined download lists.
- */
-add_task(function test_DownloadSummary()
-{
-  mustInterruptResponses();
-
-  let publicList = yield promiseNewList();
-  let privateList = yield Downloads.getList(Downloads.PRIVATE);
-
-  let publicSummary = yield Downloads.getSummary(Downloads.PUBLIC);
-  let privateSummary = yield Downloads.getSummary(Downloads.PRIVATE);
-  let combinedSummary = yield Downloads.getSummary(Downloads.ALL);
-
-  // Add a public download that has succeeded.
-  let succeededPublicDownload = yield promiseNewDownload();
-  yield succeededPublicDownload.start();
-  publicList.add(succeededPublicDownload);
-
-  // Add a public download that has been canceled midway.
-  let canceledPublicDownload =
-      yield promiseNewDownload(httpUrl("interruptible.txt"));
-  canceledPublicDownload.start();
-  yield promiseDownloadMidway(canceledPublicDownload);
-  yield canceledPublicDownload.cancel();
-  publicList.add(canceledPublicDownload);
-
-  // Add a public download that is in progress.
-  let inProgressPublicDownload =
-      yield promiseNewDownload(httpUrl("interruptible.txt"));
-  inProgressPublicDownload.start();
-  yield promiseDownloadMidway(inProgressPublicDownload);
-  publicList.add(inProgressPublicDownload);
-
-  // Add a private download that is in progress.
-  let inProgressPrivateDownload = yield Downloads.createDownload({
-    source: { url: httpUrl("interruptible.txt"), isPrivate: true },
-    target: getTempFile(TEST_TARGET_FILE_NAME).path,
-  });
-  inProgressPrivateDownload.start();
-  yield promiseDownloadMidway(inProgressPrivateDownload);
-  privateList.add(inProgressPrivateDownload);
-
-  // Verify that the summary includes the total number of bytes and the
-  // currently transferred bytes only for the downloads that are not stopped.
-  // For simplicity, we assume that after a download is added to the list, its
-  // current state is immediately propagated to the summary object, which is
-  // true in the current implementation, though it is not guaranteed as all the
-  // download operations may happen asynchronously.
-  do_check_false(publicSummary.allHaveStopped);
-  do_check_eq(publicSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
-  do_check_eq(publicSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
-
-  do_check_false(privateSummary.allHaveStopped);
-  do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
-  do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
-
-  do_check_false(combinedSummary.allHaveStopped);
-  do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 4);
-  do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length * 2);
-
-  yield inProgressPublicDownload.cancel();
-
-  // Stopping the download should have excluded it from the summary.
-  do_check_true(publicSummary.allHaveStopped);
-  do_check_eq(publicSummary.progressTotalBytes, 0);
-  do_check_eq(publicSummary.progressCurrentBytes, 0);
-
-  do_check_false(privateSummary.allHaveStopped);
-  do_check_eq(privateSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
-  do_check_eq(privateSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
-
-  do_check_false(combinedSummary.allHaveStopped);
-  do_check_eq(combinedSummary.progressTotalBytes, TEST_DATA_SHORT.length * 2);
-  do_check_eq(combinedSummary.progressCurrentBytes, TEST_DATA_SHORT.length);
-
-  yield inProgressPrivateDownload.cancel();
-
-  // All the downloads should be stopped now.
-  do_check_true(publicSummary.allHaveStopped);
-  do_check_eq(publicSummary.progressTotalBytes, 0);
-  do_check_eq(publicSummary.progressCurrentBytes, 0);
-
-  do_check_true(privateSummary.allHaveStopped);
-  do_check_eq(privateSummary.progressTotalBytes, 0);
-  do_check_eq(privateSummary.progressCurrentBytes, 0);
-
-  do_check_true(combinedSummary.allHaveStopped);
-  do_check_eq(combinedSummary.progressTotalBytes, 0);
-  do_check_eq(combinedSummary.progressCurrentBytes, 0);
-});
-
-/**
- * Checks that views receive the summary change notification.  This is tested on
- * the combined summary when adding a public download, as we assume that if we
- * pass the test in this case we will also pass it in the others.
- */
-add_task(function test_DownloadSummary_notifications()
-{
-  let list = yield promiseNewList();
-  let summary = yield Downloads.getSummary(Downloads.ALL);
-
-  let download = yield promiseNewDownload();
-  list.add(download);
-
-  // Check that we receive change notifications.
-  let receivedOnSummaryChanged = false;
-  summary.addView({
-    onSummaryChanged: function () {
-      receivedOnSummaryChanged = true;
-    },
-  });
-  yield download.start();
-  do_check_true(receivedOnSummaryChanged);
-});
--- a/toolkit/components/jsdownloads/test/unit/test_Downloads.js
+++ b/toolkit/components/jsdownloads/test/unit/test_Downloads.js
@@ -114,36 +114,16 @@ add_task(function test_getList()
 
   do_check_eq(publicListOne, publicListTwo);
   do_check_eq(privateListOne, privateListTwo);
 
   do_check_neq(publicListOne, privateListOne);
 });
 
 /**
- * Tests that the getSummary function returns the same summary when called
- * multiple times with the same argument, but returns different summaries when
- * called with different arguments.  More detailed tests are implemented
- * separately for the DownloadSummary object in the DownloadList module.
- */
-add_task(function test_getSummary()
-{
-  let publicSummaryOne = yield Downloads.getSummary(Downloads.PUBLIC);
-  let privateSummaryOne = yield Downloads.getSummary(Downloads.PRIVATE);
-
-  let publicSummaryTwo = yield Downloads.getSummary(Downloads.PUBLIC);
-  let privateSummaryTwo = yield Downloads.getSummary(Downloads.PRIVATE);
-
-  do_check_eq(publicSummaryOne, publicSummaryTwo);
-  do_check_eq(privateSummaryOne, privateSummaryTwo);
-
-  do_check_neq(publicSummaryOne, privateSummaryOne);
-});
-
-/**
  * Tests that the getSystemDownloadsDirectory returns a valid nsFile
  * download directory object.
  */
 add_task(function test_getSystemDownloadsDirectory()
 {
   let downloadDir = yield Downloads.getSystemDownloadsDirectory();
   do_check_true(downloadDir instanceof Ci.nsIFile);
 });
--- a/toolkit/components/social/MozSocialAPI.jsm
+++ b/toolkit/components/social/MozSocialAPI.jsm
@@ -61,21 +61,16 @@ function injectController(doc, topic, da
                   Services.prefs.getBoolPref("social.debug.injectIntoTabs");
     } catch(e) {}
 
     let origin = containingBrowser.getAttribute("origin");
     if (!allowTabs && !origin) {
       return;
     }
 
-    // we always handle window.close on social content, even if they are not
-    // "enabled".  "enabled" is about the worker state and a provider may
-    // still be in e.g. the share panel without having their worker enabled.
-    handleWindowClose(window);
-
     SocialService.getProvider(doc.nodePrincipal.origin, function(provider) {
       if (provider && provider.enabled) {
         attachToWindow(provider, window);
       }
     });
   } catch(e) {
     Cu.reportError("MozSocialAPI injectController: unable to attachToWindow for " + doc.location + ": " + e);
   }
@@ -214,19 +209,17 @@ function attachToWindow(provider, target
   if (port) {
     targetWindow.addEventListener("unload", function () {
       // We want to close the port, but also want the target window to be
       // able to use the port during an unload event they setup - so we
       // set a timer which will fire after the unload events have all fired.
       schedule(function () { port.close(); });
     });
   }
-}
 
-function handleWindowClose(targetWindow) {
   // We allow window.close() to close the panel, so add an event handler for
   // this, then cancel the event (so the window itself doesn't die) and
   // close the panel instead.
   // However, this is typically affected by the dom.allow_scripts_to_close_windows
   // preference, but we can avoid that check by setting a flag on the window.
   let dwu = targetWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils);
   dwu.allowScriptsToClose();
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -35,22 +35,26 @@ const PREF_EM_CHECK_COMPATIBILITY_BASE =
 #ifdef MOZ_COMPATIBILITY_NIGHTLY
 var PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + ".nightly";
 #else
 var PREF_EM_CHECK_COMPATIBILITY;
 #endif
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
-const SHUTDOWN_EVENT                  = "profile-before-change";
-
 const VALID_TYPES_REGEXP = /^[\w\-]+$/;
 
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/AsyncShutdown.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
+                                  "resource://gre/modules/AddonRepository.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "CertUtils", function certUtilsLazyGetter() {
   let certUtils = {};
   Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
   return certUtils;
 });
 
 
@@ -448,18 +452,16 @@ var AddonManagerInternal = {
    * them.
    */
   startup: function AMI_startup() {
     if (gStarted)
       return;
 
     this.recordTimestamp("AMI_startup_begin");
 
-    Services.obs.addObserver(this, SHUTDOWN_EVENT, false);
-
     let appChanged = undefined;
 
     let oldAppVersion = null;
     try {
       oldAppVersion = Services.prefs.getCharPref(PREF_EM_LAST_APP_VERSION);
       appChanged = Services.appinfo.version != oldAppVersion;
     }
     catch (e) { }
@@ -542,16 +544,20 @@ var AddonManagerInternal = {
         Components.utils.import(url, {});
       }
       catch (e) {
         ERROR("Exception loading provider " + entry + " from category \"" +
               url + "\"", e);
       }
     }
 
+    // Register our shutdown handler with the AsyncShutdown manager
+    AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down providers",
+                                                 this.shutdown.bind(this));
+
     // Once we start calling providers we must allow all normal methods to work.
     gStarted = true;
 
     this.callProviders("startup", appChanged, oldAppVersion,
                        oldPlatformVersion);
 
     // If this is a new profile just pretend that there were no changes
     if (appChanged === undefined) {
@@ -674,55 +680,108 @@ var AddonManagerInternal = {
       }
       catch (e) {
         ERROR("Exception calling provider " + aMethod, e);
       }
     }
   },
 
   /**
+   * Calls a method on all registered providers, if the provider implements
+   * the method. The called method is expected to return a promise, and
+   * callProvidersAsync returns a promise that resolves when every provider
+   * method has either resolved or rejected. Rejection reasons are logged
+   * but otherwise ignored. Return values are ignored. Any parameters after the
+   * method parameter are passed to the provider's method.
+   *
+   * @param  aMethod
+   *         The method name to call
+   * @see    callProvider
+   */
+  callProvidersAsync: function AMI_callProviders(aMethod, ...aArgs) {
+    if (!aMethod || typeof aMethod != "string")
+      throw Components.Exception("aMethod must be a non-empty string",
+                                 Cr.NS_ERROR_INVALID_ARG);
+
+    let allProviders = [];
+
+    let providers = this.providers.slice(0);
+    for (let provider of providers) {
+      try {
+        if (aMethod in provider) {
+          // Resolve a new promise with the result of the method, to handle both
+          // methods that return values (or nothing) and methods that return promises.
+          let providerResult = provider[aMethod].apply(provider, aArgs);
+          let nextPromise = Promise.resolve(providerResult);
+          // Log and swallow the errors from methods that do return promises.
+          nextPromise = nextPromise.then(
+              null,
+              e => ERROR("Exception calling provider " + aMethod, e));
+          allProviders.push(nextPromise);
+        }
+      }
+      catch (e) {
+        ERROR("Exception calling provider " + aMethod, e);
+      }
+    }
+    // Because we use promise.then to catch and log all errors above, Promise.all()
+    // will never exit early because of a rejection.
+    return Promise.all(allProviders);
+  },
+
+  /**
    * Shuts down the addon manager and all registered providers, this must clean
    * up everything in order for automated tests to fake restarts.
+   * @return Promise{null} that resolves when all providers and dependent modules
+   *                       have finished shutting down
    */
   shutdown: function AMI_shutdown() {
     LOG("shutdown");
-    Services.obs.removeObserver(this, SHUTDOWN_EVENT);
+    // Clean up listeners
     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
     Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
     Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
     Services.prefs.removeObserver(PREF_EM_HOTFIX_ID, this);
 
-    // Always clean up listeners, but only shutdown providers if they've been 
-    // started.
-    if (gStarted)
-      this.callProviders("shutdown");
+    // Only shut down providers if they've been started. Shut down
+    // AddonRepository after providers (if any).
+    let shuttingDown = null;
+    if (gStarted) {
+      shuttingDown = this.callProvidersAsync("shutdown")
+        .then(null,
+              err => ERROR("Failure during async provider shutdown", err))
+        .then(() => AddonRepository.shutdown());
+    }
+    else {
+      shuttingDown = AddonRepository.shutdown();
+    }
 
-    this.managerListeners.splice(0, this.managerListeners.length);
-    this.installListeners.splice(0, this.installListeners.length);
-    this.addonListeners.splice(0, this.addonListeners.length);
-    this.typeListeners.splice(0, this.typeListeners.length);
-    for (let type in this.startupChanges)
-      delete this.startupChanges[type];
-    gStarted = false;
-    gStartupComplete = false;
+    shuttingDown.then(val => LOG("Async provider shutdown done"),
+                      err => ERROR("Failure during AddonRepository shutdown", err))
+      .then(() => {
+        this.managerListeners.splice(0, this.managerListeners.length);
+        this.installListeners.splice(0, this.installListeners.length);
+        this.addonListeners.splice(0, this.addonListeners.length);
+        this.typeListeners.splice(0, this.typeListeners.length);
+        for (let type in this.startupChanges)
+          delete this.startupChanges[type];
+        gStarted = false;
+        gStartupComplete = false;
+      });
+    return shuttingDown;
   },
 
   /**
    * Notified when a preference we're interested in has changed.
    *
    * @see nsIObserver
    */
   observe: function AMI_observe(aSubject, aTopic, aData) {
-    if (aTopic == SHUTDOWN_EVENT) {
-      this.shutdown();
-      return;
-    }
-
     switch (aData) {
       case PREF_EM_CHECK_COMPATIBILITY: {
         let oldValue = gCheckCompatibility;
         try {
           gCheckCompatibility = Services.prefs.getBoolPref(PREF_EM_CHECK_COMPATIBILITY);
         } catch(e) {
           gCheckCompatibility = true;
         }
@@ -905,28 +964,27 @@ var AddonManagerInternal = {
         Services.obs.notifyObservers(null,
                                      "addons-background-update-complete",
                                      null);
       }
     }
 
     if (this.updateEnabled) {
       let scope = {};
-      Components.utils.import("resource://gre/modules/AddonRepository.jsm", scope);
       Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
       scope.LightweightThemeManager.updateCurrentTheme();
 
       pendingUpdates++;
       this.getAllAddons(function getAddonsCallback(aAddons) {
         // If there is a known hotfix then exclude it from the list of add-ons to update.
         var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
 
         // Repopulate repository cache first, to ensure compatibility overrides
         // are up to date before checking for addon updates.
-        scope.AddonRepository.backgroundUpdateCheck(
+        AddonRepository.backgroundUpdateCheck(
                      ids, function BUC_backgroundUpdateCheckCallback() {
           AddonManagerInternal.updateAddonRepositoryData(
                                     function BUC_updateAddonCallback() {
 
             pendingUpdates += aAddons.length;
             aAddons.forEach(function BUC_forEachCallback(aAddon) {
               if (aAddon.id == hotfixID) {
                 notifyComplete();
@@ -1139,17 +1197,17 @@ var AddonManagerInternal = {
    * the extraListeners parameter are passed to the listener.
    *
    * @param  aMethod
    *         The method on the listeners to call
    * @param  aExtraListeners
    *         An optional array of extra InstallListeners to also call
    * @return false if any of the listeners returned false, true otherwise
    */
-  callInstallListeners: function AMI_callInstallListeners(aMethod, 
+  callInstallListeners: function AMI_callInstallListeners(aMethod,
                                  aExtraListeners, ...aArgs) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!aMethod || typeof aMethod != "string")
       throw Components.Exception("aMethod must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
@@ -1243,17 +1301,17 @@ var AddonManagerInternal = {
    */
   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     this.callProviders("updateAddonAppDisabledStates");
   },
-  
+
   /**
    * Notifies all providers that the repository has updated its data for
    * installed add-ons.
    *
    * @param  aCallback
    *         Function to call when operation is complete.
    */
   updateAddonRepositoryData: function AMI_updateAddonRepositoryData(aCallback) {
@@ -1627,17 +1685,17 @@ var AddonManagerInternal = {
    *
    * @param  aListener
    *         The InstallListener to add
    */
   addInstallListener: function AMI_addInstallListener(aListener) {
     if (!aListener || typeof aListener != "object")
       throw Components.Exception("aListener must be a InstallListener object",
                                  Cr.NS_ERROR_INVALID_ARG);
-    
+
     if (!this.installListeners.some(function addInstallListener_matchListener(i) {
       return i == aListener; }))
       this.installListeners.push(aListener);
   },
 
   /**
    * Removes an InstallListener if the listener is registered.
    *
@@ -1758,17 +1816,17 @@ var AddonManagerInternal = {
     if (typeof aCallback != "function")
       throw Components.Exception("aCallback must be a function",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     let addons = [];
 
     new AsyncObjectCaller(aIDs, null, {
       nextObject: function getAddonsByIDs_nextObject(aCaller, aID) {
-        AddonManagerInternal.getAddonByID(aID, 
+        AddonManagerInternal.getAddonByID(aID,
                              function getAddonsByIDs_getAddonByID(aAddon) {
           addons.push(aAddon);
           aCaller.callNext();
         });
       },
 
       noMoreObjects: function getAddonsByIDs_noMoreObjects(aCaller) {
         safeCall(aCallback, addons);
@@ -2439,17 +2497,17 @@ this.AddonManager = {
    * @param  aAddon
    *         The Addon representing the add-on
    * @return true if the addon should auto-update, false otherwise.
    */
   shouldAutoUpdate: function AM_shouldAutoUpdate(aAddon) {
     if (!aAddon || typeof aAddon != "object")
       throw Components.Exception("aAddon must be specified",
                                  Cr.NS_ERROR_INVALID_ARG);
-    
+
     if (!("applyBackgroundUpdates" in aAddon))
       return false;
     if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE)
       return true;
     if (aAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE)
       return false;
     return this.autoUpdateDefault;
   },
--- a/toolkit/mozapps/extensions/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/AddonRepository.jsm
@@ -18,16 +18,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DeferredSave",
                                   "resource://gre/modules/DeferredSave.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository_SQLiteMigrator",
                                   "resource://gre/modules/AddonRepository_SQLiteMigrator.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/Promise.jsm");
 
 this.EXPORTED_SYMBOLS = [ "AddonRepository" ];
 
 const PREF_GETADDONS_CACHE_ENABLED       = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_TYPES         = "extensions.getAddons.cache.types";
 const PREF_GETADDONS_CACHE_ID_ENABLED    = "extensions.%ID%.getAddons.cache.enabled"
 const PREF_GETADDONS_BROWSEADDONS        = "extensions.getAddons.browseAddons";
 const PREF_GETADDONS_BYIDS               = "extensions.getAddons.get.url";
@@ -521,43 +523,26 @@ this.AddonRepository = {
    * searchFailed - Called when an error occurred when performing a search.
    */
   _callback: null,
 
   // Maximum number of results to return
   _maxResults: null,
   
   /**
-   * Initialize AddonRepository.
-   */
-  initialize: function AddonRepo_initialize() {
-    Services.obs.addObserver(this, "xpcom-shutdown", false);
-  },
-
-  /**
-   * Observe xpcom-shutdown notification, so we can shutdown cleanly.
-   */
-  observe: function AddonRepo_observe(aSubject, aTopic, aData) {
-    if (aTopic == "xpcom-shutdown") {
-      Services.obs.removeObserver(this, "xpcom-shutdown");
-      this.shutdown();
-    }
-  },
-
-  /**
    * Shut down AddonRepository
+   * return: promise{integer} resolves with the result of flushing
+   *         the AddonRepository database
    */
   shutdown: function AddonRepo_shutdown() {
     this.cancelSearch();
 
     this._addons = null;
     this._pendingCallbacks = null;
-    AddonDatabase.shutdown(function shutdown_databaseShutdown() {
-      Services.obs.notifyObservers(null, "addon-repository-shutdown", null);
-    });
+    return AddonDatabase.shutdown(false);
   },
 
   /**
    * Asynchronously get a cached add-on by id. The add-on (or null if the
    * add-on is not found) is passed to the specified callback. If caching is
    * disabled, null is passed to the specified callback.
    *
    * @param  aId
@@ -1509,17 +1494,16 @@ this.AddonRepository = {
           Services.vc.compare(appVersion, override.appMaxVersion) <= 0) {
         return override;
       }
     }
     return null;
   }
 
 };
-AddonRepository.initialize();
 
 var AddonDatabase = {
   // true if the database connection has been opened
   initialized: false,
   // false if there was an unrecoverable error openning the database
   databaseOk: true,
 
   // the in-memory database
@@ -1639,55 +1623,54 @@ var AddonDatabase = {
    * cached objects
    *
    * @param  aCallback
    *         An optional callback to call once complete
    * @param  aSkipFlush
    *         An optional boolean to skip flushing data to disk. Useful
    *         when the database is going to be deleted afterwards.
    */
-  shutdown: function AD_shutdown(aCallback, aSkipFlush) {
+  shutdown: function AD_shutdown(aSkipFlush) {
     this.databaseOk = true;
-    aCallback = aCallback || function() {};
 
     if (!this.initialized) {
-      aCallback();
-      return;
+      return Promise.resolve(0);
     }
 
     this.initialized = false;
 
     this.__defineGetter__("connection", function shutdown_connectionGetter() {
       return this.openConnection();
     });
 
     if (aSkipFlush) {
-      aCallback();
+      return Promise.resolve(0);
     } else {
-      this.Writer.flush().then(aCallback, aCallback);
+      return this.Writer.flush();
     }
   },
 
   /**
    * Asynchronously deletes the database, shutting down the connection
    * first if initialized
    *
    * @param  aCallback
    *         An optional callback to call once complete
    */
   delete: function AD_delete(aCallback) {
     this.DB = BLANK_DB();
 
-    this.Writer.flush().then(null, () => {}).then(() => {
-      this.shutdown(() => {
-        let promise = OS.File.remove(this.jsonFile.path, {});
-        if (aCallback)
-          promise.then(aCallback, aCallback);
-      }, true);
-    });
+    this.Writer.flush()
+      .then(null, () => {})
+      // shutdown(true) never rejects
+      .then(() => this.shutdown(true))
+      .then(() => OS.File.remove(this.jsonFile.path, {}))
+      .then(null, error => ERROR("Unable to delete Addon Repository file " +
+                                 this.jsonFile.path, error))
+      .then(aCallback);
   },
 
   toJSON: function AD_toJSON() {
     let json = {
       schema: this.DB.schema,
       addons: []
     }
 
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -1680,16 +1680,18 @@ var XPIProvider = {
    *         if it is a new profile or the version is unknown
    */
   startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
     LOG("startup");
     this.runPhase = XPI_STARTING;
     this.installs = [];
     this.installLocations = [];
     this.installLocationsByName = {};
+    // Hook for tests to detect when saving database at shutdown time fails
+    this._shutdownError = null;
 
     AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
 
     function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
       try {
         var dir = FileUtils.getDir(aKey, aPaths);
       }
       catch (e) {
@@ -1868,16 +1870,19 @@ var XPIProvider = {
     AddonManagerPrivate.recordTimestamp("XPI_startup_end");
 
     this.extensionsActive = true;
     this.runPhase = XPI_BEFORE_UI_STARTUP;
   },
 
   /**
    * Shuts down the database and releases all references.
+   * Return: Promise{integer} resolves / rejects with the result of
+   *                          flushing the XPI Database if it was loaded,
+   *                          0 otherwise.
    */
   shutdown: function XPI_shutdown() {
     LOG("shutdown");
 
     this.bootstrappedAddons = {};
     this.bootstrapScopes = {};
     this.enabledAddons = null;
     this.allAppGlobal = true;
@@ -1898,20 +1903,29 @@ var XPIProvider = {
 
     // This is needed to allow xpcshell tests to simulate a restart
     this.extensionsActive = false;
 
     // Remove URI mappings again
     delete this._uriMappings;
 
     if (gLazyObjectsLoaded) {
-      XPIDatabase.shutdown(function shutdownCallback(saveError) {
-        LOG("Notifying XPI shutdown observers");
-        Services.obs.notifyObservers(null, "xpi-provider-shutdown", saveError);
-      });
+      let done = XPIDatabase.shutdown();
+      done.then(
+        ret => {
+          LOG("Notifying XPI shutdown observers");
+          Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
+        },
+        err => {
+          LOG("Notifying XPI shutdown observers");
+          this._shutdownError = err;
+          Services.obs.notifyObservers(null, "xpi-provider-shutdown", err);
+        }
+      );
+      return done;
     }
     else {
       LOG("Notifying XPI shutdown observers");
       Services.obs.notifyObservers(null, "xpi-provider-shutdown", null);
     }
   },
 
   /**
@@ -3185,16 +3199,19 @@ var XPIProvider = {
     if (updated) {
       updateReasons.push("pendingFileChanges");
     }
 
     // This will be true if the previous session made changes that affect the
     // active state of add-ons but didn't commit them properly (normally due
     // to the application crashing)
     let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS);
+    if (hasPendingChanges) {
+      updateReasons.push("hasPendingChanges");
+    }
 
     // If the schema appears to have changed then we should update the database
     if (DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
       updateReasons.push("schemaChanged");
     }
 
     // If the application has changed then check for new distribution add-ons
     if (aAppChanged !== false &&
@@ -3244,19 +3261,16 @@ var XPIProvider = {
       }
     }
 
     // Catch and log any errors during the main startup
     try {
       let extensionListChanged = false;
       // If the database needs to be updated then open it and then update it
       // from the filesystem
-      if (hasPendingChanges) {
-        updateReasons.push("hasPendingChanges");
-      }
       if (updateReasons.length > 0) {
         AddonManagerPrivate.recordSimpleMeasure("XPIDB_startup_load_reasons", updateReasons);
         XPIDatabase.syncLoadDB(false);
         try {
           extensionListChanged = this.processFileChanges(state, manifests,
                                                          aAppChanged,
                                                          aOldAppVersion,
                                                          aOldPlatformVersion);
--- a/toolkit/mozapps/extensions/XPIProviderUtils.js
+++ b/toolkit/mozapps/extensions/XPIProviderUtils.js
@@ -973,18 +973,21 @@ this.XPIDatabase = {
         stmt.finalize();
     }
 
     return migrateData;
   },
 
   /**
    * Shuts down the database connection and releases all cached objects.
+   * Return: Promise{integer} resolves / rejects with the result of the DB
+   *                          flush after the database is flushed and
+   *                          all cleanup is done
    */
-  shutdown: function XPIDB_shutdown(aCallback) {
+  shutdown: function XPIDB_shutdown() {
     LOG("shutdown");
     if (this.initialized) {
       // If our last database I/O had an error, try one last time to save.
       if (this.lastError)
         this.saveChanges();
 
       this.initialized = false;
 
@@ -992,47 +995,38 @@ this.XPIDatabase = {
         AddonManagerPrivate.recordSimpleMeasure(
             "XPIDB_saves_total", this._deferredSave.totalSaves);
         AddonManagerPrivate.recordSimpleMeasure(
             "XPIDB_saves_overlapped", this._deferredSave.overlappedSaves);
         AddonManagerPrivate.recordSimpleMeasure(
             "XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
       }
 
-      // Make sure any pending writes of the DB are complete, and we
-      // finish cleaning up, and then call back
-      this.flush()
-        .then(null, error => {
+      // Return a promise that any pending writes of the DB are complete and we
+      // are finished cleaning up
+      let flushPromise = this.flush();
+      flushPromise.then(null, error => {
           ERROR("Flush of XPI database failed", error);
           AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
-          return 0;
-        })
-        .then(count => {
           // If our last attempt to read or write the DB failed, force a new
           // extensions.ini to be written to disk on the next startup
-          let lastSaveFailed = this.lastError;
-          if (lastSaveFailed)
-            Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
-
+          Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
+        })
+        .then(count => {
           // Clear out the cached addons data loaded from JSON
           delete this.addonDB;
           delete this._dbPromise;
           // same for the deferred save
           delete this._deferredSave;
           // re-enable the schema version setter
           delete this._schemaVersionSet;
-
-          if (aCallback)
-            aCallback(lastSaveFailed);
         });
+      return flushPromise;
     }
-    else {
-      if (aCallback)
-        aCallback(null);
-    }
+    return Promise.resolve(0);
   },
 
   /**
    * Return a list of all install locations known about by the database. This
    * is often a a subset of the total install locations when not all have
    * installed add-ons, occasionally a superset when an install location no
    * longer exists. Only called from XPIProvider.processFileChanges, when
    * the database should already be loaded.
@@ -1377,16 +1371,23 @@ this.XPIDatabase = {
     aAddon.active = aActive;
     this.saveChanges();
   },
 
   /**
    * Synchronously calculates and updates all the active flags in the database.
    */
   updateActiveAddons: function XPIDB_updateActiveAddons() {
+    if (!this.addonDB) {
+      WARN("updateActiveAddons called when DB isn't loaded");
+      // force the DB to load
+      AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_updateActive",
+          XPIProvider.runPhase);
+      this.syncLoadDB(true);
+    }
     LOG("Updating add-on states");
     for (let [, addon] of this.addonDB) {
       let newActive = (addon.visible && !addon.userDisabled &&
                       !addon.softDisabled && !addon.appDisabled &&
                       !addon.pendingUninstall);
       if (newActive != addon.active) {
         addon.active = newActive;
         this.saveChanges();
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -395,56 +395,46 @@ function restartManager(aNewVersion) {
     startupManager(false);
   }
 }
 
 function shutdownManager() {
   if (!gInternalManager)
     return;
 
-  let xpiShutdown = false;
-  Services.obs.addObserver({
-    observe: function(aSubject, aTopic, aData) {
-      xpiShutdown = true;
-      gXPISaveError = aData;
-      Services.obs.removeObserver(this, "xpi-provider-shutdown");
-    }
-  }, "xpi-provider-shutdown", false);
-
-  let repositoryShutdown = false;
-  Services.obs.addObserver({
-    observe: function(aSubject, aTopic, aData) {
-      repositoryShutdown = true;
-      Services.obs.removeObserver(this, "addon-repository-shutdown");
-    }
-  }, "addon-repository-shutdown", false);
+  let shutdownDone = false;
 
   Services.obs.notifyObservers(null, "quit-application-granted", null);
   let scope = Components.utils.import("resource://gre/modules/AddonManager.jsm");
-  scope.AddonManagerInternal.shutdown();
+  scope.AddonManagerInternal.shutdown()
+    .then(
+        () => shutdownDone = true,
+        err => shutdownDone = true);
+
   gInternalManager = null;
 
-  AddonRepository.shutdown();
-
   // Load the add-ons list as it was after application shutdown
   loadAddonsList();
 
   // Clear any crash report annotations
   gAppInfo.annotations = {};
 
   let thr = Services.tm.mainThread;
 
   // Wait until we observe the shutdown notifications
-  while (!repositoryShutdown || !xpiShutdown) {
+  while (!shutdownDone) {
     thr.processNextEvent(true);
   }
 
   // Force the XPIProvider provider to reload to better
   // simulate real-world usage.
   scope = Components.utils.import("resource://gre/modules/XPIProvider.jsm");
+  // This would be cleaner if I could get it as the rejection reason from
+  // the AddonManagerInternal.shutdown() promise
+  gXPISaveError = scope.XPIProvider._shutdownError;
   AddonManagerPrivate.unregisterProvider(scope.XPIProvider);
   Components.utils.unload("resource://gre/modules/XPIProvider.jsm");
 }
 
 function loadAddonsList() {
   function readDirectories(aSection) {
     var dirs = [];
     var keys = parser.getKeys(aSection);
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrateAddonRepository.js
@@ -73,56 +73,55 @@ function run_test() {
   stmt.params.thumbnailURL = "http://localhost/thumbnail1-1.png";
   stmt.params.caption = "Caption 1 - 1";
   stmt.execute();
   stmt.finalize();
 
   db.schemaVersion = 1;
   db.close();
 
-  Services.obs.addObserver({
-    observe: function () {
-      Services.obs.removeObserver(this, "addon-repository-shutdown");
-      // Check the DB schema has changed once AddonRepository has freed it.
-      db = AM_Cc["@mozilla.org/storage/service;1"].
-           getService(AM_Ci.mozIStorageService).
-           openDatabase(dbfile);
-      do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
-      do_check_true(db.indexExists("developer_idx"));
-      do_check_true(db.indexExists("screenshot_idx"));
-      do_check_true(db.indexExists("compatibility_override_idx"));
-      do_check_true(db.tableExists("compatibility_override"));
-      do_check_true(db.indexExists("icon_idx"));
-      do_check_true(db.tableExists("icon"));
-
-      // Check the trigger is working
-      db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
-      let internalID = db.lastInsertRowID;
-      db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
-
-      let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
-      stmt.executeStep();
-      do_check_eq(stmt.row.count, 1);
-      stmt.reset();
-
-      db.executeSimpleSQL("DELETE FROM addon");
-      stmt.executeStep();
-      do_check_eq(stmt.row.count, 0);
-      stmt.finalize();
-
-      db.close();
-      do_test_finished();
-    }
-  }, "addon-repository-shutdown", null);
 
   Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
   AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) {
     do_check_neq(aAddon, null);
     do_check_eq(aAddon.screenshots.length, 1);
     do_check_true(aAddon.screenshots[0].width === null);
     do_check_true(aAddon.screenshots[0].height === null);
     do_check_true(aAddon.screenshots[0].thumbnailWidth === null);
     do_check_true(aAddon.screenshots[0].thumbnailHeight === null);
     do_check_eq(aAddon.iconURL, undefined);
     do_check_eq(JSON.stringify(aAddon.icons), "{}");
-    AddonRepository.shutdown();
+    AddonRepository.shutdown().then(
+      function checkAfterRepoShutdown() {
+        // Check the DB schema has changed once AddonRepository has freed it.
+        db = AM_Cc["@mozilla.org/storage/service;1"].
+             getService(AM_Ci.mozIStorageService).
+             openDatabase(dbfile);
+        do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
+        do_check_true(db.indexExists("developer_idx"));
+        do_check_true(db.indexExists("screenshot_idx"));
+        do_check_true(db.indexExists("compatibility_override_idx"));
+        do_check_true(db.tableExists("compatibility_override"));
+        do_check_true(db.indexExists("icon_idx"));
+        do_check_true(db.tableExists("icon"));
+
+        // Check the trigger is working
+        db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
+        let internalID = db.lastInsertRowID;
+        db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
+
+        let stmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
+        stmt.executeStep();
+        do_check_eq(stmt.row.count, 1);
+        stmt.reset();
+
+        db.executeSimpleSQL("DELETE FROM addon");
+        stmt.executeStep();
+        do_check_eq(stmt.row.count, 0);
+        stmt.finalize();
+
+        db.close();
+        do_test_finished();
+      },
+      do_report_unexpected_exception
+    );
   });
 }