Merge mozilla-central to beta. a=merge, l10n=me on a CLOSED TREE
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 20 Sep 2017 20:39:12 -0400
changeset 431556 efff4f307675a2226d4ef5867d64b6499bde39e3
parent 431382 4bfb0b8fb6b1da31cb6c22b243bb071b6db01984 (current diff)
parent 431555 f8dd3f21e434be32fe5901849f1723b0e64cf668 (diff)
child 431719 06d4034a8a035cfefeec3b96164d5c66198666cd
push id7784
push userryanvm@gmail.com
push dateThu, 21 Sep 2017 00:40:13 +0000
treeherdermozilla-beta@efff4f307675 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.0
Merge mozilla-central to beta. a=merge, l10n=me on a CLOSED TREE
browser/base/content/defaultthemes/compactdark.icon.svg
browser/base/content/defaultthemes/compactlight.icon.svg
browser/components/places/tests/chrome/test_RecentBookmarksMenuUI.xul
browser/extensions/formautofill/test/mochitest/test_basic_creditcard_autocomplete_form.html
browser/themes/shared/aboutRights.css
taskcluster/taskgraph/transforms/toolchain.py
testing/talos/talos/tests/perf-reftest/bloom_basic.manifest
testing/talos/talos/tests/perf-reftest/bloom_basic_ref.manifest
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1497,17 +1497,17 @@ DocAccessible::DoInitialUpdate()
       if (RefPtr<dom::TabChild> tabChild = dom::TabChild::GetFrom(docShell)) {
         DocAccessibleChild* ipcDoc = new DocAccessibleChild(this, tabChild);
         SetIPCDoc(ipcDoc);
         if (IsRoot()) {
           tabChild->SetTopLevelDocAccessibleChild(ipcDoc);
         }
 
 #if defined(XP_WIN)
-        IAccessibleHolder holder(CreateHolderFromAccessible(this));
+        IAccessibleHolder holder(CreateHolderFromAccessible(WrapNotNull(this)));
         MOZ_DIAGNOSTIC_ASSERT(!holder.IsNull());
         int32_t childID = AccessibleWrap::GetChildIDFor(this);
 #else
         int32_t holder = 0, childID = 0;
 #endif
         tabChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0, childID,
                                                 holder);
       }
--- a/accessible/ipc/win/COMPtrTypes.cpp
+++ b/accessible/ipc/win/COMPtrTypes.cpp
@@ -6,41 +6,39 @@
 
 #include "mozilla/a11y/COMPtrTypes.h"
 
 #include "Accessible2_3.h"
 #include "MainThreadUtils.h"
 #include "mozilla/a11y/Accessible.h"
 #include "mozilla/a11y/Platform.h"
 #include "mozilla/a11y/HandlerProvider.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Move.h"
 #include "mozilla/mscom/MainThreadHandoff.h"
 #include "mozilla/mscom/Utils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/RefPtr.h"
 #include "nsXULAppAPI.h"
 
 using mozilla::mscom::MainThreadHandoff;
 using mozilla::mscom::ProxyUniquePtr;
 using mozilla::mscom::STAUniquePtr;
 
 namespace mozilla {
 namespace a11y {
 
 IAccessibleHolder
-CreateHolderFromAccessible(Accessible* aAccToWrap)
+CreateHolderFromAccessible(NotNull<Accessible*> aAccToWrap)
 {
-  MOZ_ASSERT(aAccToWrap && NS_IsMainThread());
-  if (!aAccToWrap) {
-    return nullptr;
-  }
+  MOZ_ASSERT(NS_IsMainThread());
 
   STAUniquePtr<IAccessible> iaToProxy;
   aAccToWrap->GetNativeInterface(mscom::getter_AddRefs(iaToProxy));
-  MOZ_ASSERT(iaToProxy);
+  MOZ_DIAGNOSTIC_ASSERT(iaToProxy);
   if (!iaToProxy) {
     return nullptr;
   }
 
   static const bool useHandler =
     Preferences::GetBool("accessibility.handler.enabled", false) &&
     IsHandlerRegistered();
 
@@ -48,17 +46,17 @@ CreateHolderFromAccessible(Accessible* a
   if (useHandler) {
     payload = new HandlerProvider(IID_IAccessible,
                                   mscom::ToInterceptorTargetPtr(iaToProxy));
   }
 
   ProxyUniquePtr<IAccessible> intercepted;
   HRESULT hr = MainThreadHandoff::WrapInterface(Move(iaToProxy), payload,
                                                 (IAccessible**) mscom::getter_AddRefs(intercepted));
-  MOZ_ASSERT(SUCCEEDED(hr));
+  MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr));
   if (FAILED(hr)) {
     return nullptr;
   }
 
   return IAccessibleHolder(Move(intercepted));
 }
 
 IHandlerControlHolder
--- a/accessible/ipc/win/COMPtrTypes.h
+++ b/accessible/ipc/win/COMPtrTypes.h
@@ -4,29 +4,30 @@
  * 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 mozilla_a11y_COMPtrTypes_h
 #define mozilla_a11y_COMPtrTypes_h
 
 #include "mozilla/a11y/AccessibleHandler.h"
 #include "mozilla/mscom/COMPtrHolder.h"
+#include "mozilla/NotNull.h"
 
 #include <oleacc.h>
 
 namespace mozilla {
 namespace a11y {
 
 typedef mozilla::mscom::COMPtrHolder<IAccessible, IID_IAccessible> IAccessibleHolder;
 typedef mozilla::mscom::COMPtrHolder<IDispatch, IID_IDispatch> IDispatchHolder;
 
 class Accessible;
 
 IAccessibleHolder
-CreateHolderFromAccessible(Accessible* aAccToWrap);
+CreateHolderFromAccessible(NotNull<Accessible*> aAccToWrap);
 
 typedef mozilla::mscom::COMPtrHolder<IHandlerControl, IID_IHandlerControl> IHandlerControlHolder;
 
 IHandlerControlHolder
 CreateHolderFromHandlerControl(mscom::ProxyUniquePtr<IHandlerControl> aHandlerControl);
 
 } // namespace a11y
 } // namespace mozilla
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -216,19 +216,16 @@ pref("browser.eme.ui.enabled", false);
 pref("browser.uitour.enabled", true);
 pref("browser.uitour.loglevel", "Error");
 pref("browser.uitour.requireSecure", true);
 pref("browser.uitour.themeOrigin", "https://addons.mozilla.org/%LOCALE%/firefox/themes/");
 pref("browser.uitour.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tour/");
 // How long to show a Hearbeat survey (two hours, in seconds)
 pref("browser.uitour.surveyDuration", 7200);
 
-pref("browser.customizemode.tip0.shown", false);
-pref("browser.customizemode.tip0.learnMoreUrl", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/customize");
-
 pref("keyword.enabled", true);
 pref("browser.fixup.domainwhitelist.localhost", true);
 
 pref("general.useragent.locale", "@AB_CD@");
 pref("general.skins.selectedSkin", "classic/1.0");
 
 pref("general.smoothScroll", true);
 #ifdef UNIX_BUT_NOT_MAC
@@ -494,18 +491,16 @@ pref("browser.ctrlTab.previews", false);
 pref("browser.bookmarks.autoExportHTML",          false);
 
 // The maximum number of daily bookmark backups to
 // keep in {PROFILEDIR}/bookmarkbackups. Special values:
 // -1: unlimited
 //  0: no backups created (and deletes all existing backups)
 pref("browser.bookmarks.max_backups",             15);
 
-pref("browser.bookmarks.showRecentlyBookmarked",  true);
-
 // Whether menu should close after Ctrl-click, middle-click, etc.
 pref("browser.bookmarks.openInTabClosesMenu", true);
 
 // Scripts & Windows prefs
 pref("dom.disable_open_during_load",              true);
 pref("javascript.options.showInConsole",          true);
 #ifdef DEBUG
 pref("general.warnOnAboutConfig",                 false);
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -157,17 +157,20 @@
         if (cssClass == "badStsCert") {
           document.getElementById("badStsCertExplanation").removeAttribute("hidden");
         }
       }
 
       function initPage() {
         var err = getErrorCode();
         // List of error pages with an illustration.
-        let illustratedErrors = ["malformedURI", "dnsNotFound"];
+        let illustratedErrors = [
+          "malformedURI", "dnsNotFound", "connectionFailure", "netInterrupt",
+          "netTimeout", "netReset", "netOffline",
+        ];
         if (illustratedErrors.includes(err)) {
           document.body.classList.add("illustrated", err);
         }
 
         gIsCertError = (err == "nssBadCert");
         // Only worry about captive portals if this is a cert error.
         let showCaptivePortalUI = isCaptive() && gIsCertError;
         if (showCaptivePortalUI) {
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -420,20 +420,16 @@
                    oncommand="return FeedHandler.subscribeToFeed(null, event);"
                    onclick="checkForMiddleClick(this, event);"/>
       </menu>
       <menuitem id="menu_bookmarkAllTabs"
                 label="&addCurPagesCmd.label;"
                 class="show-only-for-keyboard"
                 command="Browser:BookmarkAllTabs"
                 key="bookmarkAllTabsKb"/>
-      <menuseparator/>
-      <menuitem label="&recentBookmarks.label;"
-                id="menu_recentBookmarks"
-                disabled="true"/>
       <menuseparator id="bookmarksToolbarSeparator"/>
       <menu id="bookmarksToolbarFolderMenu"
             class="menu-iconic bookmark-item"
             label="&personalbarCmd.label;"
             container="true">
         <menupopup id="bookmarksToolbarFolderPopup"
 #ifndef XP_MACOSX
                    placespopup="true"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1276,267 +1276,16 @@ var PlacesToolbarHelper = {
       if (this._viewElt._placesView) {
         this._viewElt._placesView.uninit();
       }
       this.init();
     }
   },
 };
 
-var RecentBookmarksMenuUI = {
-  RECENTLY_BOOKMARKED_PREF: "browser.bookmarks.showRecentlyBookmarked",
-  MAX_RESULTS: 5,
-  // This timeout affects how soon the recent menu items are updated when
-  // an onItemRemoved notification is received - when we receive a notification,
-  // we delay updating the UI in case another is received. If one is, then we
-  // we'll restart the wait again. It wants to be more than 16ms (60fps) but
-  // probably less than 100ms.
-  ITEM_REMOVED_TIMEOUT: 40,
-
-  _recentGuids: undefined,
-  _visible: undefined,
-
-  QueryInterface: XPCOMUtils.generateQI([
-    Ci.nsINavBookmarkObserver,
-    Ci.nsIObserver,
-    Ci.nsISupportsWeakReference
-  ]),
-
-  get visible() {
-    return this._visible;
-  },
-
-  /**
-   * Set the visibility of the recently bookmarked menu items.
-   *
-   * @param {Boolean} show Set to true to show the menu items, false otherwise.
-   */
-  set visible(visible) {
-    // If we're not changing anything, bail early so that we're not unnecessarily
-    // doing things we don't need to.
-    if (visible == this._visible) {
-      return;
-    }
-
-    this._visible = visible;
-    Services.prefs.setBoolPref(this.RECENTLY_BOOKMARKED_PREF, visible);
-    this._clearExistingItems();
-
-    if (visible) {
-      this._insertRecentMenuItems();
-    }
-  },
-
-  /**
-   * Observer for observing pref changes.
-   */
-  observe(subject, topic, data) {
-    if (topic == "nsPref:changed" && data == this.RECENTLY_BOOKMARKED_PREF) {
-      this.visible = Services.prefs.getBoolPref(this.RECENTLY_BOOKMARKED_PREF, true);
-    }
-  },
-
-  /**
-   * Initializes the recent bookmarks menu items into a menu.
-   *
-   * @param {menuitem} aHeaderItem A DOM menuitem to insert the recent bookmarks
-   *                               into.
-   * @param {String} aExtraCSSClass Any extra CSS classes to insert onto the recent
-   *                                bookmark menuitems.
-   */
-  init(aHeaderItem, aExtraCSSClass = "") {
-    this.headerItem = aHeaderItem;
-    this.extraCSSClass = aExtraCSSClass;
-    this._recentGuids = new Set();
-
-    // This also displays the initial list if necessary.
-    this.visible = Services.prefs.getBoolPref(this.RECENTLY_BOOKMARKED_PREF, true);
-
-    // Add observers and listeners and remove them again when the menupopup closes.
-
-    let bookmarksMenu = aHeaderItem.parentNode;
-    let placesContextMenu = document.getElementById("placesContext");
-
-    let onPlacesContextMenuShowing = event => {
-      if (event.target == event.currentTarget) {
-        let triggerPopup = event.target.triggerNode;
-        while (triggerPopup && triggerPopup.localName != "menupopup") {
-          triggerPopup = triggerPopup.parentNode;
-        }
-        let shouldHidePrefUI = triggerPopup != bookmarksMenu;
-        this._updatePlacesContextMenu(shouldHidePrefUI);
-      }
-    };
-
-    let onBookmarksMenuHidden = event => {
-      // If hide event is not targeted to the main menu (e.g. hiding a sub-menu),
-      // nothing to do.
-      if (event.target != event.currentTarget) {
-        return;
-      }
-
-      // Cancel any item removed timers.
-      if (this._itemRemovedTimer) {
-        clearTimeout(this._itemRemovedTimer);
-      }
-
-      this._updatePlacesContextMenu(true);
-
-      Services.prefs.removeObserver(this.RECENTLY_BOOKMARKED_PREF, this);
-      PlacesUtils.bookmarks.removeObserver(this);
-      this._recentlyBookmarkedObserver = null;
-      if (placesContextMenu) {
-        placesContextMenu.removeEventListener("popupshowing", onPlacesContextMenuShowing);
-      }
-      bookmarksMenu.removeEventListener("popuphidden", onBookmarksMenuHidden);
-
-      this._visible = undefined;
-      delete this.headerItem;
-      delete this.extraCSSClass;
-    };
-
-    Services.prefs.addObserver(this.RECENTLY_BOOKMARKED_PREF, this, true);
-    PlacesUtils.bookmarks.addObserver(this, true);
-
-    // The context menu doesn't exist in non-browser windows on Mac
-    if (placesContextMenu) {
-      placesContextMenu.addEventListener("popupshowing", onPlacesContextMenuShowing);
-    }
-
-    bookmarksMenu.addEventListener("popuphidden", onBookmarksMenuHidden);
-  },
-
-  /**
-   * Clears existing recent items from the menu and updates the separators
-   * according to this.visible.
-   */
-  _clearExistingItems() {
-    this._recentGuids.clear();
-
-    while (this.headerItem.nextSibling &&
-           this.headerItem.nextSibling.localName == "menuitem") {
-      this.headerItem.nextSibling.remove();
-    }
-
-    let separator = this.headerItem.previousSibling;
-    this.headerItem.hidden = !this.visible;
-    separator.hidden = !this.visible;
-  },
-
-  /**
-   * Inserts recent bookmark items into the menu.
-   */
-  _insertRecentMenuItems() {
-    let separator = this.headerItem.previousSibling;
-    this.headerItem.hidden = !this.visible;
-    separator.hidden = !this.visible;
-
-    let options = PlacesUtils.history.getNewQueryOptions();
-    options.excludeQueries = true;
-    options.queryType = options.QUERY_TYPE_BOOKMARKS;
-    options.sortingMode = options.SORT_BY_DATEADDED_DESCENDING;
-    options.maxResults = this.MAX_RESULTS;
-    let query = PlacesUtils.history.getNewQuery();
-
-    let sh = Cc["@mozilla.org/network/serialization-helper;1"]
-               .getService(Ci.nsISerializationHelper);
-    let loadingPrincipal = sh.serializeToString(document.nodePrincipal);
-
-    let fragment = document.createDocumentFragment();
-    let root = PlacesUtils.history.executeQuery(query, options).root;
-    root.containerOpen = true;
-    for (let i = 0; i < root.childCount; i++) {
-      let node = root.getChild(i);
-      let uri = node.uri;
-      let title = node.title;
-      let icon = node.icon;
-
-      let item =
-        document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                 "menuitem");
-      item.setAttribute("label", title || uri);
-      item.setAttribute("targetURI", uri);
-      item.setAttribute("simulated-places-node", true);
-      item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
-                                 this.extraCSSClass);
-      if (icon) {
-        item.setAttribute("image", icon);
-        item.setAttribute("loadingprincipal", loadingPrincipal);
-      }
-      item._placesNode = node;
-      fragment.appendChild(item);
-      this._recentGuids.add(node.bookmarkGuid);
-    }
-    root.containerOpen = false;
-    this.headerItem.parentNode.insertBefore(fragment, this.headerItem.nextSibling);
-  },
-
-  /**
-   * Show the places related context menu for the bookmark items.
-   *
-   * @param {Boolean} shouldHidePrefUI Set to true to hide the UI for switching
-   *                                   the showRecentlyBookmarked pref.
-   */
-  _updatePlacesContextMenu(shouldHidePrefUI = false) {
-    let showItem = document.getElementById("placesContext_showRecentlyBookmarked");
-    // On Mac the menuitem doesn't exist when we're in the Library window context.
-    if (!showItem) {
-      return;
-    }
-    let hideItem = document.getElementById("placesContext_hideRecentlyBookmarked");
-    let separator = document.getElementById("placesContext_recentlyBookmarkedSeparator");
-    let prefEnabled = !shouldHidePrefUI && Services.prefs.getBoolPref(this.RECENTLY_BOOKMARKED_PREF);
-    showItem.hidden = shouldHidePrefUI || prefEnabled;
-    hideItem.hidden = shouldHidePrefUI || !prefEnabled;
-    separator.hidden = shouldHidePrefUI;
-    if (!shouldHidePrefUI) {
-      // Move to the bottom of the menu.
-      separator.parentNode.appendChild(separator);
-      showItem.parentNode.appendChild(showItem);
-      hideItem.parentNode.appendChild(hideItem);
-    }
-  },
-
-  /**
-   * nsINavBookmarkObserver methods.
-   */
-
-  /*
-   * Handles onItemRemoved notifications from the bookmarks service.
-   */
-  onItemRemoved(itemId, parentId, index, itemType, uri, guid) {
-    if (!this.visible) {
-      return;
-    }
-    // Update the menu when a bookmark has been removed.
-    // The native menubar on Mac doesn't support live update, so this is
-    // unlikely to be called there.
-    if (guid && this._recentGuids.has(guid)) {
-      if (this._itemRemovedTimer) {
-        clearTimeout(this._itemRemovedTimer);
-      }
-
-      this._itemRemovedTimer = setTimeout(() => {
-        this._clearExistingItems();
-        this._insertRecentMenuItems();
-      }, this.ITEM_REMOVED_TIMEOUT);
-    }
-  },
-
-  skipTags: true,
-  skipDescendantsOnItemRemoval: false,
-
-  onBeginUpdateBatch() {},
-  onEndUpdateBatch() {},
-  onItemAdded() {},
-  onItemChanged() {},
-  onItemVisited() {},
-  onItemMoved() {},
-}
-
 /**
  * Handles the Library button in the toolbar.
  */
 var LibraryUI = {
   triggerLibraryAnimation(animation) {
     if (!this.hasOwnProperty("COSMETIC_ANIMATIONS_ENABLED")) {
       XPCOMUtils.defineLazyPreferenceGetter(this, "COSMETIC_ANIMATIONS_ENABLED",
         "toolkit.cosmeticAnimations.enabled", true);
@@ -1718,18 +1467,16 @@ var BookmarkingUI = {
       // Don't open a popup in the overflow popup, rather just open the Library.
       event.preventDefault();
       widget.node.removeAttribute("closemenu");
       PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
       return;
     }
 
     this._initMobileBookmarks(document.getElementById("BMB_mobileBookmarks"));
-    RecentBookmarksMenuUI.init(document.getElementById("BMB_recentBookmarks"),
-                               "subviewbutton");
 
     if (!this._popupNeedsUpdate)
       return;
     this._popupNeedsUpdate = false;
 
     let popup = event.target;
     let getPlacesAnonymousElement =
       aAnonId => document.getAnonymousElementByAttribute(popup.parentNode,
@@ -1955,17 +1702,16 @@ var BookmarkingUI = {
   onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
     // Don't handle events for submenus.
     if (event.target != event.currentTarget)
       return;
 
     this.updateBookmarkPageMenuItem();
     PlacesCommandHook.updateBookmarkAllTabsCommand();
     this._initMobileBookmarks(document.getElementById("menu_mobileBookmarks"));
-    RecentBookmarksMenuUI.init(document.getElementById("menu_recentBookmarks"));
   },
 
   _showBookmarkedNotification: function BUI_showBookmarkedNotification() {
     function getCenteringTransformForRects(rectToPosition, referenceRect) {
       let topDiff = referenceRect.top - rectToPosition.top;
       let leftDiff = referenceRect.left - rectToPosition.left;
       let heightDiff = referenceRect.height - rectToPosition.height;
       let widthDiff = referenceRect.width - rectToPosition.width;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -388,35 +388,17 @@
                onpopuphiding="if (event.target != this)
                                 return;
                               gContextMenu.hiding();
                               gContextMenu = null;
                               updateEditUIVisibility();">
 #include browser-context.inc
     </menupopup>
 
-    <menupopup id="placesContext">
-      <menuseparator id="placesContext_recentlyBookmarkedSeparator"
-                     ignoreitem="true"
-                     hidden="true"/>
-      <menuitem id="placesContext_hideRecentlyBookmarked"
-                label="&hideRecentlyBookmarked.label;"
-                accesskey="&hideRecentlyBookmarked.accesskey;"
-                oncommand="RecentBookmarksMenuUI.visible = false;"
-                closemenu="single"
-                ignoreitem="true"
-                hidden="true"/>
-      <menuitem id="placesContext_showRecentlyBookmarked"
-                label="&showRecentlyBookmarked.label;"
-                accesskey="&showRecentlyBookmarked.accesskey;"
-                oncommand="RecentBookmarksMenuUI.visible = true;"
-                closemenu="single"
-                ignoreitem="true"
-                hidden="true"/>
-    </menupopup>
+    <menupopup id="placesContext"/>
 
     <panel id="ctrlTab-panel" hidden="true" norestorefocus="true" level="top">
       <hbox>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
@@ -1110,21 +1092,16 @@
           </menuitem>
           <!-- NB: temporary solution for bug 985024, this should go away soon. -->
           <menuitem id="BMB_bookmarksShowAllTop"
                     class="menuitem-iconic subviewbutton"
                     label="&showAllBookmarks2.label;"
                     command="Browser:ShowAllBookmarks"
                     key="manBookmarkKb"/>
           <menuseparator/>
-          <menuitem label="&recentBookmarks.label;"
-                    id="BMB_recentBookmarks"
-                    disabled="true"
-                    class="menuitem-iconic subviewbutton"/>
-          <menuseparator/>
           <menu id="BMB_bookmarksToolbar"
                 class="menu-iconic bookmark-item subviewbutton"
                 label="&personalbarCmd.label;"
                 container="true">
             <menupopup id="BMB_bookmarksToolbarPopup"
                        placespopup="true"
                        context="placesContext"
                        onpopupshowing="if (!this.parentNode._placesView)
--- a/browser/base/content/default-theme-icon.svg
+++ b/browser/base/content/default-theme-icon.svg
@@ -1,13 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
-    <rect fill="#fff" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-    <path fill="#e3e3e3" d="M3 1h26a2 2 0 0 1 2 2v18H1V3a2 2 0 0 1 2-2z"/>
-    <rect stroke="gray" fill="#fff" x="10.5" y="5.5" width="18" height="11" rx="1" ry="1"/>
-    <circle fill="#fcfcfc" stroke="gray" stroke-width="1.2px" cx="11" cy="11" r="7.5"/>
-    <path fill="#595959" d="M14 10h-3.6l1.3-1.3a1 1 0 0 0-1.4-1.4l-3 3a1 1 0 0 0 0 1.4l3 3a1 1 0 0 0 1.4-1.4L10.4 12H14a1 1 0 0 0 0-2z"/>
-    <path fill="none" stroke="#999" d="M1.5 20.5h29"/>
-    <rect fill="none" stroke="#999" stroke-width="2" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#202340" d="M2 2h14v13H2z"/>
+  <path fill="#f9f9fa" d="M16 2v13H2v15h28V2H16z"/>
+  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
+  <circle cx="9.5" cy="22.5" r="6" fill="#fff" stroke="#adadb3"/>
+  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
+  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path fill="#08091a" d="M15 2v12H2v1h14V2h-1z"/>
+</svg>
\ No newline at end of file
rename from browser/base/content/defaultthemes/compactdark.icon.svg
rename to browser/base/content/defaultthemes/dark.icon.svg
--- a/browser/base/content/defaultthemes/compactdark.icon.svg
+++ b/browser/base/content/defaultthemes/dark.icon.svg
@@ -1,16 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
-    <rect fill="#727780" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-    <path fill="#393f4c" d="M3 1h26a2 2 0 0 1 2 2v14H1V3a2 2 0 0 1 2-2z"/>
-    <rect fill="#171b1f" x="3.5" y="3.5" width="25" height="11" rx="1" ry="1"/>
-    <path fill="#252c33" d="M4.5 3.5h12v11h-12a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1z"/>
-    <rect stroke="#1d2328" fill="none" stroke-width="2" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-    <path class="icon-line-2px" d="M10 6L7 9l3 3M7.5 9H13"/>
-    <path stroke="#1d2328" d="M1.5 16.5h29"/>
-    <path class="separator-toolbar-items" d="M16.5 3.5v11"/>
-    <rect fill="none" stroke="#171b1f" x="3.5" y="3.5" width="25" height="11" rx="1" ry="1"/>
-    <path fill="#f0f1f2" d="M13 8H9.4l1.3-1.3a1 1 0 0 0-1.4-1.4l-3 3a1 1 0 0 0 0 1.4l3 3a1 1 0 0 0 1.4-1.4L9.4 10H13a1 1 0 0 0 0-2z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#0c0c0d" d="M2 2h14v13H2z"/>
+  <path fill="#323234" d="M16 2v13H2v15h28V2H16z"/>
+  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
+  <circle cx="9.5" cy="22.5" r="6" fill="#474749" stroke="#08091a"/>
+  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#f9f9fa" fill-opacity=".8"/>
+  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#f9f9fa" fill-opacity=".8"/>
+  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
+  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#f9f9fa" fill-opacity=".8"/>
+  <path fill="#08091a" d="M15 2v12H2v1h14V2h-1z"/>
+</svg>
\ No newline at end of file
rename from browser/base/content/defaultthemes/compactlight.icon.svg
rename to browser/base/content/defaultthemes/light.icon.svg
--- a/browser/base/content/defaultthemes/compactlight.icon.svg
+++ b/browser/base/content/defaultthemes/light.icon.svg
@@ -1,15 +1,11 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
-    <rect fill="#fff" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-    <path fill="#e3e3e3" d="M3 1h26a2 2 0 0 1 2 2v14H1V3a2 2 0 0 1 2-2z"/>
-    <rect fill="#fff" x="3.5" y="3.5" width="25" height="11" rx="1" ry="1"/>
-    <path fill="#fcfcfc" d="M4.5 3.5h12v11h-12a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1z"/>
-    <rect fill="none" stroke="#999" stroke-width="2" x="1" y="1" width="30" height="30" rx="2" ry="2"/>
-    <path stroke="#999" d="M1.5 16.5h29"/>
-    <path stroke="#b3b3b3" d="M16.5 3.5v11"/>
-    <rect fill="none" stroke="#b3b3b3" x="3.5" y="3.5" width="25" height="11" rx="1" ry="1"/>
-    <path fill="#595959" d="M13 8H9.4l1.3-1.3a1 1 0 0 0-1.4-1.4l-3 3a1 1 0 0 0 0 1.4l3 3a1 1 0 0 0 1.4-1.4L9.4 10H13a1 1 0 0 0 0-2z"/>
-</svg>
+<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
+  <path fill="#e1e1e5" d="M2 2h14v13H2z"/>
+  <path fill="#f9f9fa" d="M16 2v13H2v15h28V2H16z"/>
+  <rect x="1" y="1" width="30" height="30" rx="2" ry="2" fill="none" stroke="#08091a" stroke-opacity=".35" stroke-width="2"/>
+  <circle cx="9.5" cy="22.5" r="6" fill="#fff" stroke="#adadb3"/>
+  <path d="M12.5 22H7.707l2.146-2.146a.5.5 0 0 0-.707-.707l-3 3a.5.5 0 0 0 0 .708l3 3a.5.5 0 1 0 .707-.707L7.707 23H12.5a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path d="M20.5 20h4a.5.5 0 0 0 0-1h-4a.5.5 0 0 0 0 1zm4 2h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zm0 3h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path fill="#0a84ff" d="M16 2h14v1H16z"/>
+  <path d="M26.354 8.646l-3.5-3.5a.5.5 0 0 0-.707 0l-3.5 3.5a.5.5 0 0 0 .707.707L20 8.707V11.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5V8.707l.646.646a.5.5 0 1 0 .707-.707zM24 11h-1V9h-1v2h-1V7.707l1.5-1.5 1.5 1.5z" fill="#0c0c0d" fill-opacity=".8"/>
+  <path fill="#08091a" fill-opacity=".25" d="M15 2v12H2v1h14V2h-1z"/>
+</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/base/content/illustrations/error-connection-failure.svg
@@ -0,0 +1,49 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="300" height="300" viewBox="0 0 300 300">
+  <defs>
+    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#ccfbff"/>
+      <stop offset="1" stop-color="#c9e4ff"/>
+    </linearGradient>
+    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#00c8d7"/>
+      <stop offset="1" stop-color="#0a84ff"/>
+    </linearGradient>
+  </defs>
+  <path d="M224.245 144.067h-10.733c.136.343.274.674.41 1h10.323a.5.5 0 0 0 0-1zm2.454-11.821a.5.5 0 0 0-.5-.5h-20.26c.373.357.727.688 1.065 1h19.2a.5.5 0 0 0 .496-.5zm8.546 11.821h-3a.5.5 0 1 0 0 1h3a.5.5 0 0 0 0-1zm5 0h-1a.5.5 0 1 0 0 1h1a.5.5 0 0 0 0-1zm-3.3-6.66h-25.78a12.767 12.767 0 0 1 .862 2h24.918a1 1 0 0 0 0-2zm20.422 6.66h-8.122a.5.5 0 1 0 0 1h8.122a.5.5 0 0 0 0-1z" fill="#eaeaee"/>
+  <path d="M269.53 87.757h-24.236c-2.108-3.9-7.559-12.718-14.4-14.023-8.952-1.707-10.737 7.217-10.737 7.217s-5.949-15.468-21-13.419c-16.878 2.3-8.928 20.065-8.928 20.065h-25.408l8.181.159h-8.184a1 1 0 0 0 0 2H269.53a1 1 0 0 0 0-2z" fill="#fff"/>
+  <path d="M118.373 63.908h-13.69c-1.129-2.112-4.19-7.156-8.057-7.894-4.978-.949-5.971 4.013-5.971 4.013s-3.309-8.6-11.68-7.462c-9.386 1.278-4.965 11.158-4.965 11.158H59.88l9.471.185h-9.212a1 1 0 0 0 0 2h58.233a1 1 0 1 0 0-2z" fill="#fff"/>
+  <ellipse cx="143.566" cy="245.472" rx="55.042" ry="8.362" fill="#eaeaee"/>
+  <path d="M102.31 121.507H60.818a1 1 0 0 0 0 2h41.492a1 1 0 1 0 0-2zM70.336 117.6H82.1a.5.5 0 0 0 0-1H70.336a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
+  <path d="M111.457 174.8h-78.3a1 1 0 0 0 0 2h78.3a1 1 0 1 0 0-2zm-26.742-3.793h1a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1zm10 0h3.1a.5.5 0 0 0 0-1h-3.1a.5.5 0 0 0 0 1zm-17 0h3a.5.5 0 0 0 0-1h-3a.5.5 0 0 0 0 1zm-20 0h12a.5.5 0 0 0 0-1h-12a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
+  <path d="M206.885 62.973l.045-.1c-.058.027-.063.059-.045.1z" fill="#fff"/>
+  <path d="M77.937 214.941H39.95a1 1 0 1 1 0-2h37.987a1 1 0 1 1 0 2z" fill="#eaeaee"/>
+  <path d="M258.931 214.941h-61.813a1 1 0 0 1 0-2h61.813a1 1 0 0 1 0 2z" fill="#eaeaee"/>
+  <path d="M265.745 85.333h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-8.07a.5.5 0 0 1-.447-.277c-.007-.014-.724-1.425-1.979-3.342a.5.5 0 1 1 .837-.548c.393.6 1.444 2.293 1.888 3.167h7.772a.5.5 0 0 1 0 1zm-66.489-.712h-3a.5.5 0 0 1 0-1h3a.5.5 0 0 1 0 1zm-11 0h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zM190.1 83.13a.5.5 0 0 1-.474-.339c-.1-.29-.2-.615-.31-.971a.5.5 0 1 1 .958-.287c.1.343.2.657.3.937a.5.5 0 0 1-.474.661zm30.5-5.156a.5.5 0 0 1-.467-.32 23.7 23.7 0 0 0-2.182-4.039.5.5 0 0 1 .834-.552 27.291 27.291 0 0 1 1.719 2.982 10.522 10.522 0 0 1 3.679-5.015.5.5 0 0 1 .571.82 10.181 10.181 0 0 0-3.665 5.721.5.5 0 0 1-.448.4zm18.345-2.964a.5.5 0 0 1-.339-.132q-.361-.333-.735-.651a.5.5 0 0 1 .647-.762q.39.331.765.678a.5.5 0 0 1-.339.868zm-49.923-1.725a.47.47 0 0 1-.09-.008.5.5 0 0 1-.4-.581c.792-4.351 3.544-7.229 8.18-8.556a.5.5 0 0 1 .275.962c-4.24 1.212-6.753 3.828-7.472 7.773a.5.5 0 0 1-.499.411zm45.893-1.218a.5.5 0 0 1-.237-.06 12.545 12.545 0 0 0-2.666-1.081.5.5 0 1 1 .261-.966 13.559 13.559 0 0 1 2.88 1.167.5.5 0 0 1-.238.94zM212.146 67.4a.5.5 0 0 1-.28-.086q-.4-.27-.82-.524a.5.5 0 1 1 .516-.856q.444.267.865.552a.5.5 0 0 1-.281.914zm-4.47-2.2a.5.5 0 0 1-.154-.024 16.724 16.724 0 0 0-2.832-.647.5.5 0 0 1 .137-.99 17.6 17.6 0 0 1 3 .686.5.5 0 0 1-.154.976z" fill="#eaeaee"/>
+  <path d="M72.315 62.052h-12a.5.5 0 0 1 0-1h12a.5.5 0 0 1 0 1zm45.582-.184h-1.8a.5.5 0 0 1 0-1h1.8a.5.5 0 0 1 0 1zm-10.8 0h-1a.5.5 0 0 1 0-1h1a.5.5 0 0 1 0 1zm-3.491-2.881a.5.5 0 0 1-.39-.186 18.484 18.484 0 0 0-2-2.129.5.5 0 0 1 .668-.744A19.433 19.433 0 0 1 104 58.174a.5.5 0 0 1-.389.814zm-12.521-.631a.5.5 0 0 1-.466-.319 13.8 13.8 0 0 0-3.857-5.165.5.5 0 0 1 .623-.782 14.47 14.47 0 0 1 3.624 4.438A5.414 5.414 0 0 1 94.12 53.5a.5.5 0 1 1 .326.945 4.994 4.994 0 0 0-2.867 3.51.5.5 0 0 1-.49.401zm-17.637-2.037h-.051a.5.5 0 0 1-.447-.548 6.454 6.454 0 0 1 1.057-3.036.5.5 0 1 1 .824.566 5.46 5.46 0 0 0-.886 2.571.5.5 0 0 1-.497.447zm4.193-5.039a.5.5 0 0 1-.151-.977 10.27 10.27 0 0 1 1.017-.264.5.5 0 0 1 .2.979 9.033 9.033 0 0 0-.917.237.5.5 0 0 1-.148.025z" fill="#eaeaee"/>
+  <path d="M216.326 144.72c-1.079-4.2-3.73-8.616-7.88-13.117a84.471 84.471 0 0 0-8.921-9.988 5.558 5.558 0 0 0-2.261-1.3c-7.177-6.885-21.972-19.819-32.5-20.546a27.625 27.625 0 0 0-1.889-.063 47.155 47.155 0 0 0-15.147 2.913l-5.114-1.4a5 5 0 0 0-6.142 3.5l-.969 3.545a68.668 68.668 0 0 0-7.844 5.3l-2.052-.048h-.116a5 5 0 0 0-5 4.884l-.042 1.829a45.575 45.575 0 0 0-5.648 7.656 5 5 0 0 0-2.515 5.326c-1.882 5.474-2.848 13.743-2.875 24.633a100.416 100.416 0 0 1-1.646 7.791l-4.279 2.594a6.312 6.312 0 0 0-2.125 8.657l1.516 2.5q-.375.778-.761 1.518l-4.037 1.24a6.312 6.312 0 0 0-4.176 7.877l.6 1.953q-.516.587-1.038 1.143l-1.616-.127a5.926 5.926 0 0 0-.5-.02 6.334 6.334 0 0 0-6.277 5.8l-.115 1.456c-2.741 2.043-5.415 4.547-5.415 9.117 0 6.528 10.272 10.568 13.648 11.9a68.955 68.955 0 0 0 24.224 4.781q.566 0 1.127-.012c.607 2.411 1.208 4.468 1.8 6.144a54.956 54.956 0 0 0 4.745 10.264c1.748 2.839 4.22 6.1 7.845 6.1a5.673 5.673 0 0 0 3.572-1.268c1.746-1.421 2.429-3.072 2.482-10.087a86.865 86.865 0 0 0 16.862 1.533 80.987 80.987 0 0 0 14.53-1.463c-.215 6.82.472 8.55 2.275 10.017a5.672 5.672 0 0 0 3.571 1.268c5.645 0 9.953-8.9 12.573-16.361a52.867 52.867 0 0 0 1.069-3.467c1.432-3.724 4.539-13.353 4.892-27.476.161-6.428 0-12.95-.488-19.419a7.56 7.56 0 0 0 1.131-4.949c6.461-1.2 10.808-3.919 15.1-9.34 3.4-4.308 8-14.308 5.826-22.788z" fill="#fff"/>
+  <path d="M211.476 145.947c-1.092-4.252-4.261-8.341-6.9-11.168a78.846 78.846 0 0 0-8.482-9.533.649.649 0 0 0-.907.018c-10.173-10.009-23.029-19.987-30.783-20.521-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.561a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.744a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842a104.047 104.047 0 0 1-2.33 10.6l-6 3.638a1.452 1.452 0 0 0-.685 1.835l3.021 4.978a54.736 54.736 0 0 1-2.883 5.777l-5.979 1.836a1.305 1.305 0 0 0-.864 1.63l1.394 4.541a51.394 51.394 0 0 1-4.691 5.189l-3.93-.31a1.3 1.3 0 0 0-1.4 1.2l-.3 3.752c-3.258 2.357-5.218 3.541-5.218 6.422 0 4.144 20.8 13.416 37.9 11.39a88.878 88.878 0 0 0 2.615 9.761c2.852 8.125 6.861 14.032 8.287 12.871.675-.549.711-6.554.562-12.73.446.174 8.025 3.054 21.939 3.054a88.332 88.332 0 0 0 19.876-2.718c-.4 5.844-.626 11.816.085 12.394 1.426 1.161 5.418-4.745 8.269-12.871.357-1.018.7-2.159 1.031-3.377 1.276-3.253 4.307-12.338 4.649-26.035a191.885 191.885 0 0 0-.651-21.1 2.592 2.592 0 0 0-.475-4.743 128.961 128.961 0 0 0-.36-2.871 28.465 28.465 0 0 0 4.578-.19c6.147-.82 9.835-2.869 13.728-7.786 2.797-3.536 6.551-12.06 4.914-18.439z" fill="url(#a)"/>
+  <path d="M193.99 124.155c.033.013.067.031.1.045-9.986-9.641-22.2-18.942-29.683-19.457-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.561a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.744a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842a109.944 109.944 0 0 1-.921 4.824c.168-.72.327-1.43.475-2.127.061 3.039.177 6.023.332 8.836-1.014 1.2-4.408 5.548-2.546 8.155a10.876 10.876 0 0 0 3.445 2.858c.069.589.138 1.155.209 1.681 1.015 7.544 4.11 29 6.408 38.3.207-.021.417-.036.623-.061a88.878 88.878 0 0 0 2.615 9.761c2.852 8.125 6.861 14.032 8.287 12.871.675-.549.711-6.554.562-12.73.038.015.145.054.283.1V222.3a46 46 0 0 0 27.634 3.451c15.651-3.043 21.467-14.705 22.172-22.607 1.194-13.372-1.415-19.893-1.415-19.893l6.558-2.852c-.011-.135-.022-.284-.034-.417a2.592 2.592 0 0 0-.475-4.743l-11.158 4.316s-.109-1.739-2.5-4.348c-3.174-3.462-10.521-6.355-11.428-6.7a84.091 84.091 0 0 0 24.884 5.065l-.109-.839c-10.706-.609-24.216-5.348-24.216-5.348s23.783 4.316 33.476-.761c6.847-3.587 11.086-13.369 9.456-21.846-1.731-9.023-13.024-20.623-13.024-20.623z" fill="#f9f9fa"/>
+  <path d="M149.523 146.54l-5.537-1.957a1 1 0 0 0-1.219 1.406l1.99 3.81a1 1 0 0 0 1.349.423l3.547-1.853a1 1 0 0 0-.13-1.829z" fill="#fff"/>
+  <g fill="url(#b)">
+    <path d="M268.754 200.7h-.768c-17.152 0-33.29 0-42.937.322-8.333.277-16.774 1.994-16.787 4.736-.008 1.811 4.646 3.3 13.565 5.982 7.387 2.225 17.5 5.271 17.736 7.75a2.9 2.9 0 0 1-1.2 2.046c-2.984 2.772-11.281 5.909-19.089 4.588a25.1 25.1 0 0 1-12.468-6.4c-4.383-3.888-6.191-7.86-7.786-11.364-.619-1.359-1.2-2.643-1.9-3.837a22.145 22.145 0 0 0-5.357-6.126 194.068 194.068 0 0 0-.6-17.308 4.6 4.6 0 0 0-.2-6.523c.75-.036 1.463-.1 2.13-.188 6.749-.9 10.823-3.211 15.032-8.527 3.282-4.146 7.092-13.147 5.287-20.18-1.2-4.662-4.606-9.055-7.29-11.947a80.769 80.769 0 0 0-8.657-9.724 2.62 2.62 0 0 0-1.653-.713 133.306 133.306 0 0 0-15.775-13.264c-6.315-4.414-11.527-6.79-15.492-7.064a24.782 24.782 0 0 0-1.683-.056 45.119 45.119 0 0 0-15.027 3.055l-6.025-1.648a2 2 0 0 0-2.457 1.4l-1.3 4.745a65.633 65.633 0 0 0-9.391 6.332l-3.141-.073h-.047a2 2 0 0 0-2 1.953l-.069 2.965a42.572 42.572 0 0 0-6.522 8.837l-.3.093a2 2 0 0 0-1.312 2.506l.145.465c-1.993 5.12-3.01 13.443-3.023 24.766a102.594 102.594 0 0 1-2.05 9.47L105.03 171a3.309 3.309 0 0 0-1.114 4.538l2.345 3.87a56.333 56.333 0 0 1-2.1 4.209l-5.216 1.6a3.309 3.309 0 0 0-2.189 4.129l1.082 3.524a49.795 49.795 0 0 1-3.217 3.554l-3.023-.238a3.177 3.177 0 0 0-.267-.011 3.318 3.318 0 0 0-3.287 3.043l-.223 2.83c-2.965 2.13-5.3 3.833-5.3 7.5 0 2.65 3.4 5.1 6.95 6.946a89.372 89.372 0 0 0-1.57 3.365c-1.866 4.156-3.1 6.9-7.448 8.232-8.459 2.584-20.437.244-26.315-3.564-1.825-1.182-2.9-2.432-3.041-3.519-.264-2.111 5.188-4.187 9.167-5.7 3.919-1.492 6.152-2.394 6.47-3.56a1.66 1.66 0 0 0-.295-1.493C64.48 207.69 53.977 207.22 34 207.22a.5.5 0 0 0 0 1c12.5 0 29.627 0 31.645 2.642a.661.661 0 0 1 .125.623c-.2.732-3.339 1.928-5.861 2.888-5 1.9-10.165 3.871-9.8 6.761.176 1.405 1.382 2.869 3.489 4.234 4.275 2.77 11.581 4.841 18.68 4.841a29.2 29.2 0 0 0 8.471-1.16c4.784-1.462 6.161-4.531 8.068-8.778a89.007 89.007 0 0 1 1.552-3.326 54.676 54.676 0 0 0 3.9 1.709 65.842 65.842 0 0 0 23.126 4.572c1.177 0 2.336-.042 3.462-.126a80.823 80.823 0 0 0 2.292 8.265c.516 1.469 5.171 14.354 9.759 14.354a2.629 2.629 0 0 0 1.678-.595c.831-.677 1.5-1.223 1.353-11.536a76.69 76.69 0 0 0 19.885 2.308 86.392 86.392 0 0 0 17.71-2.167c-.585 10.114.149 10.711.989 11.395a2.628 2.628 0 0 0 1.677.595c4.572 0 9.226-12.885 9.742-14.354.347-.989.7-2.137 1.046-3.412 1.345-3.458 4.4-12.766 4.747-26.612.014-.552.01-1.108.019-1.662a20.857 20.857 0 0 1 4.507 5.34c.668 1.152 1.242 2.415 1.851 3.751 1.636 3.595 3.491 7.669 8.032 11.7a26.054 26.054 0 0 0 12.965 6.641 24.08 24.08 0 0 0 4.006.325c6.5 0 12.949-2.4 15.93-5.166a3.735 3.735 0 0 0 1.514-2.872c-.274-2.93-7.481-5.313-18.444-8.614-5.427-1.634-12.859-3.872-12.854-5.02.006-1.314 5.643-3.4 15.82-3.742 9.631-.321 25.785-.326 42.9-.321h.768a.5.5 0 0 0 0-1zM204.579 135c2.636 2.827 5.805 6.916 6.9 11.168 1.637 6.379-2.117 14.9-4.918 18.441-3.893 4.918-7.581 6.967-13.728 7.786-.7.093-1.432.142-2.179.172a.988.988 0 0 0-.354-.077 73.652 73.652 0 0 1-26.1-5.54 1 1 0 0 0-.826 1.821 74.46 74.46 0 0 0 25.115 5.664c.039.323.08.659.121 1.021a2.579 2.579 0 0 1 1.39 3.874.484.484 0 0 0-.243.11c-1.838 1.634-8.407 3.132-8.474 3.146a.5.5 0 0 0-.113.041l-2.2 1.1a.5.5 0 0 0-.274.5c.006.061.561 6.134-2.275 8.973a5.1 5.1 0 0 1-3.97 1.438l.82-4.449a.5.5 0 0 0-.383-.579l-2.605-.579c-.139-1-.37-4.182 1.943-5.917.976-.731 8.157-3.579 10.842-4.614a.5.5 0 0 0-.359-.933c-1 .386-9.818 3.8-11.082 4.747-3.262 2.447-2.312 7.043-2.27 7.237a.492.492 0 0 0 .107.2q-1.276-.217-2.6-.406c-.048-1.62-.626-3.782-2.151-4.88a3.922 3.922 0 0 0-3.715-.379.507.507 0 0 0-.086.039 5.413 5.413 0 0 0-2.65 4.454c0 2.46 1.4 5.3 3.473 5.614a5.187 5.187 0 0 0 .772.059 4.147 4.147 0 0 0 2.763-1 4.691 4.691 0 0 0 1.526-2.9q2.748.39 5.267.9l-.694 3.765a.5.5 0 0 0 .444.588q.446.043.87.043a6.04 6.04 0 0 0 4.449-1.735 6.78 6.78 0 0 0 .928-1.172 40.237 40.237 0 0 1 11.732 5.419q-.012 1.546-.051 3.143c-.342 13.7-3.374 22.782-4.649 26.035a48.73 48.73 0 0 1-1.031 3.377c-2.611 7.438-6.177 13.017-7.855 13.017a.639.639 0 0 1-.415-.146c-.669-.545-.51-5.868-.153-11.366a19.327 19.327 0 0 0 5.9-3.617.5.5 0 0 0-.717-.7 18.885 18.885 0 0 1-5.594 3.4 87.371 87.371 0 0 1-19.4 2.607c-11.281 0-18.4-1.892-20.923-2.7l-.26-6.109a.5.5 0 1 0-1 .043l.243 5.714c.149 6.176.113 12.181-.562 12.73a.639.639 0 0 1-.415.146c-1.681 0-5.261-5.579-7.872-13.017a88.882 88.882 0 0 1-2.615-9.761h-.005c-2.551-10.248-4.636-21.971-4.657-22.089a.5.5 0 1 0-.984.175c.021.118 2.091 11.756 4.633 22-1.324.122-2.662.2-4.013.2a63.607 63.607 0 0 1-26.1-6.059 44.608 44.608 0 0 1 6.949-9.4c9.072-9.555 18.343-13.569 25.331-15.21a6.619 6.619 0 0 0 2.526 3.172 5.232 5.232 0 0 0 2.774.9 4.77 4.77 0 0 0 3.579-1.983.5.5 0 0 0 .052-.57l-1.467-2.574q2.151-.118 4.317-.032c1.077 3.689 5.807 4.056 5.858 4.06h.033a.5.5 0 0 0 .464-.314 8.392 8.392 0 0 0 .432-1.968h2.691a.5.5 0 1 0 0-1h-2.606a18.073 18.073 0 0 0-.152-3.239h2.437a.5.5 0 0 0 0-1h-2.593a24.74 24.74 0 0 0-.512-2.243.5.5 0 0 0-.564-.359 11.053 11.053 0 0 0-3 1.03c-1.867.989-2.77 2.376-2.658 4.025a46.151 46.151 0 0 0-4.689.068l-.048-.084 2.933-.3a.5.5 0 0 0 .449-.484c.005-.2.1-4.931-2.952-6.689-1.677-.968-11.4-4.872-11.818-5.037a.5.5 0 0 0-.372.928c.1.041 10.074 4.043 11.69 4.976 2.05 1.183 2.388 4.223 2.442 5.359l-3.237.333a.5.5 0 0 0-.383.745l2.271 3.988c-1.076 1.186-2.506 2.067-4.831.615-3.425-2.138-3.069-8.563-3.019-9.262 0-.022.005-.044 0-.066v-.007a.5.5 0 0 0-.052-.266.985.985 0 0 0-.548-.63c-.037-.016-3.859-1.7-7.521-3.205-3.062-1.261-3.5-3.464-3.524-3.584a1 1 0 0 0-1.975.313c.021.138.568 3.4 4.738 5.121 3.645 1.5 7.452 3.176 7.488 3.191.056.024.2.091.35.158a17.069 17.069 0 0 0 .664 4.966c-7.122 1.693-16.53 5.791-25.719 15.47a45.518 45.518 0 0 0-7.118 9.64c-3.625-1.887-5.88-3.8-5.88-5.166 0-2.882 1.96-4.065 5.218-6.422l.3-3.752a1.306 1.306 0 0 1 1.293-1.2h.11l2.534.2a15.544 15.544 0 0 1-1.922 1.463.5.5 0 0 0 .545.839 15.693 15.693 0 0 0 2.715-2.2h.058a51.394 51.394 0 0 0 4.691-5.189l-1.394-4.541a1.305 1.305 0 0 1 .864-1.63l4.352-1.336-1.57 2.34a.5.5 0 1 0 .83.558l2.256-3.363.111-.034a59.576 59.576 0 0 0 3-6.032l-2.884-4.76a1.305 1.305 0 0 1 .439-1.792l4.593-2.784-.9 2.5a.5.5 0 0 0 .941.338l1.316-3.665.051-.031c.038-.137.069-.27.106-.406l.078-.218a.5.5 0 0 0 .027-.185 105.18 105.18 0 0 0 2.119-9.789c0-9.717.779-18.179 2.627-23.484l.683 1.709a.5.5 0 0 0 .929-.371l-1.144-2.862-.308-.986 1.083-.338a39.766 39.766 0 0 1 6.223-8.725 12.852 12.852 0 0 0 .241 1.72.5.5 0 0 0 .629.315.5.5 0 0 0 .32-.631 24.71 24.71 0 0 1-.287-2.484l.084-3.613 3.884.09a63.833 63.833 0 0 1 10.4-7l1.521-5.561 5.138 1.405-2.527 1.118a.5.5 0 1 0 .409.912l3.7-1.659a44.04 44.04 0 0 1 14.851-3.124q.8 0 1.545.052c7.754.535 20.609 10.512 30.783 20.521a.655.655 0 0 1 .465-.193.639.639 0 0 1 .442.176m0 0a78.848 78.848 0 0 1 8.484 9.535m-14.79 61.935a42.035 42.035 0 0 0-11.248-5.1 15.357 15.357 0 0 0 1.207-6.3.955.955 0 0 0 .1.016.989.989 0 0 0 .292-.044l9.149-2.784c.288 4.005.511 8.839.5 14.208z"/>
+    <path d="M193.225 139.669a1.412 1.412 0 0 0 1.41-1.41V135.1a1.41 1.41 0 0 0-2.821 0v3.162a1.412 1.412 0 0 0 1.411 1.407z"/>
+    <path d="M193.225 144.475a5.736 5.736 0 0 0 4.644-2.413 1 1 0 1 0-1.679-1.088 3.76 3.76 0 0 1-2.965 1.5 3.71 3.71 0 0 1-2.969-1.506 1 1 0 0 0-1.675 1.094 5.736 5.736 0 0 0 4.644 2.413z"/>
+    <path d="M163.852 142.719a1 1 0 0 0 1.674-1.094 5.674 5.674 0 0 0-9.286 0 1 1 0 0 0 1.674 1.095 3.679 3.679 0 0 1 5.938 0z"/>
+    <path d="M159.473 135.1v3.162c0 .036.008.07.011.106a6.569 6.569 0 0 1 2.8 0c0-.036.011-.07.011-.106V135.1a1.41 1.41 0 1 0-2.821 0z"/>
+    <path d="M160.912 130.086a5.733 5.733 0 0 0 4.643-2.412 1 1 0 0 0-1.674-1.095 3.679 3.679 0 0 1-5.937 0 1 1 0 0 0-1.674 1.094 5.733 5.733 0 0 0 4.642 2.413z"/>
+    <path d="M155.934 155.646a18.515 18.515 0 0 0-4.51-8.323c-5.144-5.145-12.507-4.867-12.817-4.857a1 1 0 0 0 .089 2 16.68 16.68 0 0 1 3.159.279 18.431 18.431 0 0 0 1.1 3.47 6.118 6.118 0 0 0-1.562.513 7.174 7.174 0 0 0-2.41 2.27.5.5 0 1 0 .84.542 6.254 6.254 0 0 1 2.014-1.917 5.319 5.319 0 0 1 1.6-.478 4.113 4.113 0 0 0 .928 1.091 6.669 6.669 0 0 0-.955 2.347 7.171 7.171 0 0 0 .372 3.29.5.5 0 0 0 .469.326.494.494 0 0 0 .174-.031.5.5 0 0 0 .295-.643 6.214 6.214 0 0 1-.326-2.76 5.734 5.734 0 0 1 .894-2.1 2.673 2.673 0 0 0 .474.054 9.686 9.686 0 0 0 4.458-1.746 16.947 16.947 0 0 1 3.75 7.1 1 1 0 0 0 1.956-.42zm-10.821-6.082c-1.027-.642-1.8-3.029-2.178-4.592a15.045 15.045 0 0 1 6.547 3.285c-1.517.943-3.507 1.843-4.37 1.307z"/>
+    <path d="M193.839 116.06a1 1 0 0 0 .71-.3l2.533-2.554a1 1 0 1 0-1.42-1.408l-2.533 2.554a1 1 0 0 0 .71 1.7z"/>
+    <path d="M199.708 117.691a1 1 0 0 0 .409-.088l3.512-1.576a1 1 0 0 0-.818-1.825l-3.512 1.576a1 1 0 0 0 .41 1.913z"/>
+    <path d="M193.882 183.558a.5.5 0 0 0 .106.988.487.487 0 0 0 .105-.011 5.176 5.176 0 0 0 3-7.9.5.5 0 0 0-.807.591 4.178 4.178 0 0 1-2.41 6.327z"/>
+    <path d="M195.435 187.387a.487.487 0 0 0 .105-.011 7.781 7.781 0 0 0 4.512-11.856.5.5 0 1 0-.807.591 6.884 6.884 0 0 1 .676 6.146 6.8 6.8 0 0 1-4.593 4.142.5.5 0 0 0 .105.989z"/>
+    <path d="M127.087 198.745a5.134 5.134 0 0 0 1.54.239 5.254 5.254 0 0 0 3.143-1.063.5.5 0 0 0-.637-.771 4.178 4.178 0 0 1-6.463-2.021.5.5 0 0 0-.962.271 5.206 5.206 0 0 0 3.379 3.345z"/>
+    <path d="M132.43 200.03a6.783 6.783 0 0 1-10.508-3.284.5.5 0 0 0-.963.27 7.773 7.773 0 0 0 12.108 3.785.5.5 0 0 0-.638-.771z"/>
+  </g>
+  <g fill="#f9f9fa">
+    <path d="M160.366 188.859a.5.5 0 0 1-.43-.754l1.345-2.283a.5.5 0 1 1 .861.508l-1.345 2.283a.5.5 0 0 1-.431.246z"/>
+    <path d="M161.086 192.527a.5.5 0 0 1-.43-.754l1.344-2.282a.5.5 0 1 1 .861.508l-1.345 2.282a.5.5 0 0 1-.43.246z"/>
+  </g>
+</svg>
--- a/browser/base/content/test/general/browser_compacttheme.js
+++ b/browser/base/content/test/general/browser_compacttheme.js
@@ -48,17 +48,17 @@ add_task(async function startTests() {
   ok(!CompactTheme.isStyleSheetEnabled, "There is no compact style sheet when no lw theme is applied.");
 });
 
 function dummyLightweightTheme(id) {
   return {
     id,
     name: id,
     headerURL: "resource:///chrome/browser/content/browser/defaultthemes/compact.header.png",
-    iconURL: "resource:///chrome/browser/content/browser/defaultthemes/compactlight.icon.svg",
+    iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
     textcolor: "red",
     accentcolor: "blue"
   };
 }
 
 add_task(async function testLightweightThemePreview() {
   if (SKIP_TEST) {
     ok(true, "No need to run this test since themes aren't installed");
--- a/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
+++ b/browser/base/content/test/performance/browser_urlbar_keyed_search_reflows.js
@@ -18,19 +18,16 @@ requestLongerTimeout(5);
 const EXPECTED_REFLOWS_FIRST_OPEN = [
   // Bug 1357054
   {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
       "_onChanged@chrome://global/content/bindings/autocomplete.xml",
       "_appendCurrentResult/<@chrome://global/content/bindings/autocomplete.xml",
-      "setTimeout handler*_appendCurrentResult@chrome://global/content/bindings/autocomplete.xml",
-      "_invalidate@chrome://global/content/bindings/autocomplete.xml",
-      "invalidate@chrome://global/content/bindings/autocomplete.xml"
     ],
     times: 18, // This number should only ever go down - never up.
   },
 
   {
     stack: [
       "_rebuild@chrome://browser/content/search/search.xml",
       "set_popup@chrome://browser/content/search/search.xml",
@@ -62,18 +59,16 @@ const EXPECTED_REFLOWS_FIRST_OPEN = [
     ],
     times: 6, // This number should only ever go down - never up.
   },
 
   {
     stack: [
       "adjustHeight@chrome://global/content/bindings/autocomplete.xml",
       "_invalidate/this._adjustHeightTimeout<@chrome://global/content/bindings/autocomplete.xml",
-      "setTimeout handler*_invalidate@chrome://global/content/bindings/autocomplete.xml",
-      "invalidate@chrome://global/content/bindings/autocomplete.xml",
     ],
     times: 51, // This number should only ever go down - never up.
   },
 
   {
     stack: [
       "_handleOverflow@chrome://global/content/bindings/autocomplete.xml",
       "handleOverUnderflow@chrome://global/content/bindings/autocomplete.xml",
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -34,16 +34,17 @@ browser.jar:
         content/browser/abouthome/bookmarks@2x.png     (content/abouthome/bookmarks@2x.png)
         content/browser/abouthome/history@2x.png       (content/abouthome/history@2x.png)
         content/browser/abouthome/addons@2x.png        (content/abouthome/addons@2x.png)
         content/browser/abouthome/sync@2x.png          (content/abouthome/sync@2x.png)
         content/browser/abouthome/settings@2x.png      (content/abouthome/settings@2x.png)
         content/browser/abouthome/restore@2x.png       (content/abouthome/restore@2x.png)
         content/browser/abouthome/restore-large@2x.png (content/abouthome/restore-large@2x.png)
 
+        content/browser/illustrations/error-connection-failure.svg (content/illustrations/error-connection-failure.svg)
         content/browser/illustrations/error-server-not-found.svg (content/illustrations/error-server-not-found.svg)
         content/browser/illustrations/error-malformed-url.svg (content/illustrations/error-malformed-url.svg)
         content/browser/aboutNetError.xhtml            (content/aboutNetError.xhtml)
 
 #ifdef MOZ_SERVICES_HEALTHREPORT
         content/browser/abouthealthreport/abouthealth.xhtml   (content/abouthealthreport/abouthealth.xhtml)
         content/browser/abouthealthreport/abouthealth.js      (content/abouthealthreport/abouthealth.js)
         content/browser/abouthealthreport/abouthealth.css     (content/abouthealthreport/abouthealth.css)
@@ -105,18 +106,18 @@ browser.jar:
         content/browser/defaultthemes/3.preview.png   (content/defaultthemes/3.preview.png)
         content/browser/defaultthemes/4.header.png    (content/defaultthemes/4.header.png)
         content/browser/defaultthemes/4.icon.png      (content/defaultthemes/4.icon.png)
         content/browser/defaultthemes/4.preview.png   (content/defaultthemes/4.preview.png)
         content/browser/defaultthemes/5.header.png    (content/defaultthemes/5.header.png)
         content/browser/defaultthemes/5.icon.jpg      (content/defaultthemes/5.icon.jpg)
         content/browser/defaultthemes/5.preview.jpg   (content/defaultthemes/5.preview.jpg)
         content/browser/defaultthemes/compact.header.png    (content/defaultthemes/compact.header.png)
-        content/browser/defaultthemes/compactdark.icon.svg  (content/defaultthemes/compactdark.icon.svg)
-        content/browser/defaultthemes/compactlight.icon.svg (content/defaultthemes/compactlight.icon.svg)
+        content/browser/defaultthemes/dark.icon.svg  (content/defaultthemes/dark.icon.svg)
+        content/browser/defaultthemes/light.icon.svg (content/defaultthemes/light.icon.svg)
         content/browser/newtab/newTab.xhtml           (content/newtab/newTab.xhtml)
 *       content/browser/newtab/newTab.js              (content/newtab/newTab.js)
         content/browser/newtab/newTab.css             (content/newtab/newTab.css)
         content/browser/newtab/alternativeDefaultSites.json   (content/newtab/alternativeDefaultSites.json)
 *       content/browser/pageinfo/pageInfo.xul         (content/pageinfo/pageInfo.xul)
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
--- a/browser/branding/official/content/about-wordmark.svg
+++ b/browser/branding/official/content/about-wordmark.svg
@@ -1,8 +1,13 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="142" height="38" viewBox="0 0 142 38">
+<svg xmlns="http://www.w3.org/2000/svg" width="338" height="42" viewBox="0 0 338 42">
   <path d="M136.837 10.212H131.5l-5.7 9.9-5.645-9.9h-5.542l8.364 12.778-9.437 14.265h5.337l6.723-11.443 6.62 11.444h5.7l-9.391-14.471zM22.844 37.255h4.721V10.212h-4.721zm15.42-21.339l-.462-5.7h-4.054v27.039h4.721V23.306c0-4.205 3.079-8.878 6.466-8.878a8.485 8.485 0 0 1 2.36.308l.872-4.618A11.5 11.5 0 0 0 45.5 9.81c-3.284 0-5.8 2.053-7.236 6.106zM0 37.255h4.875V21.707h11.546v-3.849H4.875V5.8h13.342l.564-3.9H0zM25.153.163A3.14 3.14 0 0 0 21.869 3.4a3.128 3.128 0 0 0 3.284 3.182A3.143 3.143 0 0 0 28.489 3.4 3.154 3.154 0 0 0 25.153.163zm76.491 9.647c-7.7 0-12.111 5.585-12.111 13.949 0 8.57 4.362 14.112 12.059 14.112 7.646 0 12.059-5.8 12.059-14.163.001-8.57-4.309-13.898-12.007-13.898zm-.051 24.264c-4.516 0-6.979-3.284-6.979-10.315 0-7.082 2.515-10.152 7.03-10.152 4.464 0 6.928 3.07 6.928 10.1 0 7.083-2.463 10.367-6.979 10.367zM82.47 8.339c0-2.617 1.026-4.541 4-4.541a11.567 11.567 0 0 1 4.721 1.026l1.488-3.438A14.913 14.913 0 0 0 86.216 0c-5.491 0-8.467 3.447-8.467 7.963v2.249h-4.824v3.643h4.824v23.4h4.721v-23.4h6.055l.513-3.643H82.47zM59.952 9.81c-6.979 0-11.238 5.79-11.238 14.206 0 8.57 4.413 13.855 11.957 13.855a14.741 14.741 0 0 0 9.442-3.387l-2.053-2.822a11.384 11.384 0 0 1-7.03 2.361c-3.9 0-6.825-2.412-7.287-8.672h17.242c.051-.616.1-1.488.1-2.412.003-8.263-3.846-13.129-11.133-13.129zm6.466 12.051H53.743c.359-6 2.72-8.3 6.312-8.3 4.259 0 6.363 2.711 6.363 8z" fill="#fff"/>
-  <path d="M139.854 10.228a2.134 2.134 0 0 0-2.128 2.155 2.093 2.093 0 0 0 2.128 2.12 2.137 2.137 0 1 0 0-4.275zm0 3.954a1.755 1.755 0 0 1-1.772-1.8 1.783 1.783 0 0 1 1.772-1.826 1.761 1.761 0 0 1 1.79 1.808 1.779 1.779 0 0 1-1.79 1.819zm.891-2.333c0-.481-.33-.721-1.015-.721h-.614v2.422h.374v-.988h.321l.579.988h.445l-.659-1.051a.659.659 0 0 0 .568-.65zm-1.256.4v-.81h.294c.374 0 .579.1.579.41 0 .294-.2.4-.525.4z" fill="#fff"/>
+  <path d="M182.953 39.384L180.9 42c-3.8-3.386-7.284-4.309-13.542-4.309-8.464 0-14.312-6.258-14.312-17.954 0-11.593 5.9-18.21 14.312-18.21 8.566 0 14.363 6.309 14.363 18.159 0 9.644-3.693 14.414-8.874 16.312a16.2 16.2 0 0 1 10.106 3.386zm-15.594-4.206c6.874 0 11.131-4.924 11.131-15.491 0-10.67-4.36-15.543-11.131-15.543-6.617 0-11.08 4.924-11.08 15.594 0 10.515 4.566 15.44 11.08 15.44z" fill="#fff"/>
+  <path d="M206.037 37.229h-2.565l-.154-4.771c-1.9 3.18-4.617 5.335-8.772 5.335-4.822 0-7.592-2.924-7.592-8.207V10.4h2.975v18.879c0 4.258 1.8 6.053 5.283 6.053 3.539 0 6-2.308 7.848-5.489V10.4h2.975z" fill="#fff"/>
+  <path d="M232.505 35.691l-.667 2.1a4.731 4.731 0 0 1-4.155-4.463 9.45 9.45 0 0 1-8.361 4.463c-5.181 0-8.31-3.232-8.31-8 0-5.643 4.258-8.669 11.131-8.669h5.13v-2.615c0-4.258-1.744-6.155-6.207-6.155a21.849 21.849 0 0 0-7.284 1.539l-.769-2.257a22.421 22.421 0 0 1 8.464-1.8c6.1 0 8.772 3.026 8.772 8.515v12.93c-.001 2.975.82 3.847 2.256 4.412zm-5.232-5.13v-7.284h-4.668c-5.386 0-8.413 2.1-8.413 6.361 0 3.693 2.1 5.745 5.643 5.745 3.437 0 5.797-1.693 7.438-4.822z" fill="#fff"/>
+  <path d="M256.82 18.045v19.184h-2.975V18.455c0-4.411-1.8-6.207-5.078-6.207-3.642 0-6.1 2.257-8.31 5.54v19.441h-2.975V10.4h2.565l.256 4.565c2.154-3.026 5.13-5.13 9.028-5.13 4.869.003 7.489 3.029 7.489 8.21z" fill="#fff"/>
+  <path d="M276.312 36.1a10.026 10.026 0 0 1-5.643 1.693c-4.052 0-6.566-2.411-6.566-7.079v-17.9h-4.513V10.4h4.51V3.99l2.975-.359V10.4H274l-.359 2.411h-6.566v17.75c0 3.129 1.231 4.668 3.95 4.668a7.523 7.523 0 0 0 4.1-1.231z" fill="#fff"/>
+  <path d="M297.856 37.229h-2.565l-.154-4.771c-1.9 3.18-4.617 5.335-8.772 5.335-4.822 0-7.592-2.924-7.592-8.207V10.4h2.975v18.879c0 4.258 1.8 6.053 5.284 6.053 3.539 0 6-2.308 7.848-5.489V10.4h2.975z" fill="#fff"/>
+  <path d="M337.251 18.045v19.184h-2.975V18.455c0-4.36-1.8-6.207-4.565-6.207-3.232 0-5.489 2.257-7.54 5.54v19.441H319.2V18.455c0-4.36-1.8-6.207-4.565-6.207-3.232 0-5.54 2.257-7.541 5.54v19.441h-2.975V10.4h2.565l.256 4.514c2-3.026 4.771-5.078 8.259-5.078a6.432 6.432 0 0 1 6.617 5.335c2.1-3.18 4.873-5.335 8.464-5.335 4.304.002 6.971 3.131 6.971 8.209z" fill="#fff"/>
 </svg>
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -4,17 +4,16 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["CustomizeMode"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
-const kPrefCustomizationAnimation = "browser.uiCustomization.disableAnimation";
 const kPaletteId = "customization-palette";
 const kDragDataTypePrefix = "text/toolbarwrapper-id/";
 const kSkipSourceNodePref = "browser.uiCustomization.skipSourceNodeCheck";
 const kToolbarVisibilityBtn = "customization-toolbar-visibility-button";
 const kDrawInTitlebarPref = "browser.tabs.drawInTitlebar";
 const kExtraDragSpacePref = "browser.tabs.extraDragSpace";
 const kMaxTransitionDurationMs = 2000;
 const kKeepBroadcastAttributes = "keepbroadcastattributeswhencustomizing";
@@ -54,18 +53,16 @@ XPCOMUtils.defineLazyGetter(this, "log",
   gDebug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
   let consoleOptions = {
     maxLogLevel: gDebug ? "all" : "log",
     prefix: "CustomizeMode",
   };
   return new scope.ConsoleAPI(consoleOptions);
 });
 
-var gDisableAnimation = null;
-
 var gDraggingInToolbars;
 
 var gTab;
 
 function closeGlobalTab() {
   let win = gTab.ownerGlobal;
   if (win.gBrowser.browsers.length == 1) {
     win.BrowserOpenTab();
@@ -91,32 +88,27 @@ function unregisterGlobalTab() {
   win.gBrowser.removeTabsProgressListener(gTabsProgressListener);
 
   gTab.removeAttribute("customizemode");
 
   gTab = null;
 }
 
 function CustomizeMode(aWindow) {
-  if (gDisableAnimation === null) {
-    gDisableAnimation = Services.prefs.getPrefType(kPrefCustomizationAnimation) == Ci.nsIPrefBranch.PREF_BOOL &&
-                        Services.prefs.getBoolPref(kPrefCustomizationAnimation);
-  }
   this.window = aWindow;
   this.document = aWindow.document;
   this.browser = aWindow.gBrowser;
   this.areas = new Set();
 
   // There are two palettes - there's the palette that can be overlayed with
   // toolbar items in browser.xul. This is invisible, and never seen by the
   // user. Then there's the visible palette, which gets populated and displayed
   // to the user when in customizing mode.
   this.visiblePalette = this.document.getElementById(kPaletteId);
   this.paletteEmptyNotice = this.document.getElementById("customization-empty");
-  this.tipPanel = this.document.getElementById("customization-tipPanel");
   if (Services.prefs.getCharPref("general.skins.selectedSkin") != "classic/1.0") {
     let lwthemeButton = this.document.getElementById("customization-lwtheme-button");
     lwthemeButton.setAttribute("hidden", "true");
   }
   if (AppConstants.CAN_DRAW_IN_TITLEBAR) {
     this._updateTitlebarCheckbox();
     this._updateDragSpaceCheckbox();
     Services.prefs.addObserver(kDrawInTitlebarPref, this);
@@ -394,18 +386,16 @@ CustomizeMode.prototype = {
     }
 
     if (this.resetting) {
       log.debug("Attempted to exit while we're resetting. " +
                 "We'll exit after resetting has finished.");
       return;
     }
 
-    this.hideTip();
-
     this._handler.isExitingCustomizeMode = true;
 
     this._removeExtraToolbarsIfEmpty();
 
     this._teardownDownloadAutoHideToggle();
 
     CustomizableUI.removeListener(this);
 
@@ -551,54 +541,16 @@ CustomizeMode.prototype = {
 
     let deck = this.document.getElementById("tab-view-deck");
     let toolboxRect = this.window.gNavToolbox.getBoundingClientRect();
     let height = toolboxRect.bottom;
     deck.style.setProperty("--toolbox-rect-height", `${height}`);
     deck.style.setProperty("--toolbox-rect-height-with-unit", `${height}px`);
   },
 
-  maybeShowTip(aAnchor) {
-    const kShownPref = "browser.customizemode.tip0.shown";
-    let shown = Services.prefs.getBoolPref(kShownPref, false);
-    if (shown)
-      return;
-
-    let anchorNode = aAnchor || this.document.getElementById("customization-panelHolder");
-    let messageNode = this.tipPanel.querySelector(".customization-tipPanel-contentMessage");
-    if (!messageNode.childElementCount) {
-      // Put the tip contents in the popup.
-      let bundle = this.document.getElementById("bundle_browser");
-      const kLabelClass = "customization-tipPanel-link";
-      // eslint-disable-next-line no-unsanitized/property
-      messageNode.innerHTML = bundle.getFormattedString("customizeTips.tip0", [
-        "<label class=\"customization-tipPanel-em\" value=\"" +
-          bundle.getString("customizeTips.tip0.hint") + "\"/>",
-        this.document.getElementById("bundle_brand").getString("brandShortName"),
-        "<label class=\"" + kLabelClass + " text-link\" value=\"" +
-        bundle.getString("customizeTips.tip0.learnMore") + "\"/>"
-      ]);
-
-      messageNode.querySelector("." + kLabelClass).addEventListener("click", () => {
-        let url = Services.urlFormatter.formatURLPref("browser.customizemode.tip0.learnMoreUrl");
-        let browser = this.browser;
-        browser.selectedTab = browser.addTab(url);
-        this.hideTip();
-      });
-    }
-
-    this.tipPanel.hidden = false;
-    this.tipPanel.openPopup(anchorNode);
-    Services.prefs.setBoolPref(kShownPref, true);
-  },
-
-  hideTip() {
-    this.tipPanel.hidePopup();
-  },
-
   _getCustomizableChildForNode(aNode) {
     // NB: adjusted from _getCustomizableParent to keep that method fast
     // (it's used during drags), and avoid multiple DOM loops
     let areas = CustomizableUI.areas;
     // Caching this length is important because otherwise we'll also iterate
     // over items we add to the end from within the loop.
     let numberOfAreas = areas.length;
     for (let i = 0; i < numberOfAreas; i++) {
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -374,35 +374,16 @@
     <menuseparator/>
     <menuitem command="cmd_CustomizeToolbars"
               class="viewCustomizeToolbar"
               accesskey="&viewCustomizeToolbar.accesskey;"
               label="&viewCustomizeToolbar.label;"/>
   </menupopup>
 </panel>
 
-<panel id="customization-tipPanel"
-       type="arrow"
-       flip="none"
-       side="left"
-       position="leftcenter topright"
-       noautohide="true"
-       hidden="true">
-  <hbox class="customization-tipPanel-wrapper">
-    <vbox class="customization-tipPanel-infoBox"/>
-    <vbox class="customization-tipPanel-content" flex="1">
-      <description class="customization-tipPanel-contentMessage"/>
-      <image class="customization-tipPanel-contentImage"/>
-    </vbox>
-    <vbox pack="start" align="end" class="customization-tipPanel-closeBox">
-      <toolbarbutton oncommand="gCustomizeMode.hideTip()" class="close-icon"/>
-    </vbox>
-  </hbox>
-</panel>
-
 <panel id="panic-button-success-notification"
        type="arrow"
        position="bottomcenter topright"
        hidden="true"
        role="alert"
        orient="vertical">
   <hbox id="panic-button-success-header">
     <image id="panic-button-success-icon" alt=""/>
--- a/browser/components/customizableui/test/head.js
+++ b/browser/components/customizableui/test/head.js
@@ -185,38 +185,34 @@ function getAreaWidgetIds(areaId) {
 function simulateItemDrag(aToDrag, aTarget) {
   synthesizeDrop(aToDrag.parentNode, aTarget);
 }
 
 function endCustomizing(aWindow = window) {
   if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
     return true;
   }
-  Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
   return new Promise(resolve => {
     function onCustomizationEnds() {
-      Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
       aWindow.gNavToolbox.removeEventListener("aftercustomization", onCustomizationEnds);
       resolve();
     }
     aWindow.gNavToolbox.addEventListener("aftercustomization", onCustomizationEnds);
     aWindow.gCustomizeMode.exit();
 
   });
 }
 
 function startCustomizing(aWindow = window) {
   if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
     return null;
   }
-  Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", true);
   return new Promise(resolve => {
     function onCustomizing() {
       aWindow.gNavToolbox.removeEventListener("customizationready", onCustomizing);
-      Services.prefs.setBoolPref("browser.uiCustomization.disableAnimation", false);
       resolve();
     }
     aWindow.gNavToolbox.addEventListener("customizationready", onCustomizing);
     aWindow.gCustomizeMode.enter();
   });
 }
 
 function promiseObserverNotified(aTopic) {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -608,27 +608,27 @@ BrowserGlue.prototype = {
     if (AppConstants.INSTALL_COMPACT_THEMES) {
       let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");
 
       LightweightThemeManager.addBuiltInTheme({
         id: "firefox-compact-light@mozilla.org",
         name: gBrowserBundle.GetStringFromName("lightTheme.name"),
         description: gBrowserBundle.GetStringFromName("lightTheme.description"),
         headerURL: "resource:///chrome/browser/content/browser/defaultthemes/compact.header.png",
-        iconURL: "resource:///chrome/browser/content/browser/defaultthemes/compactlight.icon.svg",
+        iconURL: "resource:///chrome/browser/content/browser/defaultthemes/light.icon.svg",
         textcolor: "black",
         accentcolor: "white",
         author: vendorShortName,
       });
       LightweightThemeManager.addBuiltInTheme({
         id: "firefox-compact-dark@mozilla.org",
         name: gBrowserBundle.GetStringFromName("darkTheme.name"),
         description: gBrowserBundle.GetStringFromName("darkTheme.description"),
         headerURL: "resource:///chrome/browser/content/browser/defaultthemes/compact.header.png",
-        iconURL: "resource:///chrome/browser/content/browser/defaultthemes/compactdark.icon.svg",
+        iconURL: "resource:///chrome/browser/content/browser/defaultthemes/dark.icon.svg",
         textcolor: "white",
         accentcolor: "black",
         author: vendorShortName,
       });
     }
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete");
   },
@@ -1682,17 +1682,17 @@ BrowserGlue.prototype = {
         return;
       this._openPreferences("sync", { origin: "doorhanger" });
     }
     this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
   },
 
   // eslint-disable-next-line complexity
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 54;
+    const UI_VERSION = 56;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
 
     let currentUIVersion;
     if (Services.prefs.prefHasUserValue("browser.migration.version")) {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } else {
       // This is a new profile, nothing to migrate.
       Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
@@ -2012,24 +2012,16 @@ BrowserGlue.prototype = {
           // In this case just fallback to the safest side and disable suggestions.
           Services.prefs.setBoolPref("browser.urlbar.suggest.searches", false);
         }
       } catch (ex) {
         // A missing pref is not a fatal error.
       }
     }
 
-    if (currentUIVersion < 48) {
-      // Bug 1372954 - the checked value was persisted but the attribute removal wouldn't
-      // be persisted (Bug 15232). Turns out we can just not persist the value in this case.
-      // The situation was only happening for a few nightlies in 56, so this migration can
-      // be removed in version 58.
-      xulStore.removeValue(BROWSER_DOCURL, "sidebar-box", "checked");
-    }
-
     if (currentUIVersion < 49) {
       // Annotate that a user haven't seen any onboarding tour
       Services.prefs.setIntPref("browser.onboarding.seen-tourset-version", 0);
     }
 
     if (currentUIVersion < 50) {
       try {
         // Transform prefs related to old DevTools Console.
@@ -2082,16 +2074,31 @@ BrowserGlue.prototype = {
       // Migrate browser.onboarding.hidden to browser.onboarding.state.
       if (Services.prefs.prefHasUserValue("browser.onboarding.hidden")) {
         let state = Services.prefs.getBoolPref("browser.onboarding.hidden") ? "watermark" : "default";
         Services.prefs.setStringPref("browser.onboarding.state", state);
         Services.prefs.clearUserPref("browser.onboarding.hidden");
       }
     }
 
+    if (currentUIVersion < 55) {
+      Services.prefs.clearUserPref("browser.customizemode.tip0.shown");
+    }
+
+    if (currentUIVersion < 56) {
+      // Prior to the end of the Firefox 57 cycle, the sidebarcommand being present
+      // or not was the only thing that distinguished whether the sidebar was open.
+      // Now, the sidebarcommand always indicates the last opened sidebar, and we
+      // correctly persist the checked attribute to indicate whether or not the
+      // sidebar was open. We should set the checked attribute in case it wasn't:
+      if (xulStore.getValue(BROWSER_DOCURL, "sidebar-box", "sidebarcommand")) {
+        xulStore.setValue(BROWSER_DOCURL, "sidebar-box", "checked", "true");
+      }
+    }
+
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   _checkForDefaultBrowser() {
     // Perform default browser checking.
     if (!ShellService) {
       return;
--- a/browser/components/places/content/bookmarksPanel.xul
+++ b/browser/components/places/content/bookmarksPanel.xul
@@ -26,17 +26,17 @@
   <commandset id="editMenuCommands"/>
   <menupopup id="placesContext"/>
 
   <!-- Bookmarks and history tooltip -->
   <tooltip id="bhTooltip"/>
 
   <hbox id="sidebar-search-container" align="center">
     <textbox id="search-box" flex="1" type="search"
-             placeholder="&search.placeholder;"
+             placeholder="&bookmarksSearch.placeholder;"
              aria-controls="bookmarks-view"
              oncommand="searchBookmarks(this.value);"/>
   </hbox>
 
   <tree id="bookmarks-view" class="sidebar-placesTree" type="places"
         flex="1"
         hidecolumnpicker="true"
         context="placesContext"
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -885,20 +885,20 @@ PlacesController.prototype = {
       if (PlacesUtils.nodeIsTagQuery(node.parent)) {
         // This is a uri node inside a tag container.  It needs a special
         // untag transaction.
         var tagItemId = PlacesUtils.getConcreteItemId(node.parent);
         var uri = NetUtil.newURI(node.uri);
         if (PlacesUIUtils.useAsyncTransactions) {
           let tag = node.parent.title;
           if (!tag) {
-            let tagGuid = PlacesUtils.getConcreteItemGuid(node.parent);
+            let tagGuid = await PlacesUtils.promiseItemGuid(tagItemId);
             tag = (await PlacesUtils.bookmarks.fetch(tagGuid)).title;
           }
-          transactions.push(PlacesTransactions.Untag({ uri, tag }));
+          transactions.push(PlacesTransactions.Untag({ urls: [uri], tag }));
         } else {
           let txn = new PlacesUntagURITransaction(uri, [tagItemId]);
           transactions.push(txn);
         }
       } else if (PlacesUtils.nodeIsTagQuery(node) && node.parent &&
                PlacesUtils.nodeIsQuery(node.parent) &&
                PlacesUtils.asQuery(node.parent).queryOptions.resultType ==
                  Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY) {
--- a/browser/components/places/content/history-panel.xul
+++ b/browser/components/places/content/history-panel.xul
@@ -41,17 +41,17 @@
   <!-- required to overlay the context menu -->
   <menupopup id="placesContext"/>
 
   <!-- Bookmarks and history tooltip -->
   <tooltip id="bhTooltip"/>
 
   <hbox id="sidebar-search-container">
     <textbox id="search-box" flex="1" type="search"
-             placeholder="&search.placeholder;"
+             placeholder="&historySearch.placeholder;"
              aria-controls="historyTree"
              oncommand="searchHistory(this.value);"/>
     <button id="viewButton" style="min-width:0px !important;" type="menu"
             label="&view.label;" accesskey="&view.accesskey;" selectedsort="day"
             persist="selectedsort">
       <menupopup>
         <menuitem id="bydayandsite" label="&byDayAndSite.label;"
                   accesskey="&byDayAndSite.accesskey;" type="radio"
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -33,16 +33,17 @@ subsuite = clipboard
 [browser_copy_folder_tree.js]
 [browser_copy_query_without_tree.js]
 subsuite = clipboard
 [browser_drag_bookmarks_on_toolbar.js]
 [browser_forgetthissite_single.js]
 [browser_history_sidebar_search.js]
 [browser_library_batch_delete.js]
 [browser_library_commands.js]
+[browser_library_delete_bookmarks_in_tags.js]
 [browser_library_delete_tags.js]
 [browser_library_downloads.js]
 [browser_library_infoBox.js]
 [browser_library_left_pane_fixnames.js]
 [browser_library_left_pane_middleclick.js]
 [browser_library_left_pane_select_hierarchy.js]
 [browser_library_middleclick.js]
 [browser_library_move_bookmarks.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_library_delete_bookmarks_in_tags.js
@@ -0,0 +1,96 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+/**
+ *  Test deleting bookmarks from within tags.
+ */
+
+registerCleanupFunction(async function() {
+  await PlacesUtils.bookmarks.eraseEverything();
+  await PlacesTestUtils.clearHistory();
+});
+
+add_task(async function test_tags() {
+  const uris = [
+    Services.io.newURI("http://example.com/1"),
+    Services.io.newURI("http://example.com/2"),
+    Services.io.newURI("http://example.com/3"),
+  ];
+
+  let children = uris.map((uri, index, arr) => {
+    return {
+      title: `bm${index}`,
+      url: uri,
+    }
+  })
+
+  // Note: we insert the uris in reverse order, so that we end up with the
+  // display in "logical" order of bm0 at the top, and bm2 at the bottom.
+  children = children.reverse();
+
+  await PlacesUtils.bookmarks.insertTree({
+    guid: PlacesUtils.bookmarks.unfiledGuid,
+    children,
+  });
+
+  for (let i = 0; i < uris.length; i++) {
+    PlacesUtils.tagging.tagURI(uris[i], ["test"]);
+  }
+
+  let library = await promiseLibrary();
+
+  // Select and open the left pane "Bookmarks Toolbar" folder.
+  let PO = library.PlacesOrganizer;
+
+  PO.selectLeftPaneQuery("Tags");
+  let tagsNode = PO._places.selectedNode;
+  Assert.notEqual(tagsNode, null, "Should have a valid selection");
+  let tagsTitle = PlacesUtils.getString("TagsFolderTitle");
+  Assert.equal(tagsNode.title, tagsTitle,
+               "Should have selected the Tags node");
+
+  // Now select the tag.
+  PlacesUtils.asContainer(tagsNode).containerOpen = true;
+  let tag = tagsNode.getChild(0);
+  PO._places.selectNode(tag);
+  Assert.equal(PO._places.selectedNode.title, "test",
+               "Should have selected the created tag");
+
+  let ContentTree = library.ContentTree;
+
+  for (let i = 0; i < uris.length; i++) {
+    ContentTree.view.selectNode(ContentTree.view.result.root.getChild(0));
+
+    Assert.equal(ContentTree.view.selectedNode.title, `bm${i}`,
+                 `Should have selected bm${i}`);
+
+    let promiseNotification = PlacesTestUtils.waitForNotification("onItemChanged",
+      (id, property) => property == "tags");
+
+    ContentTree.view.controller.doCommand("cmd_delete");
+
+    await promiseNotification;
+
+    for (let j = 0; j < uris.length; j++) {
+      let tags = PlacesUtils.tagging.getTagsForURI(uris[j]);
+      if (j <= i) {
+        Assert.equal(tags.length, 0,
+                     `There should be no tags for the URI: ${uris[j].spec}`);
+      } else {
+        Assert.equal(tags.length, 1,
+                     `There should be one tag for the URI: ${uris[j].spec}`);
+      }
+    }
+  }
+
+  // The tag should now not exist.
+  Assert.deepEqual(PlacesUtils.tagging.getURIsForTag("test"), [],
+                   "There should be no URIs remaining for the tag");
+
+  tagsNode.containerOpen = false;
+
+  library.close();
+});
--- a/browser/components/places/tests/chrome/chrome.ini
+++ b/browser/components/places/tests/chrome/chrome.ini
@@ -6,11 +6,10 @@ support-files = head.js
 [test_bug1163447_selectItems_through_shortcut.xul]
 [test_bug427633_no_newfolder_if_noip.xul]
 [test_bug485100-change-case-loses-tag.xul]
 [test_bug549192.xul]
 [test_bug549491.xul]
 [test_bug631374_tags_selector_scroll.xul]
 [test_editBookmarkOverlay_keywords.xul]
 [test_editBookmarkOverlay_tags_liveUpdate.xul]
-[test_RecentBookmarksMenuUI.xul]
 [test_selectItems_on_nested_tree.xul]
 [test_treeview_date.xul]
deleted file mode 100644
--- a/browser/components/places/tests/chrome/test_RecentBookmarksMenuUI.xul
+++ /dev/null
@@ -1,299 +0,0 @@
-<?xml version="1.0"?>
-
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
-                 type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
-<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
-
-<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="Test RecentBookmarksMenuUI">
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js" />
-  <script type="application/javascript" src="chrome://browser/content/browser.js"/>
-  <script type="application/javascript"
-          src="chrome://browser/content/browser-places.js" />
-  <script type="application/javascript"
-          src="resource://testing-common/sinon-2.3.2.js" />
-
-  <body xmlns="http://www.w3.org/1999/xhtml" />
-
-  <menu id="fakeMenu">
-    <menuseparator id="fakePreviousSeparator"/>
-    <menuitem id="fakeRecentBookmarks"/>
-    <menuseparator id="fakeNextSeparator"/>
-  </menu>
-
-  <script type="application/javascript">
-  <![CDATA[
-    "use strict"
-
-    const sandbox = sinon.sandbox.create();
-
-    const BASE_URL = "http://example.org/browser/browser/components/places/tests/browser/";
-
-    var bmMenu = document.getElementById("fakeMenu");
-    var prevSep = document.getElementById("fakePreviousSeparator");
-    var recentBmItem = document.getElementById("fakeRecentBookmarks");
-    var nextSep = document.getElementById("fakeNextSeparator");
-
-    function simulateHideMenu() {
-      let event = new CustomEvent("popuphidden");
-      bmMenu.dispatchEvent(event);
-    }
-
-    add_task(async function setup() {
-      let children = [];
-      // Make dateAdded start in the past and advance it on a second for each bookmark,
-      // so that we can guarentee which ones get displayed in the test.
-      let dateAdded = (new Date()).getTime() - 1000;
-
-      for (let i = 0; i < 10; i++) {
-        children.push({
-          title: `bm${i}`,
-          url: `${BASE_URL}bookmark_dummy_${i}.html`,
-          dateAdded: new Date(dateAdded)
-        });
-        dateAdded += 100;
-      }
-
-      await PlacesUtils.bookmarks.insertTree({
-        guid: PlacesUtils.bookmarks.unfiledGuid,
-        children: [{
-          children,
-          title: "TestFolder",
-          type: PlacesUtils.bookmarks.TYPE_FOLDER,
-        }]
-      });
-    });
-
-    add_task(async function test_basic_contents() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      let recentItem = recentBmItem.nextSibling;
-      for (let i = 9; i >= 10 - RecentBookmarksMenuUI.MAX_RESULTS; --i) {
-        is(recentItem.getAttribute("label"), `bm${i}`,
-           "Should have the expected title from the bookmark");
-        is(recentItem.getAttribute("targetURI"), `${BASE_URL}bookmark_dummy_${i}.html`,
-           "Should have the expected targetURI from the bookmark");
-        is(recentItem.getAttribute("simulated-places-node"), "true",
-           "Should be flagged as a simulated-places-node");
-        recentItem = recentItem.nextSibling;
-      }
-
-      is(prevSep.hidden, false, "Previous separator should not be hidden");
-      is(recentBmItem.hidden, false, "The title item should not be hidden");
-      ok(nextSep, "Next separator should still be present");
-      is(nextSep.hidden, false, "Next separator should not be hidden");
-
-      // Also check that hiding the menu stops listening to items.
-      simulateHideMenu();
-
-      ok(!RecentBookmarksMenuUI._recentlyBookmarkedObserver,
-         "Remove observer should have been called");
-    });
-
-    add_task(async function test_remove_bookmark() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      let bmToRemove = await PlacesUtils.bookmarks.fetch({url: `${BASE_URL}bookmark_dummy_9.html`});
-
-      const clock = sandbox.useFakeTimers();
-
-      await PlacesUtils.bookmarks.remove(bmToRemove);
-
-      // Move the clock past the timeout to ensure any update happen.
-      clock.tick(RecentBookmarksMenuUI.ITEM_REMOVED_TIMEOUT + 1);
-      clock.restore();
-
-      let recentItem = recentBmItem.nextSibling;
-      for (let i = 8; i >= 9 - RecentBookmarksMenuUI.MAX_RESULTS; --i) {
-        is(recentItem.getAttribute("label"), `bm${i}`,
-           "Should have the expected title from the bookmark");
-        is(recentItem.getAttribute("targetURI"), `${BASE_URL}bookmark_dummy_${i}.html`,
-           "Should have the expected targetURI from the bookmark");
-        is(recentItem.getAttribute("simulated-places-node"), "true",
-           "Should be flagged as a simulated-places-node");
-        recentItem = recentItem.nextSibling;
-      }
-
-      simulateHideMenu();
-    });
-
-    add_task(async function test_remove_multiple_bookmarks() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      let bmToRemove = await PlacesUtils.bookmarks.fetch({url: `${BASE_URL}bookmark_dummy_8.html`});
-
-      const clock = sandbox.useFakeTimers();
-
-      sinon.stub(RecentBookmarksMenuUI, "_insertRecentMenuItems");
-
-      await PlacesUtils.bookmarks.remove(bmToRemove);
-
-      // Move the clock a little way and check we haven't re-pouplated.
-      clock.tick(RecentBookmarksMenuUI.ITEM_REMOVED_TIMEOUT - 10);
-
-      ok(RecentBookmarksMenuUI._insertRecentMenuItems.notCalled,
-         "should not have populated the recent bookmarks striaght away.")
-
-      bmToRemove = await PlacesUtils.bookmarks.fetch({url: `${BASE_URL}bookmark_dummy_7.html`});
-      await PlacesUtils.bookmarks.remove(bmToRemove);
-
-      // Move the clock a little way and check we haven't re-pouplated.
-      clock.tick(RecentBookmarksMenuUI.ITEM_REMOVED_TIMEOUT - 10);
-
-      ok(RecentBookmarksMenuUI._insertRecentMenuItems.notCalled,
-         "should not have populated the recent bookmarks striaght away.")
-
-      RecentBookmarksMenuUI._insertRecentMenuItems.restore();
-
-      // Move the clock past the timeout and check the menu is updated.
-      clock.tick(20);
-
-      let recentItem = recentBmItem.nextSibling;
-      for (let i = 6; i >= 7 - RecentBookmarksMenuUI.MAX_RESULTS; --i) {
-        is(recentItem.getAttribute("label"), `bm${i}`,
-           "Should have the expected title from the bookmark");
-        is(recentItem.getAttribute("targetURI"), `${BASE_URL}bookmark_dummy_${i}.html`,
-           "Should have the expected targetURI from the bookmark");
-        is(recentItem.getAttribute("simulated-places-node"), "true",
-           "Should be flagged as a simulated-places-node");
-        recentItem = recentItem.nextSibling;
-      }
-
-      clock.restore();
-
-      simulateHideMenu();
-    });
-
-    add_task(async function test_remove_non_shown_bookmark() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      let bmToRemove = await PlacesUtils.bookmarks.fetch({url: `${BASE_URL}bookmark_dummy_1.html`});
-
-      sandbox.stub(RecentBookmarksMenuUI, "_clearExistingItems");
-      sandbox.stub(RecentBookmarksMenuUI, "_insertRecentMenuItems");
-
-      const clock = sandbox.useFakeTimers();
-
-      await PlacesUtils.bookmarks.remove(bmToRemove);
-
-      // Move the clock past the timeout to ensure any update happen.
-      clock.tick(RecentBookmarksMenuUI.ITEM_REMOVED_TIMEOUT + 1);
-      clock.restore();
-
-      is(RecentBookmarksMenuUI._clearExistingItems.notCalled, true,
-         "Should not have cleared the existing items when a bookmark is removed that is not displayed.");
-      is(RecentBookmarksMenuUI._insertRecentMenuItems.notCalled, true,
-         "Should not have inserted new menu items when a bookmark is removed that is not displayed.");
-
-      sandbox.restore();
-      simulateHideMenu();
-    });
-
-    add_task(async function test_hide_recently_bookmarked() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      RecentBookmarksMenuUI.visible = false;
-
-      is(Services.prefs.getBoolPref(RecentBookmarksMenuUI.RECENTLY_BOOKMARKED_PREF), false,
-         "showRecentlyBookmarked pref should have been set to false");
-
-      is(bmMenu.children.length, 3,
-         "There should only be the original 3 items in the menu");
-      is(document.getElementById("fakePreviousSeparator").hidden, true,
-         "The previous separator should be hidden");
-      is(document.getElementById("fakeRecentBookmarks").hidden, true,
-         "The title item should be hidden");
-      is(document.getElementById("fakeNextSeparator").hidden, false,
-         "Next separator should not be hidden");
-
-      simulateHideMenu();
-    });
-
-    add_task(async function test_remove_with_recently_bookmarked_hidden() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-      RecentBookmarksMenuUI.visible = false;
-
-      is(bmMenu.children.length, 3,
-         "There should only be the original 3 items in the menu");
-
-      let bmToRemove = await PlacesUtils.bookmarks.fetch({url: `${BASE_URL}bookmark_dummy_6.html`});
-
-      sandbox.stub(RecentBookmarksMenuUI, "_clearExistingItems");
-      sandbox.stub(RecentBookmarksMenuUI, "_insertRecentMenuItems");
-
-      const clock = sandbox.useFakeTimers();
-
-      await PlacesUtils.bookmarks.remove(bmToRemove);
-
-      // Move the clock past the timeout to ensure any update happen.
-      clock.tick(RecentBookmarksMenuUI.ITEM_REMOVED_TIMEOUT + 1);
-      clock.restore();
-
-      is(bmMenu.children.length, 3,
-         "There should only be the original 3 items in the menu");
-      is(RecentBookmarksMenuUI._clearExistingItems.notCalled, true,
-         "Should not have cleared the existing items when recently bookmarked are hidden.");
-      is(RecentBookmarksMenuUI._insertRecentMenuItems.notCalled, true,
-         "Should not have inserted new menu items when recently bookmarked are hidden.");
-
-      sandbox.restore();
-      simulateHideMenu();
-    });
-
-    add_task(async function test_show_recently_bookmarked() {
-      RecentBookmarksMenuUI.init(document.getElementById("fakeRecentBookmarks"));
-
-      is(bmMenu.children.length, 3,
-         "There should only be the original 3 items in the menu");
-
-      RecentBookmarksMenuUI.visible = true;
-
-      is(bmMenu.children.length, RecentBookmarksMenuUI.MAX_RESULTS + 3,
-         "There should be MAX_RESULTS children plus the 2 separators and 1 menuitem.");
-
-      is(Services.prefs.getBoolPref(RecentBookmarksMenuUI.RECENTLY_BOOKMARKED_PREF), true,
-         "showRecentlyBookmarked pref should have been set to true");
-
-      is(document.getElementById("fakePreviousSeparator").hidden, false,
-         "The previous separator should not be hidden");
-      is(document.getElementById("fakeRecentBookmarks").hidden, false,
-         "The title item should not be hidden");
-      is(document.getElementById("fakeNextSeparator").hidden, false,
-         "Next separator should not be hidden");
-
-      simulateHideMenu();
-    });
-
-    add_task(async function cleanup() {
-      Services.prefs.clearUserPref(RecentBookmarksMenuUI.RECENTLY_BOOKMARKED_PREF);
-      await PlacesUtils.bookmarks.eraseEverything();
-    });
-  ]]>
-  </script>
-</window>
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -428,16 +428,17 @@
       <hbox id="browserContainersExtensionContent" align="center">
         <description control="disableContainersExtension" flex="1" />
         <button id="disableContainersExtension"
                 class="extension-controlled-button accessory-button"
                 label="&disableExtension.label;" />
       </hbox>
       <hbox align="center">
         <checkbox id="browserContainersCheckbox"
+                  class="tail-with-learn-more"
                   label="&browserContainersEnabled.label;"
                   accesskey="&browserContainersEnabled.accesskey;"
                   preference="privacy.userContext.enabled"
                   onsyncfrompreference="return gMainPane.readBrowserContainersCheckbox();"/>
         <label id="browserContainersLearnMore" class="learnMore text-link">
           &browserContainersLearnMore.label;
         </label>
         <spacer flex="1"/>
@@ -732,17 +733,17 @@
     <columns>
       <column flex="1"/>
       <column/>
     </columns>
     <rows id="contentRows-2">
       <row id="playDRMContentRow">
         <hbox align="center">
           <checkbox id="playDRMContent" preference="media.eme.enabled"
-                    label="&playDRMContent2.label;" accesskey="&playDRMContent2.accesskey;"/>
+                    class="tail-with-learn-more" label="&playDRMContent2.label;" accesskey="&playDRMContent2.accesskey;"/>
           <label id="playDRMContentLink" class="learnMore text-link">
             &playDRMContent.learnMore.label;
           </label>
         </hbox>
       </row>
     </rows>
   </grid>
 </groupbox>
@@ -759,20 +760,20 @@
   <label class="header-name" flex="1">&updateApplication.label;</label>
 </hbox>
 
 <!-- Update -->
 <groupbox id="updateApp" data-category="paneGeneral" hidden="true">
   <caption class="search-header" hidden="true"><label>&updateApplication.label;</label></caption>
 
   <label>&updateApplicationDescription.label;</label>
-  <hbox align="start">
+  <hbox align="center">
     <vbox flex="1">
       <description>
-        &updateApplication.version.pre;<label id="version"/>&updateApplication.version.post;
+        &updateApplication.version.pre;<label id="version" class="tail-with-learn-more" />&updateApplication.version.post;
         <label id="releasenotes" class="learnMore text-link" hidden="true">&releaseNotes.link;</label>
       </description>
       <description id="distribution" class="text-blurb" hidden="true"/>
       <description id="distributionId" class="text-blurb" hidden="true"/>
     </vbox>
 #ifdef MOZ_UPDATER
     <spacer flex="1"/>
     <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
@@ -916,16 +917,17 @@
 </hbox>
 
 <!-- Performance -->
 <groupbox id="performanceGroup" data-category="paneGeneral" hidden="true">
   <caption class="search-header" hidden="true"><label>&performance.label;</label></caption>
 
   <hbox align="center">
     <checkbox id="useRecommendedPerformanceSettings"
+              class="tail-with-learn-more"
               label="&useRecommendedPerformanceSettings2.label;"
               accesskey="&useRecommendedPerformanceSettings2.accesskey;"
               preference="browser.preferences.defaultPerformanceSettings.enabled"/>
     <label id="performanceSettingsLearnMore" class="learnMore text-link">&performanceSettingsLearnMore.label;</label>
   </hbox>
   <description class="indent tip-caption">&useRecommendedPerformanceSettings2.description;</description>
 
   <vbox id="performanceSettings" class="indent" hidden="true">
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -267,38 +267,35 @@
     </hbox>
     <label>&historyHeader.post.label;</label>
   </hbox>
   <deck id="historyPane">
     <vbox id="historyRememberPane">
       <hbox align="center" flex="1">
         <vbox flex="1">
           <description>&rememberDescription.label;</description>
-          <separator class="thin"/>
           <description>&rememberActions.pre.label;<label
           class="text-link" id="historyRememberClear"
           >&rememberActions.clearHistory.label;</label>&rememberActions.middle.label;<label
           class="text-link" id="historyRememberCookies"
           >&rememberActions.removeCookies.label;</label>&rememberActions.post.label;</description>
         </vbox>
       </hbox>
     </vbox>
     <vbox id="historyDontRememberPane">
       <hbox align="center" flex="1">
         <vbox flex="1">
           <description>&dontrememberDescription.label;</description>
-          <separator class="thin"/>
           <description>&dontrememberActions.pre.label;<label
           class="text-link" id="historyDontRememberClear"
           >&dontrememberActions.clearHistory.label;</label>&dontrememberActions.post.label;</description>
         </vbox>
       </hbox>
     </vbox>
     <vbox id="historyCustomPane">
-      <separator class="thin"/>
       <vbox>
         <checkbox id="privateBrowsingAutoStart"
                   label="&privateBrowsingPermanent2.label;"
                   accesskey="&privateBrowsingPermanent2.accesskey;"
                   preference="browser.privatebrowsing.autostart"/>
         <vbox class="indent">
           <checkbox id="rememberHistory"
                     label="&rememberHistory2.label;"
@@ -455,17 +452,17 @@
 
 <!-- Site Data -->
 <groupbox id="siteDataGroup" hidden="true" data-category="panePrivacy" data-hidden-from-search="true">
   <caption><label>&siteData.label;</label></caption>
 
   <hbox align="baseline">
     <vbox flex="1">
       <description flex="1">
-        <label id="totalSiteDataSize"></label>
+        <label id="totalSiteDataSize" class="tail-with-learn-more"></label>
         <label id="siteDataLearnMoreLink" class="learnMore text-link">&siteDataLearnMoreLink.label;</label>
       </description>
     </vbox>
     <vbox align="end">
       <button id="siteDataSettings"
               class="accessory-button"
               label="&siteDataSettings.label;"
               accesskey="&siteDataSettings.accesskey;"
@@ -492,17 +489,17 @@
         </description>
       </vbox>
       <spacer flex="1"/>
     </hbox>
     <hbox>
       <vbox id="trackingProtectionBox" flex="1" hidden="true">
         <description id="trackingProtectionDesc"
                      control="trackingProtectionRadioGroup">
-          &trackingProtection2.radioGroupLabel;
+           <label class="tail-with-learn-more">&trackingProtection2.radioGroupLabel;</label>
            <label id="trackingProtectionLearnMore" class="learnMore text-link">&trackingProtectionLearnMore.label;</label>
         </description>
         <radiogroup id="trackingProtectionRadioGroup" aria-labelledby="trackingProtectionDesc">
           <radio value="always"
                  label="&trackingProtectionAlways.label;"
                  accesskey="&trackingProtectionAlways.accesskey;"/>
           <radio value="private"
                  label="&trackingProtectionPrivate.label;"
@@ -545,17 +542,17 @@
                   label="&changeBlockList2.label;"
                   accesskey="&changeBlockList2.accesskey;"
                   preference="pref.privacy.disable_button.change_blocklist"
                   searchkeywords="&button.cancel.label; &button.ok.label;"/>
         </hbox>
       </vbox>
     </hbox>
     <vbox id="doNotTrackLearnMoreBox">
-      <label>&doNotTrack.description;<label
+      <label><label class="tail-with-learn-more">&doNotTrack.description;</label><label
       class="learnMore text-link" href="https://www.mozilla.org/dnt"
       >&doNotTrack.learnMore.label;</label></label>
       <radiogroup id="doNotTrackRadioGroup" aria-labelledby="doNotTrackDesc" preference="privacy.donottrackheader.enabled">
         <radio value="false" label="&doNotTrack.default.label;"/>
         <radio value="true" label="&doNotTrack.always.label;"/>
       </radiogroup>
     </vbox>
   </vbox>
@@ -631,17 +628,17 @@
                                   &button.ok.label;"/>
         </hbox>
       </row>
 
       <row id="notificationSettingsRow" align="center">
         <description flex="1">
           <image class="desktop-notification-icon permission-icon" />
           <separator orient="vertical" class="thin"/>
-          <label id="notificationPermissionsLabel">&notificationPermissions.label;</label>
+          <label id="notificationPermissionsLabel" class="tail-with-learn-more">&notificationPermissions.label;</label>
           <label id="notificationPermissionsLearnMore"
                  class="learnMore text-link">&notificationPermissionsLearnMore.label;</label>
         </description>
         <hbox pack="end">
           <button id="notificationSettingsButton"
                   class="accessory-button"
                   label="&notificationSettingsButton.label;"
                   accesskey="&notificationSettingsButton.accesskey;"
@@ -654,18 +651,18 @@
     </rows>
   </grid>
 
   <separator flex="1"/>
 
   <vbox id="notificationsDoNotDisturbBox" hidden="true">
     <checkbox id="notificationsDoNotDisturb" label="&notificationsDoNotDisturb.label;"
               accesskey="&notificationsDoNotDisturb.accesskey;"/>
-    <label id="notificationsDoNotDisturbDetails"
-           class="indent">&notificationsDoNotDisturbDetails.value;</label>
+    <description id="notificationsDoNotDisturbDetails"
+           class="indent tip-caption">&notificationsDoNotDisturbDetails.value;</description>
   </vbox>
 
   <hbox align="start">
     <checkbox id="popupPolicy" preference="dom.disable_open_during_load"
               label="&blockPopups.label;" accesskey="&blockPopups.accesskey;"
               onsyncfrompreference="return gPrivacyPane.updateButtons('popupPolicyButton',
                                          'dom.disable_open_during_load');"
               flex="1" />
@@ -697,23 +694,24 @@
                               &removepermission2.label;
                               &removeallpermissions2.label;
                               &button.cancel.label;
                               &button.ok.label;"/>
     </hbox>
   </hbox>
 
   <vbox id="a11yPermissionsBox">
-    <description flex="1">
-      <checkbox id="a11yPrivacyCheckbox" label="&a11yPrivacy.checkbox.label;"
+    <hbox flex="1" align="center">
+      <checkbox id="a11yPrivacyCheckbox" class="tail-with-learn-more"
+                label="&a11yPrivacy.checkbox.label;"
                 accesskey="&a11yPrivacy.checkbox.accesskey;"
                 oncommand="return gPrivacyPane.updateA11yPrefs(this.checked)"/>
       <label id="a11yLearnMoreLink" class="learnMore text-link"
               value="&a11yPrivacy.learnmore.label;"></label>
-    </description>
+    </hbox>
   </vbox>
 </groupbox>
 
 <hbox id="dataCollectionCategory"
       class="subcategory"
       hidden="true"
       data-category="panePrivacy"
       data-subcategory="reports">
@@ -722,31 +720,33 @@
 
 <!-- Firefox Data Collection and Use -->
 #ifdef MOZ_DATA_REPORTING
 <groupbox id="dataCollectionGroup" data-category="panePrivacy" data-subcategory="reports" hidden="true">
   <caption class="search-header" hidden="true"><label>&dataCollection.label;</label></caption>
 
   <vbox>
     <description>
-      &dataCollectionDesc.label;<label id="dataCollectionPrivacyNotice" class="learnMore text-link">&dataCollectionPrivacyNotice.label;</label>
+      <label class="tail-with-learn-more">&dataCollectionDesc.label;</label><label id="dataCollectionPrivacyNotice" class="learnMore text-link">&dataCollectionPrivacyNotice.label;</label>
     </description>
     <description flex="1">
       <checkbox id="submitHealthReportBox" label="&enableHealthReport2.label;"
+                class="tail-with-learn-more"
                 accesskey="&enableHealthReport2.accesskey;"/>
       <label id="FHRLearnMore"
              class="learnMore text-link">&healthReportLearnMore.label;</label>
     </description>
 #ifndef MOZ_TELEMETRY_REPORTING
     <description id="TelemetryDisabledDesc" class="indent tip-caption" control="telemetryGroup">&healthReportingDisabled.label;</description>
 #endif
   </vbox>
 #ifdef MOZ_CRASHREPORTER
   <hbox align="center">
     <checkbox id="automaticallySubmitCrashesBox"
+              class="tail-with-learn-more"
               preference="browser.crashReports.unsubmittedCheck.autoSubmit"
               label="&alwaysSubmitCrashReports1.label;"
               accesskey="&alwaysSubmitCrashReports1.accesskey;"/>
     <label id="crashReporterLearnMore"
            class="learnMore text-link">&crashReporterLearnMore.label;</label>
   </hbox>
 #endif
 </groupbox>
@@ -760,16 +760,17 @@
 </hbox>
 
 <!-- addons, forgery (phishing) UI Security -->
 <groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
   <caption><label>&browsingProtection.label;</label></caption>
   <hbox align = "center">
     <checkbox id="enableSafeBrowsing"
               label="&enableSafeBrowsing.label;"
+              class="tail-with-learn-more"
               accesskey="&enableSafeBrowsing.accesskey;" />
     <label id="enableSafeBrowsingLearnMore"
            class="learnMore text-link">&enableSafeBrowsingLearnMore.label;</label>
   </hbox>
   <vbox class="indent">
 #ifdef MOZILLA_OFFICIAL
     <checkbox id="blockDownloads"
               label="&blockDownloads.label;"
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -67,17 +67,17 @@
       </vbox>
     </groupbox>
 
     <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
       <caption><label>&oneClickSearchEngines.label;</label></caption>
       <description>&chooseWhichOneToDisplay2.label;</description>
 
       <tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
-            seltype="single">
+            seltype="single" allowunderflowscroll="true">
         <treechildren id="engineChildren" flex="1"/>
         <treecols>
           <treecol id="engineShown" type="checkbox" editable="true" sortable="false"/>
           <treecol id="engineName" flex="4" label="&engineNameColumn.label;" sortable="false"/>
           <treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"
                    sortable="false"/>
         </treecols>
       </tree>
--- a/browser/components/search/test/browser_searchbar_openpopup.js
+++ b/browser/components/search/test/browser_searchbar_openpopup.js
@@ -37,27 +37,25 @@ function synthesizeNativeMouseClick(aEle
     utils.sendNativeMouseEvent(x * scale, y * scale, mouseUp, 0, null);
   });
 }
 
 async function endCustomizing(aWindow = window) {
   if (aWindow.document.documentElement.getAttribute("customizing") != "true") {
     return true;
   }
-  await SpecialPowers.pushPrefEnv({set: [["browser.uiCustomization.disableAnimation", true]]});
   let eventPromise = BrowserTestUtils.waitForEvent(aWindow.gNavToolbox, "aftercustomization");
   aWindow.gCustomizeMode.exit();
   return eventPromise;
 }
 
 async function startCustomizing(aWindow = window) {
   if (aWindow.document.documentElement.getAttribute("customizing") == "true") {
     return true;
   }
-  await SpecialPowers.pushPrefEnv({set: [["browser.uiCustomization.disableAnimation", true]]});
   let eventPromise = BrowserTestUtils.waitForEvent(aWindow.gNavToolbox, "customizationready");
   aWindow.gCustomizeMode.enter();
   return eventPromise;
 }
 
 let searchbar;
 let textbox;
 let searchIcon;
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -860,16 +860,21 @@ var MessageQueue = {
    * Sends queued data when the remaining idle time is enough or waiting too
    * long; otherwise, request an idle time again. If the |deadline| is not
    * given, this function is going to schedule the first request.
    *
    * @param deadline (object)
    *        An IdleDeadline object passed by requestIdleCallback().
    */
   sendWhenIdle(deadline) {
+    if (!content) {
+      // The frameloader is being torn down. Nothing more to do.
+      return;
+    }
+
     if (deadline) {
       if (deadline.didTimeout || deadline.timeRemaining() > MessageQueue.NEEDED_IDLE_PERIOD_MS) {
         MessageQueue.send();
         return;
       }
     } else if (MessageQueue._idleCallbackID) {
       // Bail out if there's a pending run.
       return;
--- a/browser/components/syncedtabs/sidebar.xhtml
+++ b/browser/components/syncedtabs/sidebar.xhtml
@@ -92,17 +92,20 @@
     </template>
 
     <div class="content-container">
       <!-- the non-scrollable header -->
       <div class="content-header">
         <div class="sidebar-search-container tabs-container sync-state">
           <div class="search-box">
             <div class="textbox-input-box">
-              <input type="text" class="tabsFilter textbox-input" tabindex="1"/>
+              <input type="text"
+                     class="tabsFilter textbox-input"
+                     placeholder="&syncedTabs.sidebar.searchPlaceholder;"
+                     tabindex="1"/>
               <div class="textbox-search-icons">
                 <a class="textbox-search-clear"></a>
                 <a class="textbox-search-icon"></a>
               </div>
             </div>
           </div>
         </div>
       </div>
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -471,16 +471,33 @@ this.FormAutofillHeuristics = {
       "cc-name",
       "cc-number",
       "cc-exp-month",
       "cc-exp-year",
       "cc-exp",
     ];
     let regexps = isAutoCompleteOff ? FIELDNAMES_IGNORING_AUTOCOMPLETE_OFF : Object.keys(this.RULES);
 
+    if (!FormAutofillUtils.isAutofillCreditCardsAvailable) {
+      if (isAutoCompleteOff) {
+        if (!this._regexpListOf_CcUnavailable_AcOff) {
+          this._regexpListOf_CcUnavailable_AcOff = regexps.filter(name => !FormAutofillUtils.isCreditCardField(name));
+        }
+        regexps = this._regexpListOf_CcUnavailable_AcOff;
+      } else {
+        if (!this._regexpListOf_CcUnavailable_AcOn) {
+          this._regexpListOf_CcUnavailable_AcOn = regexps.filter(name => !FormAutofillUtils.isCreditCardField(name));
+        }
+        regexps = this._regexpListOf_CcUnavailable_AcOn;
+      }
+    }
+    if (regexps.length == 0) {
+      return null;
+    }
+
     let labelStrings;
     let getElementStrings = {};
     getElementStrings[Symbol.iterator] = function* () {
       yield element.id;
       yield element.name;
       if (!labelStrings) {
         labelStrings = [];
         let labels = LabelUtils.findLabelElements(element);
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -75,16 +75,17 @@ FormAutofillPreferences.prototype = {
     let addressAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
     let addressAutofillLearnMore = document.createElementNS(XUL_NS, "label");
     let savedAddressesBtn = document.createElementNS(XUL_NS, "button");
     // Wrappers are used to properly compute the search tooltip positions
     let savedAddressesBtnWrapper = document.createElementNS(XUL_NS, "hbox");
     let savedCreditCardsBtnWrapper = document.createElementNS(XUL_NS, "hbox");
 
     savedAddressesBtn.className = "accessory-button";
+    addressAutofillCheckbox.className = "tail-with-learn-more";
     addressAutofillLearnMore.className = "learnMore text-link";
 
     formAutofillGroup.id = "formAutofillGroup";
     addressAutofill.id = "addressAutofill";
     addressAutofillLearnMore.id = "addressAutofillLearnMore";
 
     addressAutofillLearnMore.setAttribute("value", this.bundle.GetStringFromName("learnMore"));
     addressAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableAddressAutofill"));
@@ -118,16 +119,17 @@ FormAutofillPreferences.prototype = {
 
     if (FormAutofillUtils.isAutofillCreditCardsAvailable) {
       let creditCardAutofill = document.createElementNS(XUL_NS, "hbox");
       let creditCardAutofillCheckboxGroup = document.createElementNS(XUL_NS, "description");
       let creditCardAutofillCheckbox = document.createElementNS(XUL_NS, "checkbox");
       let creditCardAutofillLearnMore = document.createElementNS(XUL_NS, "label");
       let savedCreditCardsBtn = document.createElementNS(XUL_NS, "button");
       savedCreditCardsBtn.className = "accessory-button";
+      creditCardAutofillCheckbox.className = "tail-with-learn-more";
       creditCardAutofillLearnMore.className = "learnMore text-link";
 
       creditCardAutofill.id = "creditCardAutofill";
       creditCardAutofillLearnMore.id = "creditCardAutofillLearnMore";
 
       creditCardAutofillLearnMore.setAttribute("value", this.bundle.GetStringFromName("learnMore"));
       creditCardAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("enableCreditCardAutofill"));
       savedCreditCardsBtn.setAttribute("label", this.bundle.GetStringFromName("savedCreditCards"));
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -32,17 +32,17 @@ function clickOnElement(selector) {
 
   if (!element) {
     throw new Error("Can not find the element");
   }
 
   SimpleTest.executeSoon(() => element.click());
 }
 
-async function onStorageChanged(type) {
+async function onAddressChanged(type) {
   return new Promise(resolve => {
     formFillChromeScript.addMessageListener("formautofill-storage-changed", function onChanged(data) {
       formFillChromeScript.removeMessageListener("formautofill-storage-changed", onChanged);
       is(data.data, type, `Receive ${type} storage changed event`);
       resolve();
     });
   });
 }
@@ -53,68 +53,70 @@ function checkMenuEntries(expectedValues
   let expectedLength = isFormAutofillResult ? expectedValues.length + 1 : expectedValues.length;
 
   is(actualValues.length, expectedLength, " Checking length of expected menu");
   for (let i = 0; i < expectedValues.length; i++) {
     is(actualValues[i], expectedValues[i], " Checking menu entry #" + i);
   }
 }
 
-function invokeAsyncChromeTask(message, response, payload = {}) {
+async function addAddress(address) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:AddAddress", {address});
+    formFillChromeScript.addMessageListener("FormAutofillTest:AddressAdded", function onAdded(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:AddressAdded", onAdded);
+
+      SimpleTest.requestFlakyTimeout("Ensure ProfileAutocomplete is registered");
+      setTimeout(resolve, 500);
+    });
+  });
+}
+
+async function removeAddress(guid) {
   return new Promise(resolve => {
-    formFillChromeScript.sendAsyncMessage(message, payload);
-    formFillChromeScript.addMessageListener(response, function onReceived(data) {
-      formFillChromeScript.removeMessageListener(response, onReceived);
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:RemoveAddress", {guid});
+    formFillChromeScript.addMessageListener("FormAutofillTest:AddressRemoved", function onDeleted(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:AddressRemoved", onDeleted);
+
+      resolve();
+    });
+  });
+}
+
+async function updateAddress(guid, address) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:UpdateAddress", {address, guid});
+    formFillChromeScript.addMessageListener("FormAutofillTest:AddressUpdated", function onUpdated(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:AddressUpdated", onUpdated);
+
+      resolve();
+    });
+  });
+}
+
+async function checkAddresses(expectedAddresses) {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:CheckAddresses", {expectedAddresses});
+    formFillChromeScript.addMessageListener("FormAutofillTest:areAddressesMatching", function onChecked(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:areAddressesMatching", onChecked);
 
       resolve(data);
     });
   });
 }
 
-async function addAddress(address) {
-  await invokeAsyncChromeTask("FormAutofillTest:AddAddress", "FormAutofillTest:AddressAdded", {address});
-  await sleep();
-}
-
-async function removeAddress(guid) {
-  return invokeAsyncChromeTask("FormAutofillTest:RemoveAddress", "FormAutofillTest:AddressRemoved", {guid});
-}
-
-async function updateAddress(guid, address) {
-  return invokeAsyncChromeTask("FormAutofillTest:UpdateAddress", "FormAutofillTest:AddressUpdated", {address, guid});
-}
-
-async function checkAddresses(expectedAddresses) {
-  return invokeAsyncChromeTask("FormAutofillTest:CheckAddresses", "FormAutofillTest:areAddressesMatching", {expectedAddresses});
-}
-
-async function cleanUpAddresses() {
-  return invokeAsyncChromeTask("FormAutofillTest:CleanUpAddresses", "FormAutofillTest:AddressesCleanedUp");
-}
+async function cleanUpAddress() {
+  return new Promise(resolve => {
+    formFillChromeScript.sendAsyncMessage("FormAutofillTest:CleanUpAddress", {});
+    formFillChromeScript.addMessageListener("FormAutofillTest:AddressCleanedUp", function onCleanedUp(data) {
+      formFillChromeScript.removeMessageListener("FormAutofillTest:AddressCleanedUp", onCleanedUp);
 
-async function addCreditCard(creditcard) {
-  await invokeAsyncChromeTask("FormAutofillTest:AddCreditCard", "FormAutofillTest:CreditCardAdded", {creditcard});
-  await sleep();
-}
-
-async function removeCreditCard(guid) {
-  return invokeAsyncChromeTask("FormAutofillTest:RemoveCreditCard", "FormAutofillTest:CreditCardRemoved", {guid});
-}
-
-async function checkCreditCards(expectedCreditCards) {
-  return invokeAsyncChromeTask("FormAutofillTest:CheckCreditCards", "FormAutofillTest:areCreditCardsMatching", {expectedCreditCards});
-}
-
-async function cleanUpCreditCards() {
-  return invokeAsyncChromeTask("FormAutofillTest:CleanUpCreditCards", "FormAutofillTest:CreditCardsCleanedUp");
-}
-
-async function cleanUpStorage() {
-  await cleanUpAddresses();
-  await cleanUpCreditCards();
+      resolve(data);
+    });
+  });
 }
 
 // Utils for registerPopupShownListener(in satchel_common.js) that handles dropdown popup
 // Please call "initPopupListener()" in your test and "await expectPopup()"
 // if you want to wait for dropdown menu displayed.
 function expectPopup() {
   info("expecting a popup");
   return new Promise(resolve => {
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -2,199 +2,120 @@
 /* global assert */
 /* eslint-env mozilla/frame-script */
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://formautofill/FormAutofillUtils.jsm");
-
 let {profileStorage} = Cu.import("resource://formautofill/ProfileStorage.jsm", {});
 
-const {ADDRESSES_COLLECTION_NAME, CREDITCARDS_COLLECTION_NAME} = FormAutofillUtils;
-
 var ParentUtils = {
-  async _getRecords(collectionName) {
-    return new Promise(resolve => {
-      Services.cpmm.addMessageListener("FormAutofill:Records", function getResult({data}) {
-        Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
-        resolve(data);
-      });
-      Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", {searchString: "", collectionName});
-    });
-  },
+  cleanUpAddress() {
+    Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
+      Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
+
+      let addresses = result.data;
+      Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses",
+                                     {guids: addresses.map(address => address.guid)});
 
-  async _storageChangeObserved({topic = "formautofill-storage-changed", type, times = 1}) {
-    let count = times;
+      let count = addresses.length;
+      Services.obs.addObserver(function observer(subject, topic, data) {
+        if (!--count) {
+          Services.obs.removeObserver(observer, topic);
+          sendAsyncMessage("FormAutofillTest:AddressCleanedUp");
+        }
+      }, "formautofill-storage-changed");
+    });
 
-    return new Promise(resolve => {
-      Services.obs.addObserver(function observer(subject, obsTopic, data) {
-        if (type && data != type || !!--count) {
-          return;
-        }
-
-        Services.obs.removeObserver(observer, obsTopic);
-        resolve();
-      }, topic);
-    });
+    Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", {searchString: "", collectionName: "addresses"});
   },
 
-  async _operateRecord(collectionName, type, msgData, contentMsg) {
-    let times, topic;
-
-    if (collectionName == ADDRESSES_COLLECTION_NAME) {
-      switch (type) {
-        case "add": {
-          Services.cpmm.sendAsyncMessage("FormAutofill:SaveAddress", msgData);
-          break;
-        }
-        case "update": {
-          Services.cpmm.sendAsyncMessage("FormAutofill:SaveAddress", msgData);
-          break;
-        }
-        case "remove": {
-          times = msgData.guids.length;
-          Services.cpmm.sendAsyncMessage("FormAutofill:RemoveAddresses", msgData);
-          break;
-        }
+  updateAddress(type, chromeMsg, msgData, contentMsg) {
+    Services.cpmm.sendAsyncMessage(chromeMsg, msgData);
+    Services.obs.addObserver(function observer(subject, topic, data) {
+      if (data != type) {
+        return;
       }
-    } else {
-      switch (type) {
-        case "add": {
-          const msgDataCloned = Object.assign({}, msgData);
 
-          Services.cpmm.sendAsyncMessage("FormAutofill:SaveCreditCard", msgDataCloned);
-          break;
-        }
-        case "remove": {
-          times = msgData.guids.length;
-          Services.cpmm.sendAsyncMessage("FormAutofill:RemoveCreditCards", msgData);
-          break;
-        }
-      }
-    }
-
-    await this._storageChangeObserved({type, times, topic});
-    sendAsyncMessage(contentMsg);
+      Services.obs.removeObserver(observer, topic);
+      sendAsyncMessage(contentMsg);
+    }, "formautofill-storage-changed");
   },
 
-  async operateAddress(type, msgData, contentMsg) {
-    await this._operateRecord(ADDRESSES_COLLECTION_NAME, ...arguments);
-  },
-
-  async operateCreditCard(type, msgData, contentMsg) {
-    await this._operateRecord(CREDITCARDS_COLLECTION_NAME, ...arguments);
-  },
-
-  async cleanUpAddresses() {
-    const guids = (await this._getRecords(ADDRESSES_COLLECTION_NAME)).map(record => record.guid);
-
-    await this.operateAddress("remove", {guids}, "FormAutofillTest:AddressesCleanedUp");
+  observe(subject, topic, data) {
+    assert.ok(topic === "formautofill-storage-changed");
+    sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
   },
 
-  async cleanUpCreditCards() {
-    const guids = (await this._getRecords(CREDITCARDS_COLLECTION_NAME)).map(record => record.guid);
-
-    await this.operateCreditCard("remove", {guids}, "FormAutofillTest:CreditCardsCleanedUp");
+  cleanup() {
+    Services.obs.removeObserver(this, "formautofill-storage-changed");
+    this.cleanUpAddress();
   },
 
-  async cleanup() {
-    Services.obs.removeObserver(this, "formautofill-storage-changed");
-    await this.cleanUpAddresses();
-    await this.cleanUpCreditCards();
-  },
-
-  _areRecordsMatching(recordA, recordB, collectionName) {
-    for (let field of profileStorage[collectionName].VALID_FIELDS) {
-      if (recordA[field] !== recordB[field]) {
+  areAddressesMatching(addressA, addressB) {
+    for (let field of profileStorage.addresses.VALID_FIELDS) {
+      if (addressA[field] !== addressB[field]) {
         return false;
       }
     }
     // Check the internal field if both addresses have valid value.
     for (let field of profileStorage.INTERNAL_FIELDS) {
-      if (field in recordA && field in recordB && (recordA[field] !== recordB[field])) {
+      if (field in addressA && field in addressB && (addressA[field] !== addressB[field])) {
         return false;
       }
     }
     return true;
   },
 
-  async _checkRecords(collectionName, expectedRecords) {
-    const records = await this._getRecords(collectionName);
-
-    if (records.length !== expectedRecords.length) {
-      return false;
-    }
-
-    for (let record of records) {
-      let matching = expectedRecords.some(expectedRecord => {
-        return ParentUtils._areRecordsMatching(record, expectedRecord, collectionName);
-      });
-
-      if (!matching) {
-        return false;
+  checkAddresses({expectedAddresses}) {
+    Services.cpmm.addMessageListener("FormAutofill:Records", function getResult(result) {
+      Services.cpmm.removeMessageListener("FormAutofill:Records", getResult);
+      let addresses = result.data;
+      if (addresses.length !== expectedAddresses.length) {
+        sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
+        return;
       }
-    }
 
-    return true;
-  },
-
-  async checkAddresses({expectedAddresses}) {
-    const areMatched = await this._checkRecords(ADDRESSES_COLLECTION_NAME, expectedAddresses);
-
-    sendAsyncMessage("FormAutofillTest:areAddressesMatching", areMatched);
-  },
+      for (let address of addresses) {
+        let matching = expectedAddresses.some((expectedAddress) => {
+          return ParentUtils.areAddressesMatching(address, expectedAddress);
+        });
 
-  async checkCreditCards({expectedCreditCards}) {
-    const areMatched = await this._checkRecords(CREDITCARDS_COLLECTION_NAME, expectedCreditCards);
+        if (!matching) {
+          sendAsyncMessage("FormAutofillTest:areAddressesMatching", false);
+          return;
+        }
+      }
 
-    sendAsyncMessage("FormAutofillTest:areCreditCardsMatching", areMatched);
-  },
+      sendAsyncMessage("FormAutofillTest:areAddressesMatching", true);
+    });
 
-  observe(subject, topic, data) {
-    assert.ok(topic === "formautofill-storage-changed");
-    sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
+    Services.cpmm.sendAsyncMessage("FormAutofill:GetRecords", {searchString: "", collectionName: "addresses"});
   },
 };
 
 Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
 
 addMessageListener("FormAutofillTest:AddAddress", (msg) => {
-  ParentUtils.operateAddress("add", msg, "FormAutofillTest:AddressAdded");
+  ParentUtils.updateAddress("add", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressAdded");
 });
 
 addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
-  ParentUtils.operateAddress("remove", msg, "FormAutofillTest:AddressRemoved");
+  ParentUtils.updateAddress("remove", "FormAutofill:RemoveAddress", msg, "FormAutofillTest:AddressRemoved");
 });
 
 addMessageListener("FormAutofillTest:UpdateAddress", (msg) => {
-  ParentUtils.operateAddress("update", msg, "FormAutofillTest:AddressUpdated");
+  ParentUtils.updateAddress("update", "FormAutofill:SaveAddress", msg, "FormAutofillTest:AddressUpdated");
 });
 
 addMessageListener("FormAutofillTest:CheckAddresses", (msg) => {
   ParentUtils.checkAddresses(msg);
 });
 
-addMessageListener("FormAutofillTest:CleanUpAddresses", (msg) => {
-  ParentUtils.cleanUpAddresses();
-});
-
-addMessageListener("FormAutofillTest:AddCreditCard", (msg) => {
-  ParentUtils.operateCreditCard("add", msg, "FormAutofillTest:CreditCardAdded");
-});
-
-addMessageListener("FormAutofillTest:RemoveCreditCard", (msg) => {
-  ParentUtils.operateCreditCard("remove", msg, "FormAutofillTest:CreditCardRemoved");
-});
-
-addMessageListener("FormAutofillTest:CheckCreditCards", (msg) => {
-  ParentUtils.checkCreditCards(msg);
-});
-
-addMessageListener("FormAutofillTest:CleanUpCreditCards", (msg) => {
-  ParentUtils.cleanUpCreditCards();
+addMessageListener("FormAutofillTest:CleanUpAddress", (msg) => {
+  ParentUtils.cleanUpAddress();
 });
 
 addMessageListener("cleanup", () => {
   ParentUtils.cleanup();
 });
--- a/browser/extensions/formautofill/test/mochitest/mochitest.ini
+++ b/browser/extensions/formautofill/test/mochitest/mochitest.ini
@@ -2,14 +2,11 @@
 support-files =
   ../../../../../toolkit/components/satchel/test/satchel_common.js
   ../../../../../toolkit/components/satchel/test/parent_utils.js
   formautofill_common.js
   formautofill_parent_utils.js
 
 [test_autofocus_form.html]
 [test_basic_autocomplete_form.html]
-[test_basic_creditcard_autocomplete_form.html]
-scheme=https
-skip-if = debug # Bug 1401454
 [test_formautofill_preview_highlight.html]
 [test_multiple_forms.html]
 [test_on_address_submission.html]
--- a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -112,17 +112,17 @@ add_task(async function all_saved_fields
     email: "test@test.com",
   });
 
   await setInput("#email", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(["foo@mozilla.com"], false);
 
-  await cleanUpAddresses();
+  await cleanUpAddress();
 });
 
 // Form with both history and address storage.
 add_task(async function check_menu_when_both_existed() {
   await setupAddressStorage();
 
   await setInput("#organization", "");
   doKey("down");
deleted file mode 100644
--- a/browser/extensions/formautofill/test/mochitest/test_basic_creditcard_autocomplete_form.html
+++ /dev/null
@@ -1,248 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <meta charset="utf-8">
-  <title>Test basic autofill</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
-  <script type="text/javascript" src="formautofill_common.js"></script>
-  <script type="text/javascript" src="satchel_common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-Form autofill test: simple form credit card autofill
-
-<script>
-/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SpawnTask.js */
-/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
-/* import-globals-from formautofill_common.js */
-
-"use strict";
-
-const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
-
-const MOCK_STORAGE = [{
-  "cc-name": "John Doe",
-  "cc-number": "1234567812345678",
-  "cc-exp-month": 4,
-  "cc-exp-year": 2017,
-}, {
-  "cc-name": "Timothy Berners-Lee",
-  "cc-number": "1111222233334444",
-  "cc-exp-month": 12,
-  "cc-exp-year": 2022,
-}];
-
-const reducedMockRecord = {
-  "cc-name": "John Doe",
-  "cc-number": "1234123456785678",
-};
-
-function patchRecordCCNumber(record) {
-  const ccNumber = record["cc-number"];
-  const normalizedCCNumber = "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
-  const ccNumberFmt = FormAutofillUtils.fmtMaskedCreditCardLabel(normalizedCCNumber);
-
-  return Object.assign({}, record, {ccNumberFmt});
-}
-
-function checkElementFilled(element, expectedvalue) {
-  const focusedElem = document.activeElement;
-  const promises = [];
-
-  promises.push(new Promise(resolve => {
-    element.addEventListener("input", function onInput() {
-      ok(true, "Checking " + element.name + " field fires input event");
-      resolve();
-    }, {once: true});
-  }));
-
-  // Don't expect that focused input will receive "change" event since focus never move away.
-  if (element !== focusedElem) {
-    promises.push(new Promise(resolve => {
-      element.addEventListener("change", function onChange() {
-        ok(true, "Checking " + element.name + " field fires change event");
-        is(element.value, expectedvalue, "Checking " + element.name + " field");
-        resolve();
-      }, {once: true});
-    }));
-  }
-
-  return promises;
-}
-
-function checkFormFilled(creditCard) {
-  let promises = [];
-  for (let prop in creditCard) {
-    let element = document.getElementById(prop);
-    let converted = String(creditCard[prop]); // Convert potential number to string
-
-    promises.push(...checkElementFilled(element, converted));
-  }
-  doKey("return");
-  return Promise.all(promises);
-}
-
-async function setupCreditCardStorage() {
-  await addCreditCard(MOCK_STORAGE[0]);
-  await addCreditCard(MOCK_STORAGE[1]);
-}
-
-async function setupFormHistory() {
-  await updateFormHistory([
-    {op: "add", fieldname: "cc-name", value: "John Smith"},
-    {op: "add", fieldname: "cc-exp-year", value: 2023},
-  ]);
-}
-
-initPopupListener();
-
-// Form with history only.
-add_task(async function history_only_menu_checking() {
-  await setupFormHistory();
-
-  await setInput("#cc-exp-year", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(["2023"], false);
-});
-
-// Display credit card result even if the number of fillable fields is less than the threshold.
-add_task(async function all_saved_fields_less_than_threshold() {
-  await addCreditCard(reducedMockRecord);
-
-  await setInput("#cc-name", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries([reducedMockRecord].map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-name"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-
-  await cleanUpCreditCards();
-});
-
-// Form with both history and credit card storage.
-add_task(async function check_menu_when_both_existed() {
-  await setupCreditCardStorage();
-
-  await setInput("#cc-number", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primaryAffix: cc.ccNumberFmt.affix,
-    primary: cc.ccNumberFmt.label,
-    secondary: cc["cc-name"],
-  })));
-
-  await setInput("#cc-name", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-name"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-
-  await setInput("#cc-exp-year", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-exp-year"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-
-  await setInput("#cc-exp-month", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-exp-month"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-
-  await cleanUpCreditCards();
-});
-
-// Display history search result if no matched data in credit card.
-add_task(async function check_fallback_for_mismatched_field() {
-  await addCreditCard(reducedMockRecord);
-
-  await setInput("#cc-exp-year", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(["2023"], false);
-
-  await cleanUpCreditCards();
-});
-
-// Display history search result if credit card autofill is disabled.
-add_task(async function check_search_result_for_pref_off() {
-  await setupCreditCardStorage();
-
-  await SpecialPowers.pushPrefEnv({
-    set: [["extensions.formautofill.creditCards.enabled", false]],
-  });
-
-  await setInput("#cc-name", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(["John Smith"], false);
-
-  await SpecialPowers.popPrefEnv();
-});
-
-// Autofill the credit card from dropdown menu.
-add_task(async function check_fields_after_form_autofill() {
-  await setInput("#cc-exp-year", 202);
-
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.slice(1).map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-exp-year"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-
-  doKey("down");
-  await checkFormFilled(MOCK_STORAGE[1]);
-});
-
-// Fallback to history search after autofill address.
-add_task(async function check_fallback_after_form_autofill() {
-  await setInput("#cc-name", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(["John Smith"], false);
-});
-
-// Resume form autofill once all the autofilled fileds are changed.
-add_task(async function check_form_autofill_resume() {
-  document.querySelector("#cc-name").blur();
-  document.querySelector("#form1").reset();
-
-  await setInput("#cc-name", "");
-  doKey("down");
-  await expectPopup();
-  checkMenuEntries(MOCK_STORAGE.map(patchRecordCCNumber).map(cc => JSON.stringify({
-    primary: cc["cc-name"],
-    secondary: cc.ccNumberFmt.affix + cc.ccNumberFmt.label,
-  })));
-});
-
-</script>
-
-<p id="display"></p>
-
-<div id="content">
-
-  <form id="form1">
-    <p>This is a basic form.</p>
-    <p><label>Name: <input id="cc-name" autocomplete="cc-name"></label></p>
-    <p><label>Card Number: <input id="cc-number" autocomplete="cc-number"></label></p>
-    <p><label>Expiration month: <input id="cc-exp-month" autocomplete="cc-exp-month"></label></p>
-    <p><label>Expiration year: <input id="cc-exp-year" autocomplete="cc-exp-year"></label></p>
-    <p><label>CSC: <input id="cc-csc" autocomplete="cc-csc"></label></p>
-  </form>
-</div>
-
-<pre id="test"></pre>
-</body>
-</html>
--- a/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
+++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
@@ -40,17 +40,17 @@ add_task(async function check_storage_af
 
   for (let key in TEST_ADDRESSES[0]) {
     await setInput("#" + key, TEST_ADDRESSES[0][key]);
   }
 
   clickOnElement("input[type=submit]");
 
   let expectedAddresses = TEST_ADDRESSES.slice(0, 1);
-  await onStorageChanged("add");
+  await onAddressChanged("add");
   // Check if timesUsed is set correctly
   expectedAddresses[0].timesUsed = 1;
   let matching = await checkAddresses(expectedAddresses);
   ok(matching, "Address saved as expected");
   delete expectedAddresses[0].timesUsed;
 });
 
 // Submit another new address.
@@ -62,17 +62,17 @@ add_task(async function check_storage_af
 
   clickOnElement("input[type=submit]");
 
   // The 2nd test address should be on the top since it's the last used one.
   let addressesInMenu = TEST_ADDRESSES.slice(1);
   addressesInMenu.push(TEST_ADDRESSES[0]);
 
   // let expectedAddresses = TEST_ADDRESSES.slice(0);
-  await onStorageChanged("add");
+  await onAddressChanged("add");
   let matching = await checkAddresses(TEST_ADDRESSES);
   ok(matching, "New address saved as expected");
 
   await setInput("#organization", "");
   doKey("down");
   await expectPopup();
   checkMenuEntries(addressesInMenu.map(address =>
     JSON.stringify({primary: address.organization, secondary: address["street-address"]})
@@ -88,17 +88,17 @@ add_task(async function new_address_subm
   // Add country to first address in storage
   await setInput("#country", "US");
   TEST_ADDRESSES[0].country = "US";
   clickOnElement("input[type=submit]");
 
   let expectedAddresses = TEST_ADDRESSES.slice(0);
   // Check if timesUsed is set correctly
   expectedAddresses[0].timesUsed = 2;
-  await onStorageChanged("merge");
+  await onAddressChanged("merge");
   let matching = await checkAddresses(expectedAddresses);
   ok(matching, "Address merged as expected");
   delete expectedAddresses[0].timesUsed;
 });
 
 // Submit an updated autofill address and merge.
 add_task(async function check_storage_after_form_submitted() {
   document.querySelector("form").reset();
@@ -109,17 +109,17 @@ add_task(async function check_storage_af
   await setInput("#organization", "Moz");
   doKey("down");
   await expectPopup();
   doKey("down");
   doKey("return");
   clickOnElement("input[type=submit]");
 
   let expectedAddresses = TEST_ADDRESSES.slice(0);
-  await onStorageChanged("merge");
+  await onAddressChanged("merge");
   let matching = await checkAddresses(expectedAddresses);
   ok(matching, "Updated address merged as expected");
 });
 
 </script>
 
 <div>
 
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -204,16 +204,17 @@
   padding-top: 14px;
   padding-bottom: 14px;
   margin-bottom: 9px;
   background-repeat: no-repeat;
   background-position: left 17px top 14px;
   background-size: 20px;
   font-size: 16px;
   cursor: pointer;
+  max-height: 54px;
 }
 
 #onboarding-tour-list .onboarding-tour-item:dir(rtl) {
   background-position-x: right 17px;
 }
 
 #onboarding-tour-list .onboarding-tour-item.onboarding-complete::before {
   content: url("img/icons_tour-complete.svg");
@@ -525,17 +526,17 @@ a#onboarding-tour-screenshots-button:vis
   transform: translate(-50%, -50%);
 }
 
 #onboarding-notification-body {
   width: 500px;
   margin: 0 18px;
   color: #0c0c0d;
   display: inline-block;
-  max-height: 100%;
+  max-height: 120px;
   overflow: auto;
   padding: 15px 0;
   box-sizing: border-box;
 }
 
 #onboarding-notification-body * {
   font-size: 12px;
   font-weight: normal;
--- a/browser/extensions/pocket/bootstrap.js
+++ b/browser/extensions/pocket/bootstrap.js
@@ -367,19 +367,18 @@ var PocketOverlay = {
     // we need to manually unregister here anyway to ensure these aren't part
     // of the chrome process and avoid errors.
     AboutPocket.aboutSaved.unregister();
     AboutPocket.aboutSignup.unregister();
 
     PocketPageAction.shutdown();
 
     for (let window of browserWindows()) {
-      for (let id of ["panelMenu_pocket", "menu_pocket", "BMB_pocket",
-                      "panelMenu_pocketSeparator", "menu_pocketSeparator",
-                      "BMB_pocketSeparator", "appMenu-library-pocket-button"]) {
+      for (let id of ["panelMenu_pocket", "panelMenu_pocketSeparator",
+                      "appMenu-library-pocket-button"]) {
         let element = window.document.getElementById(id) ||
                       window.gNavToolbox.palette.querySelector("#" + id);
         if (element)
           element.remove();
       }
       this.removeStyles(window);
       // remove script getters/objects
       delete window.Pocket;
@@ -408,57 +407,18 @@ var PocketOverlay = {
     Object.defineProperty(window, "pktUIMessaging", pktUIGetter("pktUIMessaging", window));
   },
   // called for each window as it is opened
   updateWindow(window) {
     // insert our three menu items
     let document = window.document;
     let hidden = !isPocketEnabled();
 
-    // add to bookmarksMenu
-    let sib = document.getElementById("menu_bookmarkThisPage");
-    if (sib && !document.getElementById("menu_pocket")) {
-      let menu = createElementWithAttrs(document, "menuitem", {
-        "id": "menu_pocket",
-        "label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
-        "class": "menuitem-iconic", // OSX only
-        "oncommand": "Pocket.openList(event)",
-        "hidden": hidden
-      });
-      let sep = createElementWithAttrs(document, "menuseparator", {
-        "id": "menu_pocketSeparator",
-        "hidden": hidden
-      });
-      sib.parentNode.insertBefore(menu, sib);
-      sib.parentNode.insertBefore(sep, sib);
-    }
-
-    // add to bookmarks-menu-button
-    sib = document.getElementById("BMB_bookmarksToolbar");
-    if (!sib) {
-      sib = window.gNavToolbox.palette.querySelector("#BMB_bookmarksToolbar");
-    }
-    if (sib && !sib.parentNode.querySelector("#BMB_pocket")) {
-      let menu = createElementWithAttrs(document, "menuitem", {
-        "id": "BMB_pocket",
-        "label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
-        "class": "menuitem-iconic bookmark-item subviewbutton",
-        "oncommand": "Pocket.openList(event)",
-        "hidden": hidden
-      });
-      let sep = createElementWithAttrs(document, "menuseparator", {
-        "id": "BMB_pocketSeparator",
-        "hidden": hidden
-      });
-      sib.parentNode.insertBefore(menu, sib);
-      sib.parentNode.insertBefore(sep, sib);
-    }
-
     // add to PanelUI-bookmarks
-    sib = document.getElementById("panelMenuBookmarkThisPage");
+    let sib = document.getElementById("panelMenuBookmarkThisPage");
     if (sib && !document.getElementById("panelMenu_pocket")) {
       let menu = createElementWithAttrs(document, "toolbarbutton", {
         "id": "panelMenu_pocket",
         "label": gPocketBundle.GetStringFromName("pocketMenuitem.label"),
         "class": "subviewbutton cui-withicon",
         "oncommand": "Pocket.openList(event)",
         "hidden": hidden
       });
--- a/browser/extensions/pocket/skin/shared/pocket.css
+++ b/browser/extensions/pocket/skin/shared/pocket.css
@@ -202,26 +202,22 @@
     -moz-image-region: rect(64px, 64px, 128px, 0);
   }
 }
 
 #appMenu-library-pocket-button {
   list-style-image: url("chrome://pocket-shared/skin/pocket.svg");
 }
 
-#panelMenu_pocket,
-#menu_pocket,
-#BMB_pocket {
+#panelMenu_pocket {
   list-style-image: url("chrome://pocket/content/panels/img/pocketmenuitem16.png");
 }
 
 @media (min-resolution: 2dppx) {
-  #panelMenu_pocket,
-  #menu_pocket,
-  #BMB_pocket {
+  #panelMenu_pocket {
     list-style-image: url("chrome://pocket/content/panels/img/pocketmenuitem16@2x.png");
   }
 
   #panelMenu_pocket > .toolbarbutton-icon {
     width: 16px;
   }
 }
 
--- a/browser/extensions/pocket/test/browser_pocket_ui_check.js
+++ b/browser/extensions/pocket/test/browser_pocket_ui_check.js
@@ -23,19 +23,18 @@ add_task(async function test_setup() {
     }
   });
 });
 
 add_task(async function() {
   await promisePocketEnabled();
 
   checkWindowProperties(true, ["Pocket", "pktUI", "pktUIMessaging"]);
-  checkElements(true, ["pocket-button", "panelMenu_pocket", "menu_pocket", "BMB_pocket",
-                       "panelMenu_pocketSeparator", "menu_pocketSeparator",
-                       "BMB_pocketSeparator"]);
+  checkElements(true, ["pocket-button", "panelMenu_pocket",
+                       "panelMenu_pocketSeparator"]);
 
   // check context menu exists
   info("checking content context menu");
   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.com/browser/browser/extensions/pocket/test/test.html");
 
   let contextMenu = document.getElementById("contentAreaContextMenu");
   let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
@@ -49,14 +48,13 @@ add_task(async function() {
 
   contextMenu.hidePopup();
   await popupHidden;
   await BrowserTestUtils.removeTab(tab);
 
   await promisePocketDisabled();
 
   checkWindowProperties(false, ["Pocket", "pktUI", "pktUIMessaging"]);
-  checkElements(false, ["pocket-button", "panelMenu_pocket", "menu_pocket", "BMB_pocket",
-                       "panelMenu_pocketSeparator", "menu_pocketSeparator",
-                       "BMB_pocketSeparator", "context-pocket", "context-savelinktopocket"]);
+  checkElements(false, ["pocket-button", "panelMenu_pocket", "panelMenu_pocketSeparator",
+                        "context-pocket", "context-savelinktopocket"]);
 
   await promisePocketReset();
 });
--- a/browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
+++ b/browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
@@ -101,16 +101,17 @@ this.ShieldPreferences = {
     container.classList.add("indent");
 
     const hContainer = doc.createElementNS(XUL_NS, "hbox");
     hContainer.setAttribute("align", "center");
     container.appendChild(hContainer);
 
     const checkbox = doc.createElementNS(XUL_NS, "checkbox");
     checkbox.setAttribute("id", "optOutStudiesEnabled");
+    checkbox.setAttribute("class", "tail-with-learn-more");
     checkbox.setAttribute("label", "Allow Firefox to install and run studies");
     checkbox.setAttribute("preference", OPT_OUT_STUDIES_ENABLED_PREF);
     checkbox.setAttribute("disabled", !Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF));
     hContainer.appendChild(checkbox);
 
     const viewStudies = doc.createElementNS(XUL_NS, "label");
     viewStudies.setAttribute("id", "viewShieldStudies");
     viewStudies.setAttribute("href", "about:studies");
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -800,16 +800,17 @@ you can use these alternative items. Oth
 <!ENTITY syncedTabs.sidebar.noclients.title    "No synced tabs… yet!">
 <!ENTITY syncedTabs.sidebar.noclients.subtitle "Want to see your tabs from other devices here?">
 <!ENTITY syncedTabs.sidebar.notsignedin.label  "Sign in to view a list of tabs from your other devices.">
 <!ENTITY syncedTabs.sidebar.notabs.label       "No open tabs">
 <!ENTITY syncedTabs.sidebar.openprefs.label    "Open &syncBrand.shortName.label; Preferences">
 <!-- LOCALIZATION NOTE (syncedTabs.sidebar.tabsnotsyncing.label): This is shown
      when Sync is configured but syncing tabs is disabled. -->
 <!ENTITY syncedTabs.sidebar.tabsnotsyncing.label       "Turn on tab syncing to view a list of tabs from your other devices.">
+<!ENTITY syncedTabs.sidebar.searchPlaceholder  "Search synced tabs">
 
 <!-- LOCALIZATION NOTE (syncedTabs.context.open.accesskey,
                         syncedTabs.context.openAllInTabs.accesskey):
      These access keys are identical because their associated menu items are
      mutually exclusive -->
 <!ENTITY syncedTabs.context.open.label                       "Open">
 <!ENTITY syncedTabs.context.open.accesskey                   "O">
 <!ENTITY syncedTabs.context.openInNewTab.label               "Open in a New Tab">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -763,23 +763,16 @@ slowStartup.helpButton.accesskey = L
 slowStartup.disableNotificationButton.label = Don’t Tell Me Again
 slowStartup.disableNotificationButton.accesskey = A
 
 # LOCALIZATION NOTE  - %S is brandShortName
 flashHang.message = %S changed some Adobe Flash settings to improve performance.
 flashHang.helpButton.label = Learn More…
 flashHang.helpButton.accesskey = L
 
-# LOCALIZATION NOTE(customizeTips.tip0): %1$S will be replaced with the text defined
-# in customizeTips.tip0.hint, %2$S will be replaced with brandShortName, %3$S will
-# be replaced with a hyperlink containing the text defined in customizeTips.tip0.learnMore.
-customizeTips.tip0 = %1$S: You can customize %2$S to work the way you do. Simply drag any of the above to the menu or toolbar. %3$S about customizing %2$S.
-customizeTips.tip0.hint = Hint
-customizeTips.tip0.learnMore = Learn more
-
 # LOCALIZATION NOTE (customizeMode.tabTitle): %S is brandShortName
 customizeMode.tabTitle = Customize %S
 
 # LOCALIZATION NOTE (appMenuRemoteTabs.mobilePromo.text2):
 # %1$S will be replaced with a link, the text of which is
 # appMenuRemoteTabs.mobilePromo.android and the link will be to
 # https://www.mozilla.org/firefox/android/.
 # %2$S will be replaced with a link, the text of which is
--- a/browser/locales/en-US/chrome/browser/places/places.dtd
+++ b/browser/locales/en-US/chrome/browser/places/places.dtd
@@ -80,17 +80,18 @@
 <!ENTITY col.tags.label          "Tags">
 <!ENTITY col.url.label           "Location">
 <!ENTITY col.mostrecentvisit.label "Most Recent Visit">
 <!ENTITY col.visitcount.label    "Visit Count">
 <!ENTITY col.description.label   "Description">
 <!ENTITY col.dateadded.label     "Added">
 <!ENTITY col.lastmodified.label  "Last Modified">
 
-<!ENTITY search.placeholder  "Search">
+<!ENTITY historySearch.placeholder  "Search history">
+<!ENTITY bookmarksSearch.placeholder  "Search bookmarks">
 
 <!ENTITY cmd.find.key  "f">
 
 <!ENTITY maintenance.label      "Import and Backup">
 <!ENTITY maintenance.accesskey  "I">
 <!ENTITY maintenance.tooltip    "Import and backup your bookmarks">
 
 <!ENTITY backButton.tooltip  "Go back">
--- a/browser/modules/test/browser/browser_PageActions.js
+++ b/browser/modules/test/browser/browser_PageActions.js
@@ -1020,17 +1020,29 @@ function promisePanelEvent(panelIDOrNode
     }
     panel.addEventListener(eventType, () => {
       executeSoon(resolve);
     }, { once: true });
   });
 }
 
 function promisePageActionViewShown() {
-  return new Promise(resolve => {
-    BrowserPageActions.panelNode.addEventListener("ViewShown", (event) => {
-      let target = event.originalTarget;
-      window.setTimeout(() => {
-        resolve(target);
-      }, 5000);
-    }, { once: true });
+  let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                  .getInterface(Ci.nsIDOMWindowUtils);
+  info("promisePageActionViewShown waiting for ViewShown");
+  return BrowserTestUtils.waitForEvent(BrowserPageActions.panelNode, "ViewShown").then(async event => {
+    let panelViewNode = event.originalTarget;
+    // Wait for the subview to be really truly shown by making sure there's at
+    // least one child with non-zero bounds.
+    info("promisePageActionViewShown waiting for a child node to be visible");
+    await BrowserTestUtils.waitForCondition(() => {
+      let bodyNode = panelViewNode.firstChild;
+      for (let childNode of bodyNode.childNodes) {
+        let bounds = dwu.getBoundsWithoutFlushing(childNode);
+        if (bounds.width > 0 && bounds.height > 0) {
+          return true;
+        }
+      }
+      return false;
+    });
+    return panelViewNode;
   });
 }
--- a/browser/themes/linux/preferences/in-content/preferences.css
+++ b/browser/themes/linux/preferences/in-content/preferences.css
@@ -40,12 +40,11 @@ caption > label:not(.dialogTitle) {
   margin-top: 2px !important;
 }
 
 menulist.actionsMenu > .menulist-dropmarker {
   margin-top: 11px;
   margin-bottom: 11px;
 }
 
-textbox + button,
 filefield + button {
   margin-inline-start: -4px;
 }
--- a/browser/themes/shared/aboutNetError.css
+++ b/browser/themes/shared/aboutNetError.css
@@ -179,16 +179,24 @@ span#hostname {
   padding-inline-start: 38%;
 }
 
 .illustrated #errorPageContainer {
   background-repeat: no-repeat;
   background-size: 38%;
 }
 
+.connectionFailure #errorPageContainer,
+.netInterrupt #errorPageContainer,
+.netTimeout #errorPageContainer,
+.netReset #errorPageContainer,
+.netOffline #errorPageContainer {
+  background-image: url("chrome://browser/content/illustrations/error-connection-failure.svg");
+}
+
 .dnsNotFound #errorPageContainer {
   background-image: url("chrome://browser/content/illustrations/error-server-not-found.svg");
 }
 
 .malformedURI #errorPageContainer {
   background-image: url("chrome://browser/content/illustrations/error-malformed-url.svg");
 }
 
--- a/browser/themes/shared/compacttheme.inc.css
+++ b/browser/themes/shared/compacttheme.inc.css
@@ -82,20 +82,18 @@ toolbar[brighttext] .toolbarbutton-1 {
 }
 
 /* Default findbar text color doesn't look good - Bug 1125677 */
 .browserContainer > findbar .findbar-find-status,
 .browserContainer > findbar .found-matches {
   color: inherit;
 }
 
-#navigator-toolbox .toolbarbutton-1,
 .browserContainer > findbar .findbar-button,
 #PlacesToolbar toolbarbutton.bookmark-item {
-  color: var(--chrome-color);
   text-shadow: none;
 }
 
 #TabsToolbar {
   text-shadow: none !important;
 }
 
 /* URL bar and search bar*/
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1364,16 +1364,26 @@ photonpanelmultiview .PanelUI-subView .t
   min-width: auto;
   padding: 4px;
 }
 
 #appMenu-zoom-controls > .subviewbutton {
   margin-inline-start: 10px;
 }
 
+/* An em-based minimum height works better for a text-only button. */
+#appMenu-zoomReset-button {
+  min-height: 2em;
+}
+
+#appMenu-zoomReset-button > .toolbarbutton-text {
+  min-width: 3em;
+  text-align: center;
+}
+
 .toolbaritem-combined-buttons > toolbarseparator[orient="vertical"] + .subviewbutton,
 #appMenu-zoom-controls > toolbarseparator[orient="vertical"] + .subviewbutton {
   margin-inline-start: 0;
 }
 
 photonpanelmultiview .PanelUI-subView .toolbaritem-combined-buttons >
   .subviewbutton-iconic > .toolbarbutton-text,
 photonpanelmultiview .PanelUI-subView .toolbaritem-combined-buttons >
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -25,17 +25,17 @@
 
 #mainPrefPane {
   width: 100%;
   padding: 0;
   font: message-box;
   color: currentColor;
 }
 
-groupbox + groupbox {
+prefpane > groupbox + groupbox {
   margin-top: 16px;
 }
 
 groupbox + groupbox > .groupbox-body,
 groupbox + vbox groupbox > .groupbox-body {
   margin-top: 4px;
 }
 
@@ -57,43 +57,46 @@ html|option {
 
 description,
 label {
   line-height: 30px;
   margin-top: 0 !important;
   margin-bottom: 0 !important;
 }
 
-description > checkbox {
-  vertical-align: bottom;
+menuitem > label,
+button > hbox > label {
+  line-height: unset;
 }
 
 .indent {
   margin-inline-start: 28px !important;
 }
 
-separator.thin:not([orient="vertical"]) {
-  height: 8px;
-}
-
 .checkbox-check {
   margin-inline-end: 8px;
   width: 20px;
   height: 20px;
 }
 
+.tail-with-learn-more {
+  margin-inline-start: 0px;
+  margin-inline-end: 10px;
+}
+
 .learnMore {
-  margin-inline-start: 10px;
+  margin-inline-start: 0px;
   font-weight: normal;
   white-space: nowrap;
 }
 
 .accessory-button {
-  min-width: 145px;
-  margin: 2px 0;
+  height: 30px;
+  min-width: 150px;
+  margin: 4px 0;
 }
 
 #searchInput {
   border-radius: 0;
 }
 
 /* Subcategory title */
 
@@ -144,16 +147,17 @@ separator.thin:not([orient="vertical"]) 
 
 #startupPageBox {
   padding-top: 32px;
 }
 
 #browserHomePage {
   margin-inline-start: 0;
   margin-inline-end: 0;
+  margin-bottom: 4px;
 }
 
 .homepage-button:first-of-type {
   margin-inline-start: 0;
 }
 
 .homepage-button:last-of-type {
   margin-inline-end: 0;
@@ -185,17 +189,17 @@ separator.thin:not([orient="vertical"]) 
 }
 
 #updateApp > .groupbox-body > description {
   line-height: 30px;
   margin: 0;
 }
 
 #updateBox {
-  margin-top: 8px;
+  margin-top: 4px;
   margin-bottom: 32px;
 }
 
 #updateDeck > hbox > button {
   margin-top: 0;
   margin-bottom: 0;
   margin-inline-end: 0;
 }
@@ -203,17 +207,18 @@ separator.thin:not([orient="vertical"]) 
 #updateDeck > hbox > label {
   -moz-box-flex: 1;
 }
 
 #manualLink {
   margin-inline-start: 6px !important;
 }
 
-#updateRadioGroup > radio {
+#updateRadioGroup > radio,
+#browserStartupPage > radio {
   height: 30px;
   margin: 2px 0;
 }
 
 #filter {
   margin: 4px 0 8px 0;
 }
 
@@ -252,21 +257,16 @@ separator.thin:not([orient="vertical"]) 
 }
 
 .actionsMenu > menupopup > menuitem > .menu-iconic-left {
   margin-inline-end: 8px !important;
 }
 
 /* Search Pane */
 
-#defaultEngine {
-  margin-top: 2px;
-  margin-bottom: 6px;
-}
-
 #engineList {
   margin: 2px 0 5px 0;
 }
 
 #engineList > treechildren::-moz-tree-image(engineShown, checked),
 #blocklistsTree > treechildren::-moz-tree-image(selectionCol, checked) {
   list-style-image: url("chrome://global/skin/in-content/check.svg");
   -moz-context-properties: fill, stroke;
@@ -489,19 +489,23 @@ separator.thin:not([orient="vertical"]) 
 }
 
 #fxaNoLoginStatus {
   margin-top: 46px;
   margin-bottom: 64px;
 }
 
 #fxaSyncComputerName {
-  margin-top: 3px;
   margin-inline-start: 0;
-  margin-bottom: 0;
+  margin-bottom: 4px;
+}
+
+#fxaChangeDeviceName {
+  margin-top: 4px;
+  margin-bottom: 4px;
 }
 
 #tosPP-small-ToS {
   margin-bottom: 14px;
 }
 
 #noFxaCaption {
   line-height: 30px;
@@ -670,17 +674,17 @@ separator.thin:not([orient="vertical"]) 
 
 .search-tooltip {
   font-size: 1.25rem;
   position: absolute;
   padding: 0 10px;
   background-color: #ffe900;
   border: 1px solid #d7b600;
   -moz-user-select: none;
-  bottom: 35px;
+  bottom: 36px;
 }
 
 .search-tooltip:hover,
 .search-tooltip:hover::before {
   opacity: .1;
 }
 
 .search-tooltip::before {
@@ -704,16 +708,22 @@ separator.thin:not([orient="vertical"]) 
 .search-tooltip-parent {
   position: relative;
 }
 
 .visually-hidden {
   visibility: hidden;
 }
 
+menulist {
+  height: 30px;
+  margin-top: 4px;
+  margin-bottom: 4px;
+}
+
 menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left {
   display: -moz-box;
   min-width: auto; /* Override the min-width defined in menu.css */
   margin-inline-end: 6px;
 }
 
 menulist[indicator=true] > menupopup menuitem:not([image]) > .menu-iconic-left > .menu-iconic-icon {
   width: 8px;
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -5,17 +5,16 @@
 # This is not a complete / proper jar manifest. It is included by the
 # actual theme-specific manifests, so that shared resources need only
 # be specified once. As a result, the source file paths are relative
 # to the location of the actual manifest.
 
   skin/classic/browser/aboutNetError.css                       (../shared/aboutNetError.css)
   skin/classic/browser/blockedSite.css                         (../shared/blockedSite.css)
   skin/classic/browser/error-pages.css                         (../shared/error-pages.css)
-  skin/classic/browser/aboutRights.css                         (../shared/aboutRights.css)
 * skin/classic/browser/aboutSessionRestore.css                 (../shared/aboutSessionRestore.css)
   skin/classic/browser/aboutTabCrashed.css                     (../shared/aboutTabCrashed.css)
   skin/classic/browser/aboutWelcomeBack.css                    (../shared/aboutWelcomeBack.css)
   skin/classic/browser/addons/addon-install-blocked.svg        (../shared/addons/addon-install-blocked.svg)
   skin/classic/browser/addons/addon-install-confirm.svg        (../shared/addons/addon-install-confirm.svg)
   skin/classic/browser/addons/addon-install-downloading.svg    (../shared/addons/addon-install-downloading.svg)
   skin/classic/browser/addons/addon-install-error.svg          (../shared/addons/addon-install-error.svg)
   skin/classic/browser/addons/addon-install-installed.svg      (../shared/addons/addon-install-installed.svg)
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -265,17 +265,17 @@ toolbar .toolbarbutton-1[checked]:not(:a
   margin-top: -20px;
   margin-bottom: -20px;
 }
 
 /* ::::: bookmark buttons ::::: */
 
 toolbarbutton.bookmark-item:not(.subviewbutton) {
   margin: 0 2px;
-  padding: 0 4px;
+  padding: 1px 4px;
   -moz-appearance: none;
 }
 
 :root[uidensity=compact] toolbarbutton.bookmark-item:not(.subviewbutton) {
   margin: 0 1px;
 }
 
 :root[uidensity=touch] toolbarbutton.bookmark-item:not(.subviewbutton) {
@@ -322,11 +322,11 @@ toolbarbutton.bookmark-item {
 #PersonalToolbar .toolbarbutton-1 > .toolbarbutton-text,
 #PersonalToolbar .toolbarbutton-1 > .toolbarbutton-badge-stack {
   padding: 0 !important;
   background: none !important;
   min-height: 16px;
 }
 
 #PersonalToolbar .toolbarbutton-1 {
-  padding: 0 var(--toolbarbutton-inner-padding);
+  padding: 1px var(--toolbarbutton-inner-padding);
   border-radius: var(--toolbarbutton-border-radius);
 }
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -215,17 +215,17 @@
   list-style-image: url(chrome://global/skin/icons/arrow-dropdown-16.svg);
   transition: opacity 0.15s ease;
 }
 
 #urlbar[switchingtabs] > .urlbar-textbox-container > .urlbar-history-dropmarker {
   transition: none;
 }
 
-#navigator-toolbox:not(:hover) > #nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container > #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
+#nav-bar:not([customizing="true"]) > #nav-bar-customization-target > #urlbar-container:not(:hover) > #urlbar:not([focused]) > .urlbar-textbox-container > .urlbar-history-dropmarker {
   opacity: 0;
 }
 
 #pageActionButton {
   list-style-image: url("chrome://browser/skin/page-action.svg");
 }
 
 @keyframes bookmark-animation {
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -321,21 +321,16 @@
 }
 
 @media (-moz-windows-glass) {
   #main-window[sizemode=normal] #nav-bar {
     border-top-left-radius: 2.5px;
     border-top-right-radius: 2.5px;
   }
 
-  #main-window[sizemode=fullscreen]:not(:-moz-lwtheme) {
-    -moz-appearance: none;
-    background-color: #556;
-  }
-
   #toolbar-menubar:not(:-moz-lwtheme) {
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
   }
 
   #main-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive) {
     background-color: rgba(255,255,255,.5);
     color: black;
     border-radius: 4px;
@@ -346,52 +341,16 @@
   #main-window[sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme {
     border-top: 2px solid;
     -moz-border-top-colors: @glassActiveBorderColor@ rgba(255,255,255,.6);
   }
 
   #main-window[sizemode="normal"] > #tab-view-deck > #browser-panel:-moz-lwtheme:-moz-window-inactive {
     -moz-border-top-colors: @glassInactiveBorderColor@ rgba(255,255,255,.6);
   }
-
-  /* Glass Fog */
-
-  #TabsToolbar:not(:-moz-lwtheme) {
-    position: relative;
-  }
-
-  #TabsToolbar:not(:-moz-lwtheme)::after {
-    /* Because we use placeholders for window controls etc. in the tabstrip,
-     * and position those with ordinal attributes, and because our layout code
-     * expects :before/:after nodes to come first/last in the frame list,
-     * we have to reorder this element to come last, hence the
-     * ordinal group value (see bug 853415). */
-    -moz-box-ordinal-group: 1001;
-    box-shadow: 0 0 30px 30px rgba(174,189,204,0.85);
-    content: "";
-    display: -moz-box;
-    height: 0;
-    margin: 0 60px; /* (30px + 30px) from box-shadow */
-    position: absolute;
-    pointer-events: none;
-    top: 50%;
-    width: -moz-available;
-    z-index: -1;
-  }
-
-  /* Need to constrain the glass fog to avoid overlapping layers, see bug 886281. */
-  #navigator-toolbox:not(:-moz-lwtheme) {
-    overflow: -moz-hidden-unscrollable;
-  }
-
-  #main-window[sizemode=normal] .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
-    position: relative;
-  }
-
-  /* End Glass Fog */
 }
 
 /* Aero Basic */
 @media (-moz-windows-compositor: 0) {
   @media (-moz-windows-default-theme) {
     #main-window {
       background-color: rgb(185,209,234);
     }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -98,16 +98,47 @@
   color: var(--toolbar-color, inherit);
 }
 
 #toolbar-menubar,
 #TabsToolbar {
   color: var(--titlebar-text-color);
 }
 
+/*
+ * Windows 7 draws the chrome background color as the tab background
+ * instead of in the tabs toolbar.
+ */
+@media (-moz-os-version: windows-win7) {
+  @media (-moz-windows-default-theme) {
+    :root:not(:-moz-lwtheme) {
+      --tabs-border: #4A4A4F;
+    }
+
+    #TabsToolbar:not(:-moz-lwtheme) {
+      color: hsl(240,9%,98%);
+    }
+
+    /* Always show full-height tab separators on tabs with borders. */
+    .tabbrowser-tab:not(:-moz-lwtheme)::before {
+      border-image: none !important;
+    }
+
+    /* Show the tabs toolbar background color inside tabs on Win 7. */
+    .tab-background:not(:-moz-lwtheme):not([selected=true]) {
+      background-color: hsl(235,33%,19%) !important;
+    }
+
+    /* Show border on tabs with background colors in Windows 7. */
+    .tab-background:not(:-moz-lwtheme) {
+      border-top: 1px solid var(--tabs-border);
+    }
+  }
+}
+
 @media (-moz-windows-compositor: 0),
        (-moz-windows-default-theme: 0) {
   /* Please keep the menu text colors in this media block in sync with
    * compacttheme.css, minus the :not(:-moz-lwtheme) condition - see Bug 1165718.
    */
   :root[tabsintitlebar]:not([inFullscreen]):not(:-moz-lwtheme) {
     --titlebar-text-color: CaptionText;
   }
@@ -1077,24 +1108,16 @@ notification[value="translation"] {
  * labels during the customize mode transition. Subpixel anti-aliasing
  * on Windows with Direct2D layers acceleration is particularly slow to
  * paint, so this hack is how we sidestep that performance bottleneck.
  */
 #main-window:-moz-any([customize-entering],[customize-exiting]) label {
   transform: perspective(0.01px);
 }
 
-#customization-tipPanel > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="left"] {
-  margin-right: -2px;
-}
-
-#customization-tipPanel > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow[side="right"] {
-  margin-left: -2px;
-}
-
 /* End customization mode */
 
 /* Private browsing indicators */
 
 /**
  * Currently, we have two places where we put private browsing indicators on
  * Windows. When tabsintitlebar is enabled, we draw the indicator in the titlebar.
  * When tabsintitlebar is disabled, we draw the indicator at the end of the
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -13,88 +13,80 @@
    theme. It can't be changed to transparent when there is no compositor
    (Win 7 in classic / basic theme), or else dragging and focus become
    broken. So instead just show the normal titlebar in that case, and override
    the window color as transparent when the compositor is available. */
 @media (-moz-windows-compositor: 0) {
   #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
     visibility: visible;
   }
-
-  #main-window {
-    background: var(--chrome-background-color) !important;
-  }
 }
 
 @media (-moz-windows-compositor) {
   #main-window {
     background: transparent !important;
   }
 }
 
 #toolbar-menubar {
   text-shadow: none !important;
 }
 
 @media (-moz-windows-glass) {
+  /* Always show light toolbar elements on aero glass surface. */
+  #TabsToolbar {
+    color: hsl(240,9%,98%);
+  }
+
+  /* Keep showing the correct color inside the tabs. */
   .tabbrowser-tab {
-    background-color: var(--chrome-background-color);
+    color: var(--chrome-color) !important;
+  }
+
+  /* Make the menubar text readable on aero glass (copied from browser-aero.css). */
+  #toolbar-menubar {
+    text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
+  }
+
+  #main-menubar:not(:-moz-window-inactive) {
+    background-color: rgba(255,255,255,.5);
+    color: black;
+    border-radius: 4px;
+  }
+}
+
+/* Windows 7 specific tab styles that apply to default, dark and light themes
+ * (the default theme counterpart lives in browser.css) */
+@media (-moz-os-version: windows-win7) {
+  /* Always show full-height tab separators on tabs with borders. */
+  .tabbrowser-tab::before {
+    border-image: none !important;
+  }
+
+  /* Show the tabs toolbar background color inside tabs on Win 7. */
+  .tab-background:not([selected=true]) {
+    background-color: var(--chrome-background-color) !important;
+  }
+
+  /* Show border on tabs with background colors in Windows 7. */
+  .tab-background {
+    border-top: 1px solid var(--tabs-border);
   }
 }
 
 @media (-moz-os-version: windows-win7),
        (-moz-os-version: windows-win8) {
-  /* It'd be nice if there was an element in the scrollbox's inner content
-     that collapsed to the current width of the tabs. Since there isn't we
-     need to handle overflowing and non-overflowing tabs separately.
-
-     In the case of overflowing tabs, set a border-top on the entire container,
-     otherwise we need to set it on each element individually */
-  #main-window[sizemode=normal] .tabbrowser-tabs[overflow="true"] {
-    background-clip: padding-box;
-    border-top: 1px solid var(--chrome-nav-bar-separator-color);
-    border-inline-end: 1px solid var(--chrome-nav-bar-separator-color);
-  }
-
-  /* Add a border to the left of the first tab (or scroll arrow).  Using .tabbrowser-tabs
-     instead of #TabsToolbar because it will work even in customize mode. */
-  #main-window[sizemode=normal] .tabbrowser-tabs {
-    background-clip: padding-box;
-    border-inline-start: 1px solid var(--chrome-nav-bar-separator-color);
-    border-inline-end: 1px solid transparent;
-  }
-
-  #main-window[sizemode=normal] .tabbrowser-tabs:not([overflow="true"]) .closing-tabs-spacer {
-    background-clip: padding-box;
-    border-inline-start: 1px solid var(--chrome-nav-bar-separator-color);
-  }
-
   /* Use default window colors when in non-maximized mode */
   #tabbrowser-tabs,
   #TabsToolbar,
   #browser-panel,
   #titlebar-content {
     background: transparent;
   }
 
-  /* Ensure that the entire background is styled when maximized/fullscreen */
-  #main-window:not([sizemode="normal"]):not([customizing]) #browser-panel {
-    background: var(--chrome-background-color) !important;
-  }
-
-  /* The menu items need to be visible when the entire background is styled */
-  #main-window:not([sizemode="normal"]) #main-menubar {
-    color: var(--chrome-color);
-    background-color: transparent;
-  }
-
-  #main-window[sizemode="maximized"] #main-menubar > menu:not(:-moz-window-inactive) {
-    color: inherit;
-  }
-
   /* Use proper menu text styling in Win7 classic mode (copied from browser.css) */
   @media (-moz-windows-compositor: 0),
          (-moz-windows-default-theme: 0) {
     :root[tabsintitlebar]:not([inFullscreen]) {
       --titlebar-text-color: CaptionText;
     }
 
     :root[tabsintitlebar]:not([inFullscreen]):-moz-window-inactive {
--- a/browser/themes/windows/preferences/in-content/preferences.css
+++ b/browser/themes/windows/preferences/in-content/preferences.css
@@ -18,14 +18,13 @@ caption > label:not(.dialogTitle) {
 .help-button {
   font-size: 1.08rem;
 }
 
 .actionsMenu > .menulist-label-box > .menulist-icon {
   margin-inline-end: 9px;
 }
 
-textbox + button,
 filefield + button {
   margin-inline-start: -4px;
   margin-top: 4px;
   margin-bottom: 4px;
 }
--- a/build/valgrind/x86_64-redhat-linux-gnu.sup
+++ b/build/valgrind/x86_64-redhat-linux-gnu.sup
@@ -225,16 +225,47 @@
    Skia and CPUID, Jan 2017, #2
    Memcheck:Cond
    fun:_ZN6SkOpts4InitEv
    fun:_ZN11gfxPlatform4InitEv
    fun:_ZN11gfxPlatform11GetPlatformEv
    fun:_ZN7mozilla3dom*Content*
 }
 
+# False positives triggered by rust 1.20.0 (at least) builds of stylo.
+# See bug 1394696. The diagnosis is an llvm optimization transforming
+# `if A && B` to `if B && A` if is can be proven that A is false
+# whenever B is uninitialized. Confusing, but valid.
+#
+# Conditional jump or move depends on uninitialised value(s)
+#    at 0x113ED01E: selectors::matching::matches_complex_selector_internal (option.rs:421)
+#    by 0x113ECF19: selectors::matching::matches_complex_selector (matching.rs:501)
+#    by 0x113EBAC0: <style::selector_map::SelectorMap<style::stylist::Rule>>::get_matching_rules (matching.rs:397)
+{
+  Bug 1394696 Stylo selector, Sept 2017, part 1
+  Memcheck:Cond
+  fun:_ZN9selectors8matching33matches_complex_selector_internal*
+  fun:_ZN9selectors8matching24matches_complex_selector*
+  ...
+  fun:_ZN69_$LT$style..selector_map..SelectorMap$LT$style..stylist..Rule$GT$$GT$18get_matching_rules*
+}
+
+# Conditional jump or move depends on uninitialised value(s)
+#    at 0x113EFFDE: selectors::matching::matches_complex_selector_internal (option.rs:421)
+#    by 0x113EFED9: selectors::matching::matches_complex_selector (matching.rs:501)
+#    by 0x113DFE55: style::stylist::Stylist::match_revalidation_selectors::{{closure}} (matching.rs:397)
+{
+  Bug 1394696 Stylo selector, Sept 2017, part 2
+  Memcheck:Cond
+  fun:_ZN9selectors8matching33matches_complex_selector_internal*
+  fun:_ZN9selectors8matching24matches_complex_selector*
+  ...
+  fun:_ZN5style9traversal13compute_style*
+  fun:recalc_style_at<style::gecko::wrapper::GeckoElement,style::gecko::traversal::RecalcStyleOnly,closure>
+}
 
 ###################################################
 #  For valgrind-mochitest ("tc-M-V [tier 2]") runs on taskcluster.
 #  See bug 1248365.
 #  These are specific to Ubuntu 12.04.5, 64-bit.
 ###################################################
 
 
--- a/client.py
+++ b/client.py
@@ -107,32 +107,34 @@ def update_nspr_or_nss(tag, depfile, des
   destination = destination.rstrip('/')
   permanent_patch_dir = destination + '/patches'
   temporary_patch_dir = destination + '.patches'
   if os.path.exists(temporary_patch_dir):
     print "please clean up leftover directory " + temporary_patch_dir
     sys.exit(2)
   warn_if_patch_exists(permanent_patch_dir)
   # protect patch directory from being removed by do_hg_replace
-  shutil.move(permanent_patch_dir, temporary_patch_dir)
+  if os.path.exists(permanent_patch_dir):
+    shutil.move(permanent_patch_dir, temporary_patch_dir)
   # now update the destination
   print "reverting to HG version of %s to get its blank line state" % depfile
   check_call_noisy([options.hg, 'revert', depfile])
   old_state = get_trailing_blank_line_state(depfile)
   print "old state of %s is: %s" % (depfile, old_state)
   do_hg_replace(destination, hgpath, tag, HG_EXCLUSIONS, options.hg)
   new_state = get_trailing_blank_line_state(depfile)
   print "new state of %s is: %s" % (depfile, new_state)
   if old_state == new_state:
     print "toggling blank line in: ", depfile
     toggle_trailing_blank_line(depfile)
   tag_file = destination + "/TAG-INFO"
   print >>file(tag_file, "w"), tag
   # move patch directory back to a subdirectory
-  shutil.move(temporary_patch_dir, permanent_patch_dir)
+  if os.path.exists(temporary_patch_dir):
+    shutil.move(temporary_patch_dir, permanent_patch_dir)
 
 def warn_if_patch_exists(path):
   # If the given patch directory exists and contains at least one file,
   # then print warning and wait for the user to acknowledge.
   if os.path.isdir(path) and os.listdir(path):
     print "========================================"
     print "WARNING: At least one patch file exists"
     print "in directory: " + path
--- a/devtools/client/commandline/test/browser_cmd_appcache_invalid.js
+++ b/devtools/client/commandline/test/browser_cmd_appcache_invalid.js
@@ -49,17 +49,17 @@ function* spawnTest() {
       "after http://sub1.test1.example.com/browser/devtools/client/" +
       "commandline/test/browser_cmd_appcache_invalid_appcache.appcache. Unless " +
       "the text in the manifest file is changed the cached version will be used " +
       "instead at line 39.",
     "browser_cmd_appcache_invalid_page3.html has cache-control set to no-store. " +
       "This will prevent the application cache from storing the file at line 39.",
     "http://example.com/logo.png points to a resource that is not available at line 40.",
     "http://example.com/check.png points to a resource that is not available at line 41.",
-    "Spaces in URIs need to be replaced with % at line 42.",
+    "Spaces in URIs need to be replaced with %20 at line 42.",
     "http://example.com/cr oss.png points to a resource that is not available at line 42.",
     "Asterisk (*) incorrectly used in the CACHE section at line 43. If a line " +
       "in the NETWORK section contains only a single asterisk character, then " +
       "any URI not listed in the manifest will be treated as if the URI was " +
       "listed in the NETWORK section. Otherwise such URIs will be treated as " +
       "unavailable. Other uses of the * character are prohibited",
     "The SETTINGS section may only contain a single value, \u201cprefer-online\u201d or \u201cfast\u201d at line 47.",
     "FALLBACK section line 50 (/section1/ /offline1.html) prevents caching of " +
--- a/devtools/client/locales/en-US/appcacheutils.properties
+++ b/devtools/client/locales/en-US/appcacheutils.properties
@@ -75,20 +75,22 @@ firstLineMustBeCacheManifest=The first line of the manifest must be “CACHE MANIFEST” at line %S.
 # Parameters: %S is the line number where "CACHE MANIFEST" appears.
 cacheManifestOnlyFirstLine2=“CACHE MANIFEST” is only valid on the first line but was found at line %S.
 
 # LOCALIZATION NOTE (asteriskInWrongSection2): the associated cache manifest
 # has an asterisk (*) in a section other than the NETWORK section. Parameters:
 # %1$S is the section name, %2$S is the line number.
 asteriskInWrongSection2=Asterisk (*) incorrectly used in the %1$S section at line %2$S. If a line in the NETWORK section contains only a single asterisk character, then any URI not listed in the manifest will be treated as if the URI was listed in the NETWORK section. Otherwise such URIs will be treated as unavailable. Other uses of the * character are prohibited.
 
-# LOCALIZATION NOTE (escapeSpaces): the associated cache manifest has a space
+# LOCALIZATION NOTE (escapeSpaces1): the associated cache manifest has a space
 # in a URI. Spaces must be replaced with %20. Parameters: %S is the line
 # number where this error occurs.
-escapeSpaces=Spaces in URIs need to be replaced with %20 at line %S.
+# %% will be displayed as a single % character (% is commonly used to define
+# format specifiers, so it needs to be escaped).
+escapeSpaces1=Spaces in URIs need to be replaced with %%20 at line %S.
 
 # LOCALIZATION NOTE (slashDotDotSlashBad): the associated cache manifest has a
 # URI containing /../, which is invalid. Parameters: %S is the line number
 # where this error occurs.
 slashDotDotSlashBad=/../ is not a valid URI prefix at line %S.
 
 # LOCALIZATION NOTE (tooManyDotDotSlashes): the associated cache manifest has
 # a URI containing too many ../ operators. Too many of these operators mean
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -444,17 +444,17 @@ ManifestParser.prototype = {
       if (this.currSection != "NETWORK" || text.length != 1) {
         this._addError(this.currentLine, "asteriskInWrongSection2",
                        this.currSection, this.currentLine);
         return;
       }
     }
 
     if (/\s/.test(text)) {
-      this._addError(this.currentLine, "escapeSpaces", this.currentLine);
+      this._addError(this.currentLine, "escapeSpaces1", this.currentLine);
       text = text.replace(/\s/g, "%20");
     }
 
     if (text[0] == "/") {
       if (text.substr(0, 4) == "/../") {
         this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
       } else {
         this.uris.push(this._wrapURI(this.origin + text.substring(1)));
@@ -497,17 +497,17 @@ ManifestParser.prototype = {
 
     let [ namespace, fallback ] = split;
 
     if (namespace.indexOf("*") != -1) {
       this._addError(this.currentLine, "fallbackAsterisk2", this.currentLine);
     }
 
     if (/\s/.test(namespace)) {
-      this._addError(this.currentLine, "escapeSpaces", this.currentLine);
+      this._addError(this.currentLine, "escapeSpaces1", this.currentLine);
       namespace = namespace.replace(/\s/g, "%20");
     }
 
     if (namespace.substr(0, 4) == "/../") {
       this._addError(this.currentLine, "slashDotDotSlashBad", this.currentLine);
     }
 
     if (namespace.substr(0, 2) == "./") {
--- a/devtools/client/shared/components/notification-box.css
+++ b/devtools/client/shared/components/notification-box.css
@@ -78,17 +78,17 @@
   height: 20px;
   margin: 4px;
   margin-inline-end: 8px;
   background-image: url("chrome://devtools/skin/images/close.svg");
   background-position: center;
   background-color: transparent;
   background-repeat: no-repeat;
   border-radius: 11px;
-  filter: invert(0);
+  filter: var(--theme-messageCloseButtonFilter);
 }
 
 .notificationbox .messageCloseButton:hover {
   background-color: gray;
   filter: invert(1);
 }
 
 .notificationbox .messageCloseButton:active {
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -93,16 +93,17 @@
   --theme-tooltip-background: rgba(255, 255, 255, .9);
   --theme-tooltip-shadow: rgba(155, 155, 155, 0.26);
 
   /* Command line */
   --theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme);
   --theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#light-theme-focus);
 
   --theme-codemirror-gutter-background: #f4f4f4;
+  --theme-messageCloseButtonFilter: invert(0);
 }
 
 :root.theme-dark {
   --theme-body-background: var(--grey-80);
   --theme-sidebar-background: var(--grey-90);
   --theme-contrast-background: #ffb35b;
 
   /* Toolbar */
@@ -177,16 +178,17 @@
   --theme-tooltip-background: rgba(19, 28, 38, .9);
   --theme-tooltip-shadow: rgba(25, 25, 25, 0.76);
 
   /* Command line */
   --theme-command-line-image: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme);
   --theme-command-line-image-focus: url(chrome://devtools/skin/images/commandline-icon.svg#dark-theme-focus);
 
   --theme-codemirror-gutter-background: #262b37;
+  --theme-messageCloseButtonFilter: invert(1);
 }
 
 :root.theme-firebug {
   --theme-body-background: #fff;
   --theme-sidebar-background: #fcfcfc;
   --theme-contrast-background: #e6b064;
 
   --theme-tab-toolbar-background: rgb(240, 240, 240) linear-gradient(rgba(255, 255, 255, 0.8), transparent);
--- a/devtools/server/actors/highlighters.css
+++ b/devtools/server/actors/highlighters.css
@@ -20,16 +20,18 @@
   Content CSS applying to the html element impact the highlighters.
   To avoid that, possible cases have been set to initial.
   */
   text-transform: initial;
   text-indent: initial;
   letter-spacing: initial;
   word-spacing: initial;
   color: initial;
+  direction: initial;
+  writing-mode: initial;
 }
 
 :-moz-native-anonymous .highlighter-container {
   --highlighter-guide-color: #08c;
   --highlighter-content-color: #87ceeb;
   --highlighter-bubble-text-color: hsl(216, 33%, 97%);
   --highlighter-bubble-background-color: hsl(214, 13%, 24%);
   --highlighter-bubble-border-color: rgba(255, 255, 255, 0.2);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -14980,65 +14980,66 @@ nsDocShell::ShouldPrepareForIntercept(ns
     return NS_OK;
   }
 
   if (mSandboxFlags) {
     // If we're sandboxed, don't intercept.
     return NS_OK;
   }
 
+  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
+  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
+    // If cookies are disabled, don't intercept.
+    return NS_OK;
+  }
+
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (!swm) {
     return NS_OK;
   }
 
-  nsresult result;
-  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-    do_GetService(THIRDPARTYUTIL_CONTRACTID, &result);
-  NS_ENSURE_SUCCESS(result, result);
-
-  if (mCurrentURI &&
-      nsContentUtils::CookiesBehavior() == nsICookieService::BEHAVIOR_REJECT_FOREIGN) {
-    nsAutoCString uriSpec;
-    if (!(mCurrentURI->GetSpecOrDefault().EqualsLiteral("about:blank"))) {
-      // Reject the interception of third-party iframes if the cookie behaviour
-      // is set to reject all third-party cookies (1). In case that this pref
-      // is not set or can't be read, we default to allow all cookies (0) as
-      // this is the default value in all.js.
+  if (!aIsNonSubresourceRequest) {
+    nsCOMPtr<nsIDocument> doc = GetDocument();
+    if (!doc) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+
+    ErrorResult rv;
+    *aShouldIntercept = swm->IsControlled(doc, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+
+    return NS_OK;
+  }
+
+  // If the user has set a cookie policy that restricts cookies, then
+  // avoid intercepting 3rd party iframes.
+  if (cookieBehavior != nsICookieService::BEHAVIOR_ACCEPT) {
+    nsCOMPtr<nsIDocShellTreeItem> parent;
+    GetSameTypeParent(getter_AddRefs(parent));
+    nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
+                                                       : nullptr;
+    if (parentWindow) {
+      nsresult rv = NS_OK;
+      nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+        do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
       bool isThirdPartyURI = true;
-      result = thirdPartyUtil->IsThirdPartyURI(mCurrentURI, aURI,
-                                               &isThirdPartyURI);
-      if (NS_FAILED(result)) {
-          return result;
-      }
-
-      if (isThirdPartyURI) {
+      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI, &isThirdPartyURI);
+      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
         return NS_OK;
       }
     }
   }
 
-  if (aIsNonSubresourceRequest) {
-    nsCOMPtr<nsIPrincipal> principal =
-      BasePrincipal::CreateCodebasePrincipal(aURI, mOriginAttributes);
-    *aShouldIntercept = swm->IsAvailable(principal, aURI);
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIDocument> doc = GetDocument();
-  if (!doc) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  ErrorResult rv;
-  *aShouldIntercept = swm->IsControlled(doc, rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    return rv.StealNSResult();
-  }
-
+  nsCOMPtr<nsIPrincipal> principal =
+    BasePrincipal::CreateCodebasePrincipal(aURI, mOriginAttributes);
+  *aShouldIntercept = swm->IsAvailable(principal, aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
 {
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (!swm) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9057,16 +9057,30 @@ nsContentUtils::StorageAllowedForWindow(
     return InternalStorageAllowedForPrincipal(principal, aWindow);
   }
 
   return StorageAccess::eDeny;
 }
 
 // static, public
 nsContentUtils::StorageAccess
+nsContentUtils::StorageAllowedForDocument(nsIDocument* aDoc)
+{
+  MOZ_ASSERT(aDoc);
+
+  if (nsPIDOMWindowInner* inner = aDoc->GetInnerWindow()) {
+    nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
+    return InternalStorageAllowedForPrincipal(principal, inner);
+  }
+
+  return StorageAccess::eDeny;
+}
+
+// static, public
+nsContentUtils::StorageAccess
 nsContentUtils::StorageAllowedForPrincipal(nsIPrincipal* aPrincipal)
 {
   return InternalStorageAllowedForPrincipal(aPrincipal, nullptr);
 }
 
 // static, private
 void
 nsContentUtils::GetCookieBehaviorForPrincipal(nsIPrincipal* aPrincipal,
@@ -9134,17 +9148,17 @@ nsContentUtils::InternalStorageAllowedFo
   // calling context is chrome.
   if (aPrincipal->GetIsNullPrincipal()) {
     return StorageAccess::eDeny;
   }
 
   if (aWindow) {
     // If the document is sandboxed, then it is not permitted to use storage
     nsIDocument* document = aWindow->GetExtantDoc();
-    if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
+    if (document && document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
       return StorageAccess::eDeny;
     }
 
     // Check if we are in private browsing, and record that fact
     if (IsInPrivateBrowsing(document)) {
       access = StorageAccess::ePrivateBrowsing;
     }
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2921,16 +2921,27 @@ public:
    *
    * This logic is intended to be shared between the different forms of
    * persistent storage which are available to web pages. Cookies don't use
    * this logic, and security logic related to them must be updated separately.
    */
   static StorageAccess StorageAllowedForWindow(nsPIDOMWindowInner* aWindow);
 
   /*
+   * Checks if storage for the given document is permitted by a combination of
+   * the user's preferences, and whether the document's window is a third-party
+   * iframe.
+   *
+   * Note, this may be used on documents during the loading process where
+   * the window's extant document has not been set yet.  The code in
+   * StorageAllowedForWindow(), however, will not work in these cases.
+   */
+  static StorageAccess StorageAllowedForDocument(nsIDocument* aDoc);
+
+  /*
    * Checks if storage for the given principal is permitted by the user's
    * preferences. The caller is assumed to not be a third-party iframe.
    * (if that is possible, the caller should use StorageAllowedForWindow)
    */
   static StorageAccess StorageAllowedForPrincipal(nsIPrincipal* aPrincipal);
 
   /*
    * Serializes a HTML nsINode into its markup representation.
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1714,35 +1714,33 @@ nsMessageManagerScriptExecutor::Unlink()
 bool
 nsMessageManagerScriptExecutor::InitChildGlobalInternal(
   nsISupports* aScope,
   const nsACString& aID)
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
+  const uint32_t flags = xpc::INIT_JS_STANDARD_CLASSES;
 
   JS::CompartmentOptions options;
   options.creationOptions().setSystemZone();
   options.behaviors().setVersion(JSVERSION_DEFAULT);
 
   if (xpc::SharedMemoryEnabled()) {
     options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
   }
 
-  nsCOMPtr<nsIXPConnectJSObjectHolder> globalHolder;
-  nsresult rv =
-    xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
-                                         flags, options,
-                                         getter_AddRefs(globalHolder));
+  JS::Rooted<JSObject*> global(cx);
+  nsresult rv = xpc::InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
+                                                     flags, options,
+                                                     &global);
   NS_ENSURE_SUCCESS(rv, false);
 
-  mGlobal = globalHolder->GetJSObject();
+  mGlobal = global;
   NS_ENSURE_TRUE(mGlobal, false);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information.
   xpc::SetLocationForGlobal(mGlobal, aID);
 
   DidCreateGlobal();
   return true;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2943,18 +2943,18 @@ CreateNativeGlobalForInner(JSContext* aC
 
   options.creationOptions().setSecureContext(aIsSecureContext);
 
   xpc::InitGlobalObjectOptions(options, aPrincipal);
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
-  uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
-  flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
+  uint32_t flags = needComponents ? 0 : xpc::OMIT_COMPONENTS_OBJECT;
+  flags |= xpc::DONT_FIRE_ONNEWGLOBALHOOK;
 
   if (!WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
                            nsJSPrincipals::get(aPrincipal), false, aGlobal) ||
       !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(aNewInner->GetWrapperPreserveColor() == aGlobal);
--- a/dom/cache/test/mochitest/mochitest.ini
+++ b/dom/cache/test/mochitest/mochitest.ini
@@ -46,8 +46,9 @@ support-files =
 [test_cache_orphaned_body.html]
 scheme=https
 [test_cache_padding.html]
 [test_cache_untrusted.html]
 [test_cache_updateUsage.html]
 [test_chrome_constructor.html]
 [test_cache_worker_gc.html]
 scheme=https
+[test_cache_tons_of_fd.html]
new file mode 100644
--- /dev/null
+++ b/dom/cache/test/mochitest/test_cache_tons_of_fd.html
@@ -0,0 +1,111 @@
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test cache to create tons of fds</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="driver.js"></script>
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+  function setupTestIframe() {
+  return new Promise(function(resolve) {
+    var iframe = document.createElement("iframe");
+    iframe.src = "empty.html";
+    iframe.onload = function() {
+      window.caches = iframe.contentWindow.caches;
+      resolve();
+    };
+    document.body.appendChild(iframe);
+  });
+}
+
+function clearStorage() {
+  return new Promise(function(resolve, reject) {
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var request = qms.clearStoragesForPrincipal(principal);
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
+  });
+}
+
+async function testCreateTonsOfFD() {
+  const number_of_fd = 5120;
+  const name = 'cacheTonsOfFD';
+  const url = "foo.com"
+  const body = "This is a body";
+
+  info("Stage A: Cached a Request/Response pairs");
+  let cache = await caches.open(name);
+  let request = new Request(url);
+  let response = new Response(body);
+  await cache.put(request, response);
+
+  info("Stage B: Read the cached response mutliple times");
+  let promise_array = [];
+  for (let i = 0; i < number_of_fd; ++i) {
+    let promise = cache.match(request);
+    promise_array.push(promise);
+  }
+  let cached_response_array = [];
+  try {
+    cached_response_array = await Promise.all(promise_array);
+  } catch (e) {
+    throw("Fail to open tons of files with error: " + e);
+  }
+
+  if (cached_response_array.length != number_of_fd) {
+    throw("Fail to cache.match the cached responses");
+  }
+
+  info("Stage C: Consume the cached body")
+  for (let i = 0; i < number_of_fd; ++i) {
+    if (!cached_response_array[i]) {
+      // Reduce the checking message.
+      throw("The cached response doesn't exist");
+    }
+
+    let bodyText = "";
+    try {
+      bodyText = await cached_response_array[i].text();
+    } catch (e) {
+      throw("Fail to consume the cached response's body with error: " + e);
+    }
+
+    if (bodyText != body) {
+      // Reduce the checking message.
+      throw("The cached body doeen't be the same as original one");
+    }
+  }
+
+  ok(true, "Doesn't crash or timeout");
+  return Promise.resolve();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({
+  "set": [["dom.caches.enabled", true],
+          ["dom.caches.testing.enabled", true],
+          ["dom.quotaManager.testing", true]]
+}, async function() {
+  await setupTestIframe();
+
+  info("Stage 1: Clean storage.");
+  await clearStorage();
+
+  info("Stage 2: Verify open lots of files at the same time doesn't crash " +
+       "the browser");
+  try {
+    await testCreateTonsOfFD();
+  } catch (e) {
+    ok(false, e);
+  }
+
+  await SimpleTest.finish();
+});
+</script>
+</body>
+</html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3754,55 +3754,63 @@ HTMLMediaElement::LookupMediaElementURIT
       if (decoder->CanClone()) {
         return elem;
       }
     }
   }
   return nullptr;
 }
 
-class HTMLMediaElement::ShutdownObserver : public nsIObserver {
-  enum class Phase : int8_t {
+class HTMLMediaElement::ShutdownObserver : public nsIObserver
+{
+  enum class Phase : int8_t
+  {
     Init,
     Subscribed,
     Unsubscribed
   };
+
 public:
   NS_DECL_ISUPPORTS
 
-  NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*) override {
-    MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Subscribed);
+  NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*) override
+  {
+    if (mPhase != Phase::Subscribed) {
+      // Bail out if we are not subscribed for this might be called even after
+      // |nsContentUtils::UnregisterShutdownObserver(this)|.
+      return NS_OK;
+    }
     MOZ_DIAGNOSTIC_ASSERT(mWeak);
     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
       mWeak->NotifyShutdownEvent();
     }
     return NS_OK;
   }
-  void Subscribe(HTMLMediaElement* aPtr) {
+  void Subscribe(HTMLMediaElement* aPtr)
+  {
     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Init);
     MOZ_DIAGNOSTIC_ASSERT(!mWeak);
     mWeak = aPtr;
     nsContentUtils::RegisterShutdownObserver(this);
     mPhase = Phase::Subscribed;
   }
-  void Unsubscribe() {
+  void Unsubscribe()
+  {
     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Subscribed);
     MOZ_DIAGNOSTIC_ASSERT(mWeak);
     mWeak = nullptr;
     nsContentUtils::UnregisterShutdownObserver(this);
     mPhase = Phase::Unsubscribed;
   }
-  void AddRefMediaElement() {
-    mWeak->AddRef();
-  }
-  void ReleaseMediaElement() {
-    mWeak->Release();
-  }
+  void AddRefMediaElement() { mWeak->AddRef(); }
+  void ReleaseMediaElement() { mWeak->Release(); }
+
 private:
-  virtual ~ShutdownObserver() {
+  virtual ~ShutdownObserver()
+  {
     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Unsubscribed);
     MOZ_DIAGNOSTIC_ASSERT(!mWeak);
   }
   // Guaranteed to be valid by HTMLMediaElement.
   HTMLMediaElement* mWeak = nullptr;
   Phase mPhase = Phase::Init;
 };
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -690,17 +690,20 @@ IDBDatabase::Transaction(JSContext* aCx,
       return NS_ERROR_DOM_TYPE_ERR;
 
     default:
       MOZ_CRASH("Unknown mode!");
   }
 
   RefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(aCx, this, sortedStoreNames, mode);
-  MOZ_ASSERT(transaction);
+  if (NS_WARN_IF(!transaction)) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
 
   BackgroundTransactionChild* actor =
     new BackgroundTransactionChild(transaction);
 
   IDB_LOG_MARK("IndexedDB %s: Child  Transaction[%lld]: "
                  "database(%s).transaction(%s)",
                "IndexedDB %s: C T[%lld]: IDBDatabase.transaction()",
                IDB_LOG_ID_STRING(),
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -225,34 +225,39 @@ IDBTransaction::Create(JSContext* aCx, I
 
   RefPtr<IDBTransaction> transaction =
     new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
   IDBRequest::CaptureCaller(aCx, transaction->mFilename, &transaction->mLineNo,
                             &transaction->mColumn);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
 
+  if (!NS_IsMainThread()) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+
+    workerPrivate->AssertIsOnWorkerThread();
+
+    nsAutoPtr<WorkerHolder> workerHolder(
+      new WorkerHolder(workerPrivate, transaction));
+    if (NS_WARN_IF(!workerHolder->HoldWorker(workerPrivate, Canceling))) {
+      return nullptr;
+    }
+
+    transaction->mWorkerHolder = Move(workerHolder);
+  }
+
   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
   nsContentUtils::RunInMetastableState(runnable.forget());
 
   transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
   transaction->mRegistered = true;
 
-  if (!NS_IsMainThread()) {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-
-    workerPrivate->AssertIsOnWorkerThread();
-
-    transaction->mWorkerHolder = new WorkerHolder(workerPrivate, transaction);
-    MOZ_ALWAYS_TRUE(transaction->mWorkerHolder->HoldWorker(workerPrivate, Canceling));
-  }
-
   return transaction.forget();
 }
 
 // static
 IDBTransaction*
 IDBTransaction::GetCurrent()
 {
   using namespace mozilla::ipc;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -327,20 +327,20 @@ URLCreateObjectURL_MediaStreamWarning=UR
 # LOCALIZATION NOTE: Do not translate MozAutoGainControl or autoGainControl.
 MozAutoGainControlWarning=mozAutoGainControl is deprecated. Use autoGainControl instead.
 # LOCALIZATION NOTE: Do not translate mozNoiseSuppression or noiseSuppression.
 MozNoiseSuppressionWarning=mozNoiseSuppression is deprecated. Use noiseSuppression instead.
 # LOCALIZATION NOTE: Do not translate xml:base.
 XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it.
 # LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
 WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated.  Please use ‘window.top’ instead.
-# LOCALIZATION NOTE: The first %S is the tag name of the element that starts the loop, the second %S is the element's ID.
-SVGReferenceLoopWarning=The SVG <%S> with ID “%S” has a reference loop.
-# LOCALIZATION NOTE: The first %S is the tag name of the element in the chain where the chain was broken, the second %S is the element's ID.
-SVGReferenceChainLengthExceededWarning=An SVG <%S> reference chain which is too long was abandoned at the element with ID “%S”.
+# LOCALIZATION NOTE: %S is the tag name of the element that starts the loop
+SVGReferenceLoopWarning=There is an SVG <%S> reference loop in this document, which will prevent the document rendering correctly.
+# LOCALIZATION NOTE: %S is the tag name of the element that starts the chain
+SVGReferenceChainLengthExceededWarning=There is an SVG <%S> reference chain which is too long in this document, which will prevent the document rendering correctly.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceEmpty=‘%S’ attribute of <script> element is empty.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceInvalidUri=‘%S’ attribute of <script> element is not a valid URI: “%S”
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceLoadFailed=Loading failed for the <script> with source “%S”.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceMalformed=<script> source URI is malformed: “%S”.
--- a/dom/media/ChannelMediaResource.cpp
+++ b/dom/media/ChannelMediaResource.cpp
@@ -259,16 +259,21 @@ ChannelMediaResource::OnStartRequest(nsI
     // and the server isn't sending Accept-Ranges:bytes then we don't
     // support seeking. We also can't seek in compressed streams.
     seekable = !isCompressed && acceptsRanges;
   } else {
     // Not an HTTP channel. Assume data will be sent from position zero.
     startOffset = 0;
   }
 
+  // Update principals before OnDataAvailable() putting the data in the cache.
+  // This is important, we want to make sure all principals are updated before
+  // any consumer can see the new data.
+  UpdatePrincipal();
+
   mCacheStream.NotifyDataStarted(mLoadID, startOffset);
   mCacheStream.SetTransportSeekable(seekable);
   mChannelStatistics.Start();
   mReopenOnError = false;
 
   mSuspendAgent.UpdateSuspendedStatusIfNeeded();
 
   // Fires an initial progress event.
@@ -408,22 +413,16 @@ nsresult
 ChannelMediaResource::OnDataAvailable(uint32_t aLoadID,
                                       nsIInputStream* aStream,
                                       uint32_t aCount)
 {
   // This might happen off the main thread.
   // Don't assert |mChannel.get() == aRequest| since reading mChannel here off
   // the main thread is a data race.
 
-  // Update principals before putting the data in the cache. This is important,
-  // we want to make sure all principals are updated before any consumer can see
-  // the new data.
-  // TODO: Handle the case where OnDataAvailable() runs off the main thread.
-  UpdatePrincipal();
-
   RefPtr<ChannelMediaResource> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
     "ChannelMediaResource::OnDataAvailable",
     [self, aCount]() { self->mChannelStatistics.AddBytes(aCount); });
   mCallback->AbstractMainThread()->Dispatch(r.forget());
 
   Closure closure{ aLoadID, this };
   uint32_t count = aCount;
@@ -779,19 +778,20 @@ ChannelMediaResource::CacheClientNotifyP
 
   mCallback->NotifyPrincipalChanged();
 }
 
 void
 ChannelMediaResource::UpdatePrincipal()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mChannel);
   nsCOMPtr<nsIPrincipal> principal;
   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  if (secMan && mChannel) {
+  if (secMan) {
     secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
     mCacheStream.UpdatePrincipal(principal);
   }
 }
 
 void
 ChannelMediaResource::CacheClientNotifySuspendedStatusChanged()
 {
--- a/dom/svg/SVGLength.cpp
+++ b/dom/svg/SVGLength.cpp
@@ -18,21 +18,17 @@ namespace mozilla {
 
 // Declare some helpers defined below:
 static void GetUnitString(nsAString& unit, uint16_t unitType);
 static uint16_t GetUnitTypeForString(const nsAString& unitStr);
 
 void
 SVGLength::GetValueAsString(nsAString &aValue) const
 {
-  char16_t buf[24];
-  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            u"%g",
-                            (double)mValue);
-  aValue.Assign(buf);
+  nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
 
   nsAutoString unitString;
   GetUnitString(unitString, mUnit);
   aValue.Append(unitString);
 }
 
 bool
 SVGLength::SetValueFromString(const nsAString &aString)
--- a/dom/svg/SVGPathSegUtils.cpp
+++ b/dom/svg/SVGPathSegUtils.cpp
@@ -70,25 +70,16 @@ SVGPathSegUtils::GetValueAsString(const 
       break;
 
     default:
       MOZ_ASSERT(false, "Unknown segment type");
       aValue = u"<unknown-segment-type>";
       return;
     }
   }
-
-  // nsTextFormatter::ssprintf is one of the nsTextFormatter methods that
-  // randomly appends '\0' to its output string, which means that the length
-  // of the output string is one too long. We need to manually remove that '\0'
-  // until nsTextFormatter is fixed.
-  //
-  if (aValue[aValue.Length() - 1] == char16_t('\0')) {
-    aValue.SetLength(aValue.Length() - 1);
-  }
 }
 
 
 static float
 CalcDistanceBetweenPoints(const Point& aP1, const Point& aP2)
 {
   return NS_hypot(aP2.x - aP1.x, aP2.y - aP1.y);
 }
--- a/dom/svg/nsSVGAngle.cpp
+++ b/dom/svg/nsSVGAngle.cpp
@@ -78,21 +78,17 @@ GetUnitTypeForString(const nsAString& un
   }
 
   return SVG_ANGLETYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
-  char16_t buf[24];
-  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            u"%g",
-                            (double)aValue);
-  aValueAsString.Assign(buf);
+  nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
 static bool
 GetValueFromString(const nsAString& aString,
--- a/dom/svg/nsSVGLength2.cpp
+++ b/dom/svg/nsSVGLength2.cpp
@@ -81,21 +81,17 @@ GetUnitTypeForString(const nsAString& un
   }
 
   return nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN;
 }
 
 static void
 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
 {
-  char16_t buf[24];
-  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
-                            u"%g",
-                            (double)aValue);
-  aValueAsString.Assign(buf);
+  nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
 
   nsAutoString unitString;
   GetUnitString(unitString, aUnitType);
   aValueAsString.Append(unitString);
 }
 
 static bool
 GetValueFromString(const nsAString& aString,
--- a/dom/svg/nsSVGTransform.cpp
+++ b/dom/svg/nsSVGTransform.cpp
@@ -13,69 +13,65 @@ namespace {
   const double kRadPerDegree = 2.0 * M_PI / 360.0;
 } // namespace
 
 namespace mozilla {
 
 void
 nsSVGTransform::GetValueAsString(nsAString& aValue) const
 {
-  char16_t buf[256];
-
   switch (mType) {
     case SVG_TRANSFORM_TRANSLATE:
       // The spec say that if Y is not provided, it is assumed to be zero.
       if (mMatrix._32 != 0)
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"translate(%g, %g)",
             mMatrix._31, mMatrix._32);
       else
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"translate(%g)",
             mMatrix._31);
       break;
     case SVG_TRANSFORM_ROTATE:
       if (mOriginX != 0.0f || mOriginY != 0.0f)
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"rotate(%g, %g, %g)",
             mAngle, mOriginX, mOriginY);
       else
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"rotate(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SCALE:
       if (mMatrix._11 != mMatrix._22)
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"scale(%g, %g)", mMatrix._11, mMatrix._22);
       else
-        nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+        nsTextFormatter::ssprintf(aValue,
             u"scale(%g)", mMatrix._11);
       break;
     case SVG_TRANSFORM_SKEWX:
-      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+      nsTextFormatter::ssprintf(aValue,
                                 u"skewX(%g)", mAngle);
       break;
     case SVG_TRANSFORM_SKEWY:
-      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+      nsTextFormatter::ssprintf(aValue,
                                 u"skewY(%g)", mAngle);
       break;
     case SVG_TRANSFORM_MATRIX:
-      nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+      nsTextFormatter::ssprintf(aValue,
           u"matrix(%g, %g, %g, %g, %g, %g)",
                             mMatrix._11, mMatrix._12,
                             mMatrix._21, mMatrix._22,
                             mMatrix._31, mMatrix._32);
       break;
     default:
-      buf[0] = '\0';
+      aValue.Truncate();
       NS_ERROR("unknown transformation type");
       break;
   }
-
-  aValue.Assign(buf);
 }
 
 void
 nsSVGTransform::SetMatrix(const gfxMatrix& aMatrix)
 {
   mType    = SVG_TRANSFORM_MATRIX;
   mMatrix  = aMatrix;
   // We set the other members here too, since operator== requires it and
--- a/dom/svg/nsSVGViewBox.cpp
+++ b/dom/svg/nsSVGViewBox.cpp
@@ -201,22 +201,20 @@ nsSVGViewBox::SetBaseValueString(const n
 
 void
 nsSVGViewBox::GetBaseValueString(nsAString& aValue) const
 {
   if (mBaseVal.none) {
     aValue.AssignLiteral("none");
     return;
   }
-  char16_t buf[200];
-  nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
+  nsTextFormatter::ssprintf(aValue,
                             u"%g %g %g %g",
                             (double)mBaseVal.x, (double)mBaseVal.y,
                             (double)mBaseVal.width, (double)mBaseVal.height);
-  aValue.Assign(buf);
 }
 
 
 already_AddRefed<dom::SVGAnimatedRect>
 nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement)
 {
   RefPtr<dom::SVGAnimatedRect> domAnimatedRect =
     sSVGAnimatedRectTearoffTable.GetTearoff(this);
--- a/dom/workers/ServiceWorker.cpp
+++ b/dom/workers/ServiceWorker.cpp
@@ -88,16 +88,22 @@ ServiceWorker::PostMessage(JSContext* aC
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
   if (!window || !window->GetExtantDoc()) {
     NS_WARNING("Trying to call post message from an invalid dom object.");
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
+  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
   UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
   ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
   aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
 }
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -823,20 +823,26 @@ ServiceWorkerManager::Register(mozIDOMWi
 {
   AssertIsOnMainThread();
 
   if (NS_WARN_IF(!aWindow)) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   auto* window = nsPIDOMWindowInner::From(aWindow);
+
+  // Don't allow a service worker to be registered if storage is restricted
+  // for the window.
+  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (!doc) {
-    return NS_ERROR_FAILURE;
-  }
+  MOZ_ASSERT(doc);
 
   // Don't allow service workers to register when the *document* is chrome.
   if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
   bool serviceWorkersTestingEnabled =
@@ -1069,24 +1075,29 @@ ServiceWorkerManager::GetRegistrations(m
 {
   AssertIsOnMainThread();
 
   if (NS_WARN_IF(!aWindow)) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   auto* window = nsPIDOMWindowInner::From(aWindow);
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (NS_WARN_IF(!doc)) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+
+  // Don't allow a service worker to access service worker registrations
+  // from a window with storage disabled.  If these windows can access
+  // the registration it increases the chance they can bypass the storage
+  // block via postMessage(), etc.
+  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+    return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
-  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
+  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(window->GetExtantDoc()->NodePrincipal()));
 
   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
   ErrorResult result;
   RefPtr<Promise> promise = Promise::Create(sgo, result);
   if (result.Failed()) {
     return result.StealNSResult();
   }
 
@@ -1182,24 +1193,29 @@ ServiceWorkerManager::GetRegistration(mo
 {
   AssertIsOnMainThread();
 
   if (NS_WARN_IF(!aWindow)) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   auto* window = nsPIDOMWindowInner::From(aWindow);
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (NS_WARN_IF(!doc)) {
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
+
+  // Don't allow a service worker to access service worker registrations
+  // from a window with storage disabled.  If these windows can access
+  // the registration it increases the chance they can bypass the storage
+  // block via postMessage(), etc.
+  auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
+  if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+    return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // Don't allow service workers to register when the *document* is chrome for
   // now.
-  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
+  MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(window->GetExtantDoc()->NodePrincipal()));
 
   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
   ErrorResult result;
   RefPtr<Promise> promise = Promise::Create(sgo, result);
   if (result.Failed()) {
     return result.StealNSResult();
   }
 
@@ -2173,17 +2189,25 @@ ServiceWorkerManager::GetServiceWorkerRe
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIDocument* aDoc)
 {
   MOZ_ASSERT(aDoc);
   nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
   nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
-  return GetServiceWorkerRegistrationInfo(principal, documentURI);
+  RefPtr<ServiceWorkerRegistrationInfo> reg =
+    GetServiceWorkerRegistrationInfo(principal, documentURI);
+  if (reg) {
+    auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+    if (storageAllowed != nsContentUtils::StorageAccess::eAllow) {
+      reg = nullptr;
+    }
+  }
+  return reg.forget();
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
                                                        nsIURI* aURI)
 {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aURI);
@@ -2466,16 +2490,21 @@ ServiceWorkerManager::MaybeCheckNavigati
 void
 ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                                                 nsIDocument* aDoc,
                                                 const nsAString& aDocumentId)
 {
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(aDoc);
 
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+  auto storageAllowed = nsContentUtils::StorageAllowedForDocument(aDoc);
+  MOZ_DIAGNOSTIC_ASSERT(storageAllowed == nsContentUtils::StorageAccess::eAllow);
+#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
   aRegistration->StartControllingADocument();
   mControlledDocuments.Put(aDoc, aRegistration);
   if (!aDocumentId.IsEmpty()) {
     aDoc->SetId(aDocumentId);
   }
   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
 }
 
@@ -3276,31 +3305,39 @@ ServiceWorkerManager::GetClient(nsIPrinc
                                      PromiseFlatString(aClientId).get());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return clientInfo;
   }
 
   nsCOMPtr<nsISupports> ptr;
   ifptr->GetData(getter_AddRefs(ptr));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
-  if (NS_WARN_IF(!doc)) {
+  if (NS_WARN_IF(!doc || !doc->GetInnerWindow())) {
     return clientInfo;
   }
 
   bool equals = false;
   aPrincipal->Equals(doc->NodePrincipal(), &equals);
   if (!equals) {
     return clientInfo;
   }
 
   if (!IsFromAuthenticatedOrigin(doc)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return clientInfo;
   }
 
+  // Don't let service worker see 3rd party iframes that are denied storage
+  // access.  We don't want these to communicate.
+  auto storageAccess =
+    nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+  if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
+    return clientInfo;
+  }
+
   clientInfo.reset(new ServiceWorkerClientInfo(doc));
   return clientInfo;
 }
 
 void
 ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
                                     const nsCString& aScope,
                                     uint64_t aServiceWorkerID,
@@ -3335,17 +3372,17 @@ ServiceWorkerManager::GetAllClients(nsIP
   while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
     nsCOMPtr<nsISupports> ptr;
     rv = enumerator->GetNext(getter_AddRefs(ptr));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       continue;
     }
 
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
-    if (!doc || !doc->GetWindow()) {
+    if (!doc || !doc->GetWindow() || !doc->GetInnerWindow()) {
       continue;
     }
 
     bool equals = false;
     Unused << aPrincipal->Equals(doc->NodePrincipal(), &equals);
     if (!equals) {
       continue;
     }
@@ -3353,16 +3390,24 @@ ServiceWorkerManager::GetAllClients(nsIP
     // Treat http windows with devtools opened as secure if the correct devtools
     // setting is enabled.
     if (!doc->GetWindow()->GetServiceWorkersTestingEnabled() &&
         !Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
         !IsFromAuthenticatedOrigin(doc)) {
       continue;
     }
 
+    // Don't let service worker find 3rd party iframes that are denied storage
+    // access.  We don't want these to communicate.
+    auto storageAccess =
+      nsContentUtils::StorageAllowedForWindow(doc->GetInnerWindow());
+    if (storageAccess != nsContentUtils::StorageAccess::eAllow) {
+      continue;
+    }
+
     // If we are only returning controlled Clients then skip any documents
     // that are for different registrations.  We also skip service workers
     // that don't match the ID of our calling service worker.  We should
     // only return Clients controlled by that precise service worker.
     if (!aIncludeUncontrolled) {
       ServiceWorkerRegistrationInfo* reg = mControlledDocuments.GetWeak(doc);
       if (!reg || reg->mScope != aScope || !reg->GetActive() ||
           reg->GetActive()->ID() != aServiceWorkerID) {
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -175,16 +175,17 @@ support-files =
   strict_mode_warning.js
   skip_waiting_installed_worker.js
   skip_waiting_scope/index.html
   thirdparty/iframe1.html
   thirdparty/iframe2.html
   thirdparty/register.html
   thirdparty/unregister.html
   thirdparty/sw.js
+  thirdparty/worker.js
   register_https.html
   gzip_redirect_worker.js
   sw_clients/navigator.html
   eval_worker.js
   test_eval_allowed.html^headers^
   opaque_intercept_worker.js
   notify_loaded.js
   test_request_context.js
@@ -233,16 +234,18 @@ skip-if = debug # Bug 1262224
 [test_csp_upgrade-insecure_intercept.html]
 [test_empty_serviceworker.html]
 [test_error_reporting.html]
 [test_escapedSlashes.html]
 [test_eval_allowed.html]
 [test_eventsource_intercept.html]
 [test_fetch_event.html]
 skip-if = (debug && e10s) # Bug 1262224
+[test_fetch_event_with_thirdpartypref.html]
+skip-if = (debug && e10s) # Bug 1262224
 [test_fetch_integrity.html]
 [test_file_blob_response.html]
 [test_file_blob_upload.html]
 [test_force_refresh.html]
 [test_gzip_redirect.html]
 [test_hsts_upgrade_intercept.html]
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_fetch_event_with_thirdpartypref.html
@@ -0,0 +1,93 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 94048 - test install event.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script src="utils.js"></script>
+<script class="testbody" type="text/javascript">
+
+  // NOTE: This is just test_fetch_event.html but with an alternate cookie
+  //       mode preference set to make sure that setting the preference does
+  //       not break interception as observed in bug 1336364.
+  // TODO: Refactor this test so it doesn't duplicate so much code logic.
+
+  SimpleTest.requestCompleteLog();
+
+  var registration;
+  function simpleRegister() {
+    return navigator.serviceWorker.register("fetch_event_worker.js", { scope: "./fetch" })
+      .then(swr => {
+        registration = swr;
+        return waitForState(swr.installing, 'activated');
+      });
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(success) {
+      ok(success, "Service worker should be unregistered successfully");
+    }, function(e) {
+      dump("SW unregistration error: " + e + "\n");
+    });
+  }
+
+  function testController() {
+    var p = new Promise(function(resolve, reject) {
+      var reloaded = false;
+      window.onmessage = function(e) {
+        if (e.data.status == "ok") {
+          ok(e.data.result, e.data.message);
+        } else if (e.data.status == "done") {
+          if (reloaded) {
+            window.onmessage = null;
+            w.close();
+            resolve();
+          } else {
+            w.location.reload();
+            reloaded = true;
+          }
+        }
+      }
+    });
+
+    var w = window.open("fetch/index.html");
+    return p;
+  }
+
+  function runTest() {
+    simpleRegister()
+      .then(testController)
+      .then(unregister)
+      .then(function() {
+        SimpleTest.finish();
+      }).catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+        SimpleTest.finish();
+      });
+  }
+
+  const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["javascript.options.streams", true],
+    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN],
+    ["dom.streams.enabled", true],
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -75,101 +75,165 @@ function runTest(aExpectedResponses) {
       }
     } else {
       ok(false, "Expected " + expected.status + " got " + status);
     }
     responsesIndex++;
   };
 }
 
-function testShouldIntercept(done) {
-  runTest([{
-    status: "ok"
-  }, {
-    status: "registrationdone",
-    next: function() {
-      iframe.addEventListener("load", testIframeLoaded);
-      iframe.src = origin + basePath + "iframe1.html";
-    }
-  }, {
-    status: "networkresponse",
-    next: loadThirdPartyIframe
-  }, {
-    status: "swresponse",
-    next: function() {
-      iframe.src = thirdPartyOrigin + basePath + "unregister.html";
-    }
-  }, {
-    status: "unregistrationdone",
-    next: function() {
-      window.onmessage = null;
-      ok(true, "Test finished successfully");
-      done();
-    }
-  }]);
+// Verify that we can register and intercept a 3rd party iframe with
+// the given cookie policy.
+function testShouldIntercept(policy, done) {
+  SpecialPowers.pushPrefEnv({"set": [
+      ["network.cookie.cookieBehavior", policy]
+  ]}, function() {
+    runTest([{
+      status: "ok"
+    }, {
+      status: "registrationdone",
+      next: function() {
+        iframe.addEventListener("load", testIframeLoaded);
+        iframe.src = origin + basePath + "iframe1.html";
+      }
+    }, {
+      status: "networkresponse",
+    }, {
+      status: "worker-networkresponse",
+      next: loadThirdPartyIframe
+    }, {
+      status: "swresponse",
+    }, {
+      status: "worker-swresponse",
+      next: function() {
+        iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+      }
+    }, {
+      status: "controlled",
+    }, {
+      status: "unregistrationdone",
+      next: function() {
+        window.onmessage = null;
+        ok(true, "Test finished successfully");
+        done();
+      }
+    }]);
+  });
 }
 
-function testShouldNotIntercept(done) {
-  runTest([{
-    status: "ok"
-  }, {
-    status: "registrationdone",
-    next: function() {
-      iframe.addEventListener("load", testIframeLoaded);
-      iframe.src = origin + basePath + "iframe1.html";
-    }
-  }, {
-    status: "networkresponse",
-    next: loadThirdPartyIframe
-  }, {
-    status: "networkresponse",
-    next: function() {
-      iframe.src = thirdPartyOrigin + basePath + "unregister.html";
-    }
-  }, {
-    status: "unregistrationdone",
-    next: function() {
-      window.onmessage = null;
-      ok(true, "Test finished successfully");
-      done();
-    }
-  }]);
+// Verify that we cannot register a service worker in a 3rd party
+// iframe with the given cookie policy.
+function testShouldNotRegister(policy, done) {
+  SpecialPowers.pushPrefEnv({"set": [
+      ["network.cookie.cookieBehavior", policy]
+  ]}, function() {
+    runTest([{
+      status: "registrationfailed",
+      next: function() {
+        iframe.addEventListener("load", testIframeLoaded);
+        iframe.src = origin + basePath + "iframe1.html";
+      }
+    }, {
+      status: "networkresponse",
+    }, {
+      status: "worker-networkresponse",
+      next: loadThirdPartyIframe
+    }, {
+      status: "networkresponse",
+    }, {
+      status: "worker-networkresponse",
+      next: function() {
+        window.onmessage = null;
+        ok(true, "Test finished successfully");
+        done();
+      }
+    }]);
+  });
+}
+
+// Verify that if a service worker is already registered a 3rd
+// party iframe will still not be intercepted with the given cookie
+// policy.
+function testShouldNotIntercept(policy, done) {
+  SpecialPowers.pushPrefEnv({"set": [
+      ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
+  ]}, function() {
+    runTest([{
+      status: "ok"
+    }, {
+      status: "registrationdone",
+      next: function() {
+        iframe.addEventListener("load", testIframeLoaded);
+        SpecialPowers.pushPrefEnv({"set": [
+            ["network.cookie.cookieBehavior", policy],
+          ]}, function() {
+            iframe.src = origin + basePath + "iframe1.html";
+          });
+      }
+    }, {
+      status: "networkresponse",
+    }, {
+      status: "worker-networkresponse",
+      next: loadThirdPartyIframe
+    }, {
+      status: "networkresponse",
+    }, {
+      status: "worker-networkresponse",
+      next: function() {
+        iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+      }
+    }, {
+      status: "uncontrolled",
+    }, {
+      status: "getregistrationfailed",
+      next: function() {
+        SpecialPowers.pushPrefEnv({"set": [
+            ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT],
+          ]}, function() {
+            iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+          });
+      }
+    }, {
+      status: "controlled",
+    }, {
+      status: "unregistrationdone",
+      next: function() {
+        window.onmessage = null;
+        ok(true, "Test finished successfully");
+        done();
+      }
+    }]);
+  });
 }
 
 const COOKIE_BEHAVIOR_ACCEPT        = 0;
 const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
 const COOKIE_BEHAVIOR_REJECT        = 2;
 const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
 
 let steps = [() => {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["browser.dom.window.dump.enabled", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
   ]}, next);
 }, () => {
-  testShouldIntercept(next);
+  testShouldIntercept(COOKIE_BEHAVIOR_ACCEPT, next);
 }, () => {
-  SpecialPowers.pushPrefEnv({"set": [
-    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
-  ]}, next);
+  testShouldNotRegister(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
 }, () => {
-  testShouldNotIntercept(next);
+  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECTFOREIGN, next);
 }, () => {
-  SpecialPowers.pushPrefEnv({"set": [
-    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECT]
-  ]}, next);
+  testShouldNotRegister(COOKIE_BEHAVIOR_REJECT, next);
 }, () => {
-  testShouldIntercept(next);
+  testShouldNotIntercept(COOKIE_BEHAVIOR_REJECT, next);
 }, () => {
-  SpecialPowers.pushPrefEnv({"set": [
-    ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_LIMITFOREIGN]
-  ]}, next);
+  testShouldNotRegister(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
 }, () => {
-  testShouldIntercept(next);
+  testShouldNotIntercept(COOKIE_BEHAVIOR_LIMITFOREIGN, next);
 }];
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/thirdparty/iframe1.html
+++ b/dom/workers/test/serviceworkers/thirdparty/iframe1.html
@@ -12,16 +12,18 @@
       let message = eval(event.data);
 
       dump("got message " + JSON.stringify(message) + "\n");
       if (message.source == "parent") {
         document.getElementById("iframe2").src = message.href;
       }
       else if (message.source == "iframe") {
         parent.postMessage(event.data, "*");
+      } else if (message.source == "worker") {
+        parent.postMessage(event.data, "*");
       }
     }
   </script>
 
 </head>
 
 <body onload="window.addEventListener('message', messageListener, false);">
   <iframe id="iframe2"></iframe>
--- a/dom/workers/test/serviceworkers/thirdparty/iframe2.html
+++ b/dom/workers/test/serviceworkers/thirdparty/iframe2.html
@@ -1,7 +1,14 @@
 <!DOCTYPE html>
 <script>
   window.parent.postMessage({
     source: "iframe",
     status: "networkresponse"
   }, "*");
+  var w = new Worker('worker.js');
+  w.onmessage = function(evt) {
+    window.parent.postMessage({
+      source: 'worker',
+      status: evt.data,
+    }, '*');
+  };
 </script>
--- a/dom/workers/test/serviceworkers/thirdparty/register.html
+++ b/dom/workers/test/serviceworkers/thirdparty/register.html
@@ -18,10 +18,12 @@
     .then(function(registration) {
       if (registration.installing) {
         registration.installing.onstatechange = function(e) {
           done(registration);
         };
       } else {
         done(registration);
       }
+    }).catch(function(e) {
+      window.parent.postMessage({status: "registrationfailed"}, "*");
     });
 </script>
--- a/dom/workers/test/serviceworkers/thirdparty/sw.js
+++ b/dom/workers/test/serviceworkers/thirdparty/sw.js
@@ -1,14 +1,29 @@
 self.addEventListener("fetch", function(event) {
   dump("fetch " + event.request.url + "\n");
-  if (event.request.url.indexOf("iframe2.html") >= 0) {
+  if (event.request.url.includes("iframe2.html")) {
     var body =
       "<script>" +
         "window.parent.postMessage({" +
           "source: 'iframe', status: 'swresponse'" +
         "}, '*');" +
+        "var w = new Worker('worker.js');" +
+        "w.onmessage = function(evt) {" +
+          "window.parent.postMessage({" +
+            "source: 'worker'," +
+            "status: evt.data," +
+          "}, '*');" +
+      "};" +
       "</script>";
     event.respondWith(new Response(body, {
       headers: {'Content-Type': 'text/html'}
     }));
+    return;
+  }
+  if (event.request.url.includes("worker.js")) {
+    var body = "self.postMessage('worker-swresponse');";
+    event.respondWith(new Response(body, {
+      headers: {'Content-Type': 'application/javascript'}
+    }));
+    return;
   }
 });
--- a/dom/workers/test/serviceworkers/thirdparty/unregister.html
+++ b/dom/workers/test/serviceworkers/thirdparty/unregister.html
@@ -1,11 +1,19 @@
 <!DOCTYPE html>
 <script>
+  if (navigator.serviceWorker.controller) {
+    window.parent.postMessage({status: "controlled"}, "*");
+  } else {
+    window.parent.postMessage({status: "uncontrolled"}, "*");
+  }
+
   navigator.serviceWorker.getRegistration(".").then(function(registration) {
     if(!registration) {
       return;
     }
     registration.unregister().then(() => {
       window.parent.postMessage({status: "unregistrationdone"}, "*");
     });
+  }).catch(function(e) {
+    window.parent.postMessage({status: "getregistrationfailed"}, "*");
   });
 </script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/worker.js
@@ -0,0 +1,1 @@
+self.postMessage('worker-networkresponse');
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -2738,23 +2738,24 @@ EditorBase::SetTextImpl(Selection& aSele
   // We don't support undo here, so we don't really need all of the transaction
   // machinery, therefore we can run our transaction directly, breaking all of
   // the rules!
   nsresult rv = aCharData.SetData(aString);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Only set selection to insertion point if editor gives permission
-  if (GetShouldTxnSetSelection()) {
+  {
+    // Create a nested scope to not overwrite rv from the outer scope.
     RefPtr<Selection> selection = GetSelection();
-    DebugOnly<nsresult> rv = selection->Collapse(&aCharData, length);
+    DebugOnly<nsresult> rv = selection->Collapse(&aCharData, aString.Length());
     NS_ASSERTION(NS_SUCCEEDED(rv),
                  "Selection could not be collapsed after insert");
   }
+
   mRangeUpdater.SelAdjDeleteText(&aCharData, 0, length);
   mRangeUpdater.SelAdjInsertText(aCharData, 0, aString);
 
   // Let listeners know what happened
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       if (length) {
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -868,19 +868,16 @@ TextEditRules::WillSetText(Selection& aS
     return NS_OK;
   }
 
   nsINode* curNode = rootElement->GetFirstChild();
   if (NS_WARN_IF(!EditorBase::IsTextNode(curNode))) {
     return NS_OK;
   }
 
-  // don't change my selection in subtransactions
-  AutoTransactionsConserveSelection dontChangeMySelection(textEditor);
-
   // Even if empty text, we don't remove text node and set empty text
   // for performance
   nsresult rv = textEditor->SetTextImpl(aSelection, tString,
                                         *curNode->GetAsText());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -248,16 +248,17 @@ skip-if = toolkit == 'android' # bug 131
 [test_bug1332876.html]
 [test_bug1352799.html]
 [test_bug1355792.html]
 [test_bug1358025.html]
 [test_bug1361008.html]
 [test_bug1368544.html]
 [test_bug1385905.html]
 [test_bug1394758.html]
+[test_bug1399722.html]
 
 [test_CF_HTML_clipboard.html]
 subsuite = clipboard
 [test_composition_event_created_in_chrome.html]
 [test_contenteditable_focus.html]
 [test_documentCharacterSet.html]
 [test_dom_input_event_on_htmleditor.html]
 skip-if = toolkit == 'android' # bug 1054087
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1399722.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1399722
+-->
+<html>
+<head>
+  <title>Test for Bug 1399722</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399722">Mozilla Bug 1399722</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+
+</div>
+
+<input spellcheck="true" onkeypress="if (event.key=='Enter')this.value='';">
+<pre id="test">
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  let elm = document.querySelector("input");
+
+  elm.focus();
+  synthesizeKey("A", {});
+  synthesizeKey("B", {});
+  synthesizeKey("KEY_Enter", {});
+  synthesizeKey("C", {});
+  synthesizeKey("D", {});
+  is(elm.value, "CD", "Can type into the textbox successfully after the onkeypress handler deleting the value");
+
+  SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -233,18 +233,17 @@ DrawTargetD2D1::DrawSurface(SourceSurfac
                                                      D2D1_EXTEND_MODE_CLAMP,
                                                      D2D1_EXTEND_MODE_CLAMP,
                                                      D2DInterpolationMode(aSurfOptions.mSamplingFilter)),
                           D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
                           getter_AddRefs(brush));
     mDC->FillRectangle(D2DRect(aDest), brush);
   }
 
-  Rect destBounds = mTransform.TransformBounds(aDest);
-  FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()), &destBounds);
+  FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color()));
 }
 
 void
 DrawTargetD2D1::DrawFilter(FilterNode *aNode,
                            const Rect &aSourceRect,
                            const Point &aDestPoint,
                            const DrawOptions &aOptions)
 {
@@ -1340,17 +1339,17 @@ DrawTargetD2D1::PrepareForDrawing(Compos
   if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
     PushClipsToDC(mDC);
   }
 
   FlushTransformToDC();
 }
 
 void
-DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern, const Rect* aAffectedRect /* = nullptr */)
+DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
 {
   bool patternSupported = IsPatternSupportedByD2D(aPattern);
 
   if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
     if (aOp != CompositionOp::OP_OVER)
       mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
     return;
   }
@@ -1403,35 +1402,25 @@ DrawTargetD2D1::FinalizeDrawing(Composit
     RefPtr<ID2D1Effect> blendEffect;
     HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
 
     if (FAILED(hr) || !blendEffect) {
       gfxWarning() << "Failed to create blend effect!";
       return;
     }
 
-    Point outOffset;
-    // We don't need to preserve the current content of this layer if the output
+    // We don't need to preserve the current content of this layer as the output
     // of the blend effect should completely replace it.
-    bool shouldPreserveContent = !!aAffectedRect && !aAffectedRect->Contains(Rect(0, 0, mSize.width, mSize.height));
-    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(shouldPreserveContent, aAffectedRect, &outOffset);
+    RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(false);
     if (!tmpImage) {
       return;
     }
 
     blendEffect->SetInput(0, tmpImage);
     blendEffect->SetInput(1, source);
-
-    if (outOffset != Point()) {
-      RefPtr<ID2D1Effect> transformEffect;
-      mDC->CreateEffect(CLSID_D2D12DAffineTransform, getter_AddRefs(transformEffect));
-      transformEffect->SetInput(0, tmpImage);
-      transformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, D2D1::Matrix3x2F::Translation(outOffset.x, outOffset.y));
-      blendEffect->SetInputEffect(0, transformEffect);
-    }
     blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
 
     mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
 
     // This may seem a little counter intuitive. If this is false, we go through the regular
     // codepaths and set it to true. When this was true, GetImageForLayerContent will return
     // a bitmap for the current command list and we will no longer have a complex blend
     // with a list for tmpImage. Therefore we can set it to false again.
@@ -1513,24 +1502,20 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D
     if (!iter->mIsPixelAligned) {
       aIsPixelAligned = false;
     }
   }
   return true;
 }
 
 already_AddRefed<ID2D1Image>
-DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent, const Rect* aBounds /* = nullptr */, Point* aOutOffset /* = nullptr */) 
+DrawTargetD2D1::GetImageForLayerContent(bool aShouldPreserveContent)
 {
   PopAllClips();
 
-  if (aOutOffset) {
-    *aOutOffset = Point();
-  }
-
   if (!CurrentLayer().mCurrentList) {
     RefPtr<ID2D1Bitmap> tmpBitmap;
     HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap));
     if (FAILED(hr)) {
       gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat;
       // If it's a recreate target error, return and handle it elsewhere.
       if (hr == D2DERR_RECREATE_TARGET) {
         mDC->Flush();
@@ -1550,30 +1535,20 @@ DrawTargetD2D1::GetImageForLayerContent(
     list->Close();
 
     RefPtr<ID2D1Bitmap1> tmpBitmap;
     if (mDidComplexBlendWithListInList) {
       D2D1_BITMAP_PROPERTIES1 props =
         D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_TARGET,
                                 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
                                                   D2D1_ALPHA_MODE_PREMULTIPLIED));
-      D2D1_SIZE_U size = mBitmap->GetPixelSize();
-      D2D1_POINT_2F offset = D2D1::Point2F();
-      if (aBounds) {
-        size.width = aBounds->width;
-        size.height = aBounds->height;
-        offset.x = -aBounds->x;
-        offset.y = -aBounds->y;
-        aOutOffset->x = aBounds->x;
-        aOutOffset->y = aBounds->y;
-      }
-      mDC->CreateBitmap(size, nullptr, 0, &props, getter_AddRefs(tmpBitmap));
+      mDC->CreateBitmap(mBitmap->GetPixelSize(), nullptr, 0, &props, getter_AddRefs(tmpBitmap));
       mDC->SetTransform(D2D1::IdentityMatrix());
       mDC->SetTarget(tmpBitmap);
-      mDC->DrawImage(list, offset, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
+      mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
       mDC->SetTarget(CurrentTarget());
     }
 
     DCCommandSink sink(mDC);
 
     if (aShouldPreserveContent) {
       list->Stream(&sink);
       PushAllClips();
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -175,33 +175,27 @@ private:
 
   typedef std::unordered_set<DrawTargetD2D1*> TargetSet;
 
   // This function will mark the surface as changing, and make sure any
   // copy-on-write snapshots are notified.
   void MarkChanged();
   bool ShouldClipTemporarySurfaceDrawing(CompositionOp aOp, const Pattern& aPattern, bool aClipIsComplex);
   void PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern);
-  // aAffectedRect may be used to supply the bounds of the drawing operations in order to prevent
-  // excessive surface area being operated on
-  void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern, const Rect* aAffectedRect = nullptr);
+  void FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern);
   void FlushTransformToDC() {
     if (mTransformDirty) {
       mDC->SetTransform(D2DMatrix(mTransform));
       mTransformDirty = false;
     }
   }
   void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
 
   // Must be called with all clips popped and an identity matrix set.
-  // aBounds can be specified to allow the function to return only part of the layer contents
-  // aOutOffset will return the offset that should be applied to move the ID2D1Image onto the
-  // correct portion of the destination layer. If !aBounds this will always be 0,0 and can be
-  // ignored.
-  already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true, const Rect* aBounds = nullptr, Point* aOutOffset = nullptr);
+  already_AddRefed<ID2D1Image> GetImageForLayerContent(bool aShouldPreserveContent = true);
 
   ID2D1Image* CurrentTarget()
   {
     if (CurrentLayer().mCurrentList) {
       return CurrentLayer().mCurrentList;
     }
     return mBitmap;
   }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -59,16 +59,17 @@ ImageBridgeParent::Setup()
   }
 }
 
 ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
                                      ProcessId aChildProcessId)
   : mMessageLoop(aLoop)
   , mSetChildThreadPriority(false)
   , mClosed(false)
+  , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // creates the map only if it has not been created already, so it is safe
   // with several bridges
   {
     MonitorAutoLock lock(*sImageBridgesLock);
     sImageBridges[aChildProcessId] = this;
@@ -359,23 +360,16 @@ RefPtr<ImageBridgeParent>
 ImageBridgeParent::GetInstance(ProcessId aId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MonitorAutoLock lock(*sImageBridgesLock);
   NS_ASSERTION(sImageBridges.count(aId) == 1, "ImageBridgeParent for the process");
   return sImageBridges[aId];
 }
 
-void
-ImageBridgeParent::OnChannelConnected(int32_t aPid)
-{
-  mCompositorThreadHolder = GetCompositorThreadHolder();
-}
-
-
 bool
 ImageBridgeParent::AllocShmem(size_t aSize,
                       ipc::SharedMemory::SharedMemoryType aType,
                       ipc::Shmem* aShmem)
 {
   if (mClosed) {
     return false;
   }
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -117,18 +117,16 @@ public:
 
   static bool NotifyImageComposites(nsTArray<ImageCompositeNotificationInfo>& aNotifications);
 
   virtual bool UsesImageBridge() const override { return true; }
 
   virtual bool IPCOpen() const override { return !mClosed; }
 
 protected:
-  void OnChannelConnected(int32_t pid) override;
-
   void Bind(Endpoint<PImageBridgeParent>&& aEndpoint);
 
 private:
   void DeferredDestroy();
   MessageLoop* mMessageLoop;
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<ImageBridgeParent> mSelfRef;
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -46,24 +46,24 @@ parent:
   // next Update call.
   async InitReadLocks(ReadLockInit[] locks);
 
   sync Create(IntSize aSize);
   async DeleteCompositorAnimations(uint64_t[] aIds);
   async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                        LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
                        WebRenderScrollData aScrollData,
-                       OpUpdateResource[] aResourceUpdates, Shmem[] aResourceData,
+                       OpUpdateResource[] aResourceUpdates, Shmem[] aSmallShmems, Shmem[] aLargeShmems,
                        IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
   sync SetDisplayListSync(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
                           LayoutSize aContentSize, ByteBuffer aDL, BuiltDisplayListDescriptor aDLDesc,
                           WebRenderScrollData aScrollData,
-                          OpUpdateResource[] aResourceUpdates, Shmem[] aResourceData,
+                          OpUpdateResource[] aResourceUpdates, Shmem[] aSmallShmems, Shmem[] aLargeShmems,
                           IdNamespace aIdNamespace, TimeStamp txnStartTime, TimeStamp fwdTime);
-  async UpdateResources(OpUpdateResource[] aResourceUpdates, Shmem[] aResourceData);
+  async UpdateResources(OpUpdateResource[] aResourceUpdates, Shmem[] aSmallShmems, Shmem[] aLargeShmems);
   async ParentCommands(WebRenderParentCommand[] commands);
   sync GetSnapshot(PTexture texture);
   async AddPipelineIdForCompositable(PipelineId aImageId, CompositableHandle aHandle, bool aAsync);
   async RemovePipelineIdForCompositable(PipelineId aPipelineId);
   async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
   async RemoveExternalImageId(ExternalImageId aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -57,23 +57,23 @@ struct OpUpdateAsyncImagePipeline {
   LayerRect scBounds;
   Matrix4x4 scTransform;
   MaybeIntSize scaleToSize;
   ImageRendering filter;
   MixBlendMode mixBlendMode;
 };
 
 union WebRenderParentCommand {
-  OpAddExternalImage;
   OpUpdateAsyncImagePipeline;
   CompositableOperation;
   OpAddCompositorAnimations;
 };
 
 struct OffsetRange {
+  uint32_t source;
   uint32_t start;
   uint32_t length;
 };
 
 struct OpAddImage {
   ImageDescriptor descriptor;
   OffsetRange bytes;
   uint16_t tiling;
@@ -130,12 +130,13 @@ union OpUpdateResource {
   OpAddBlobImage;
   OpUpdateImage;
   OpUpdateBlobImage;
   OpDeleteImage;
   OpAddRawFont;
   OpDeleteFont;
   OpAddFontInstance;
   OpDeleteFontInstance;
+  OpAddExternalImage;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -25,127 +25,189 @@ ShmSegmentsWriter::~ShmSegmentsWriter()
 }
 
 layers::OffsetRange
 ShmSegmentsWriter::Write(Range<uint8_t> aBytes)
 {
   const size_t start = mCursor;
   const size_t length = aBytes.length();
 
+  if (length >= mChunkSize * 4) {
+    auto range = AllocLargeChunk(length);
+    uint8_t* dstPtr = mLargeAllocs.LastElement().get<uint8_t>();
+    memcpy(dstPtr, aBytes.begin().get(), length);
+    return range;
+  }
+
   int remainingBytesToCopy = length;
 
   size_t srcCursor = 0;
   size_t dstCursor = mCursor;
 
   while (remainingBytesToCopy > 0) {
-    if (dstCursor >= mData.Length() * mChunkSize) {
+    if (dstCursor >= mSmallAllocs.Length() * mChunkSize) {
       AllocChunk();
       continue;
     }
 
-    const size_t dstMaxOffset = mChunkSize * mData.Length();
-    const size_t dstBaseOffset = mChunkSize * (mData.Length() - 1);
+    const size_t dstMaxOffset = mChunkSize * mSmallAllocs.Length();
+    const size_t dstBaseOffset = mChunkSize * (mSmallAllocs.Length() - 1);
 
     MOZ_ASSERT(dstCursor >= dstBaseOffset);
     MOZ_ASSERT(dstCursor <= dstMaxOffset);
 
     size_t availableRange = dstMaxOffset - dstCursor;
     size_t copyRange = std::min<int>(availableRange, remainingBytesToCopy);
 
     uint8_t* srcPtr = &aBytes[srcCursor];
-    uint8_t* dstPtr = mData.LastElement().get<uint8_t>() + (dstCursor - dstBaseOffset);
+    uint8_t* dstPtr = mSmallAllocs.LastElement().get<uint8_t>() + (dstCursor - dstBaseOffset);
 
     memcpy(dstPtr, srcPtr, copyRange);
 
     srcCursor += copyRange;
     dstCursor += copyRange;
     remainingBytesToCopy -= copyRange;
 
     // sanity check
     MOZ_ASSERT(remainingBytesToCopy >= 0);
   }
 
   mCursor += length;
 
-  return layers::OffsetRange(start, length);
+  return layers::OffsetRange(0, start, length);
 }
 
 void
 ShmSegmentsWriter::AllocChunk()
 {
   ipc::Shmem shm;
   auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
   if (!mShmAllocator->AllocShmem(mChunkSize, shmType, &shm)) {
-    MOZ_CRASH("Shared memory allocation failed");
+    gfxCriticalError() << "ShmSegmentsWriter failed to allocate chunk #" << mSmallAllocs.Length();
+    MOZ_CRASH();
   }
-  mData.AppendElement(shm);
+  mSmallAllocs.AppendElement(shm);
+}
+
+layers::OffsetRange
+ShmSegmentsWriter::AllocLargeChunk(size_t aSize)
+{
+  ipc::Shmem shm;
+  auto shmType = ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
+  if (!mShmAllocator->AllocShmem(aSize, shmType, &shm)) {
+    gfxCriticalError() << "ShmSegmentsWriter failed to allocate large chunk of size " << aSize;
+    MOZ_CRASH();
+  }
+  mLargeAllocs.AppendElement(shm);
+
+  return layers::OffsetRange(mLargeAllocs.Length(), 0, aSize);
 }
 
 void
-ShmSegmentsWriter::Flush(nsTArray<ipc::Shmem>& aInto)
+ShmSegmentsWriter::Flush(nsTArray<ipc::Shmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs)
 {
-  aInto.Clear();
-  mData.SwapElements(aInto);
+  aSmallAllocs.Clear();
+  aLargeAllocs.Clear();
+  mSmallAllocs.SwapElements(aSmallAllocs);
+  mLargeAllocs.SwapElements(aLargeAllocs);
 }
 
 void
 ShmSegmentsWriter::Clear()
 {
   if (mShmAllocator) {
-    for (auto& shm : mData) {
+    for (auto& shm : mSmallAllocs) {
+      mShmAllocator->DeallocShmem(shm);
+    }
+    for (auto& shm : mLargeAllocs) {
       mShmAllocator->DeallocShmem(shm);
     }
   }
-  mData.Clear();
+  mSmallAllocs.Clear();
+  mLargeAllocs.Clear();
   mCursor = 0;
 }
 
-ShmSegmentsReader::ShmSegmentsReader(const nsTArray<ipc::Shmem>& aShmems)
-: mData(aShmems)
+ShmSegmentsReader::ShmSegmentsReader(const nsTArray<ipc::Shmem>& aSmallShmems,
+                                     const nsTArray<ipc::Shmem>& aLargeShmems)
+: mSmallAllocs(aSmallShmems)
+, mLargeAllocs(aLargeShmems)
 , mChunkSize(0)
 {
-  if (mData.IsEmpty()) {
+  if (mSmallAllocs.IsEmpty()) {
     return;
   }
 
-  mChunkSize = mData[0].Size<uint8_t>();
+  mChunkSize = mSmallAllocs[0].Size<uint8_t>();
 
   // Check that all shmems are readable and have the same size. If anything
   // isn't right, set mChunkSize to zero which signifies that the reader is
   // in an invalid state and Read calls will return false;
-  for (const auto& shm : mData) {
+  for (const auto& shm : mSmallAllocs) {
     if (!shm.IsReadable()
         || shm.Size<uint8_t>() != mChunkSize
         || shm.get<uint8_t>() == nullptr) {
       mChunkSize = 0;
       return;
     }
   }
+
+  for (const auto& shm : mLargeAllocs) {
+    if (!shm.IsReadable()
+        || shm.get<uint8_t>() == nullptr) {
+      mChunkSize = 0;
+      return;
+    }
+  }
 }
 
 bool
-ShmSegmentsReader::Read(layers::OffsetRange aRange, wr::Vec_u8& aInto)
+ShmSegmentsReader::ReadLarge(const layers::OffsetRange& aRange, wr::Vec_u8& aInto)
 {
+  // source = zero is for small allocs.
+  MOZ_RELEASE_ASSERT(aRange.source() != 0);
+  if (aRange.source() > mLargeAllocs.Length()) {
+    return false;
+  }
+  size_t id = aRange.source() - 1;
+  const ipc::Shmem& shm = mLargeAllocs[id];
+  if (shm.Size<uint8_t>() < aRange.length()) {
+    return false;
+  }
+
+  uint8_t* srcPtr = shm.get<uint8_t>();
+  aInto.PushBytes(Range<uint8_t>(srcPtr, aRange.length()));
+
+  return true;
+}
+
+bool
+ShmSegmentsReader::Read(const layers::OffsetRange& aRange, wr::Vec_u8& aInto)
+{
+  if (aRange.source() != 0) {
+    return ReadLarge(aRange, aInto);
+  }
+
   if (mChunkSize == 0) {
     return false;
   }
 
-  if (aRange.start() + aRange.length() > mChunkSize * mData.Length()) {
+  if (aRange.start() + aRange.length() > mChunkSize * mSmallAllocs.Length()) {
     return false;
   }
 
   size_t initialLength = aInto.Length();
 
   size_t srcCursor = aRange.start();
   int remainingBytesToCopy = aRange.length();
   while (remainingBytesToCopy > 0) {
     const size_t shm_idx = srcCursor / mChunkSize;
     const size_t ptrOffset = srcCursor % mChunkSize;
     const size_t copyRange = std::min<int>(remainingBytesToCopy, mChunkSize - ptrOffset);
-    uint8_t* srcPtr = mData[shm_idx].get<uint8_t>() + ptrOffset;
+    uint8_t* srcPtr = mSmallAllocs[shm_idx].get<uint8_t>() + ptrOffset;
 
     aInto.PushBytes(Range<uint8_t>(srcPtr, copyRange));
 
     srcCursor += copyRange;
     remainingBytesToCopy -= copyRange;
   }
 
   return aInto.Length() - initialLength == aRange.length();
@@ -168,16 +230,22 @@ void
 IpcResourceUpdateQueue::AddBlobImage(ImageKey key, const ImageDescriptor& aDescriptor,
                                      Range<uint8_t> aBytes)
 {
   auto bytes = mWriter.Write(aBytes);
   mUpdates.AppendElement(layers::OpAddBlobImage(aDescriptor, bytes, 0, key));
 }
 
 void
+IpcResourceUpdateQueue::AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey)
+{
+  mUpdates.AppendElement(layers::OpAddExternalImage(aExtId, aKey));
+}
+
+void
 IpcResourceUpdateQueue::UpdateImageBuffer(ImageKey aKey,
                                           const ImageDescriptor& aDescriptor,
                                           Range<uint8_t> aBytes)
 {
   auto bytes = mWriter.Write(aBytes);
   mUpdates.AppendElement(layers::OpUpdateImage(aDescriptor, bytes, aKey));
 }
 
@@ -227,21 +295,22 @@ IpcResourceUpdateQueue::AddFontInstance(
 void
 IpcResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey)
 {
   mUpdates.AppendElement(layers::OpDeleteFontInstance(aKey));
 }
 
 void
 IpcResourceUpdateQueue::Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
-                              nsTArray<ipc::Shmem>& aResourceData)
+                              nsTArray<ipc::Shmem>& aSmallAllocs,
+                              nsTArray<ipc::Shmem>& aLargeAllocs)
 {
   aUpdates.Clear();
   mUpdates.SwapElements(aUpdates);
-  mWriter.Flush(aResourceData);
+  mWriter.Flush(aSmallAllocs, aLargeAllocs);
 }
 
 void
 IpcResourceUpdateQueue::Clear()
 {
   mWriter.Clear();
   mUpdates.Clear();
 }
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -9,65 +9,73 @@
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace ipc {
 class IShmemAllocator;
 }
 namespace wr {
 
+/// ShmSegmentsWriter pushes bytes in a sequence of fixed size shmems for small
+/// allocations and creates dedicated shmems for large allocations.
 class ShmSegmentsWriter {
 public:
   ShmSegmentsWriter(ipc::IShmemAllocator* aAllocator, size_t aChunkSize);
   ~ShmSegmentsWriter();
 
   layers::OffsetRange Write(Range<uint8_t> aBytes);
 
-  void Flush(nsTArray<ipc::Shmem>& aData);
+  void Flush(nsTArray<ipc::Shmem>& aSmallAllocs, nsTArray<ipc::Shmem>& aLargeAllocs);
 
   void Clear();
 
 protected:
   void AllocChunk();
+  layers::OffsetRange AllocLargeChunk(size_t aSize);
 
-  nsTArray<ipc::Shmem> mData;
+  nsTArray<ipc::Shmem> mSmallAllocs;
+  nsTArray<ipc::Shmem> mLargeAllocs;
   ipc::IShmemAllocator* mShmAllocator;
   size_t mCursor;
   size_t mChunkSize;
 };
 
 class ShmSegmentsReader {
 public:
-  explicit ShmSegmentsReader(const nsTArray<ipc::Shmem>& aShmems);
+  ShmSegmentsReader(const nsTArray<ipc::Shmem>& aSmallShmems,
+                    const nsTArray<ipc::Shmem>& aLargeShmems);
 
-  bool Read(layers::OffsetRange aRange, wr::Vec_u8& aInto);
+  bool Read(const layers::OffsetRange& aRange, wr::Vec_u8& aInto);
 
 protected:
-  void AllocChunk();
+  bool ReadLarge(const layers::OffsetRange& aRange, wr::Vec_u8& aInto);
 
-  const nsTArray<ipc::Shmem>& mData;
+  const nsTArray<ipc::Shmem>& mSmallAllocs;
+  const nsTArray<ipc::Shmem>& mLargeAllocs;
   size_t mChunkSize;
 };
 
 class IpcResourceUpdateQueue {
 public:
-  // TODO: 8192 is completely arbitrary, needs some adjustments.
+  // TODO: 32768 is completely arbitrary, needs some adjustments.
   // Because we are using shmems, the size should be a multiple of the page size, and keep in mind
   // that each shmem has guard pages, one of which contains meta-data so at least an extra page
   // is mapped under the hood (so having a lot of smaller shmems = overhead).
-  explicit IpcResourceUpdateQueue(ipc::IShmemAllocator* aAllocator, size_t aChunkSize = 8192);
+  explicit IpcResourceUpdateQueue(ipc::IShmemAllocator* aAllocator, size_t aChunkSize = 32768);
 
   void AddImage(wr::ImageKey aKey,
                 const ImageDescriptor& aDescriptor,
                 Range<uint8_t> aBytes);
 
   void AddBlobImage(wr::ImageKey aKey,
                     const ImageDescriptor& aDescriptor,
                     Range<uint8_t> aBytes);
 
+  void AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey);
+
   void UpdateImageBuffer(wr::ImageKey aKey,
                          const ImageDescriptor& aDescriptor,
                          Range<uint8_t> aBytes);
 
   void UpdateBlobImage(wr::ImageKey aKey,
                        const ImageDescriptor& aDescriptor,
                        Range<uint8_t> aBytes);
 
@@ -89,17 +97,18 @@ public:
                        const wr::FontInstanceOptions* aOptions,
                        const wr::FontInstancePlatformOptions* aPlatformOptions);
 
   void DeleteFontInstance(wr::FontInstanceKey aKey);
 
   void Clear();
 
   void Flush(nsTArray<layers::OpUpdateResource>& aUpdates,
-             nsTArray<ipc::Shmem>& aResources);
+             nsTArray<ipc::Shmem>& aSmallAllocs,
+             nsTArray<ipc::Shmem>& aLargeAllocs);
 
 protected:
   ShmSegmentsWriter mWriter;
   nsTArray<layers::OpUpdateResource> mUpdates;
 };
 
 } // namespace
 } // namespace
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ b/gfx/layers/wr/ScrollingLayersHelper.cpp
@@ -12,59 +12,60 @@
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "UnitTransforms.h"
 
 namespace mozilla {
 namespace layers {
 
 ScrollingLayersHelper::ScrollingLayersHelper(WebRenderLayer* aLayer,
                                              wr::DisplayListBuilder& aBuilder,
+                                             wr::IpcResourceUpdateQueue& aResources,
                                              const StackingContextHelper& aStackingContext)
   : mLayer(aLayer)
   , mBuilder(&aBuilder)
   , mPushedLayerLocalClip(false)
   , mPushedClipAndScroll(false)
 {
   if (!mLayer->WrManager()->AsyncPanZoomEnabled()) {
     // If APZ is disabled then we don't need to push the scrolling clips. We
     // still want to push the layer's local clip though.
-    PushLayerLocalClip(aStackingContext);
+    PushLayerLocalClip(aStackingContext, aResources);
     return;
   }
 
   Layer* layer = mLayer->GetLayer();
   for (uint32_t i = layer->GetScrollMetadataCount(); i > 0; i--) {
     const ScrollMetadata& metadata = layer->GetScrollMetadata(i - 1);
     // The scroll clip on a given metadata is affected by all async transforms
     // from metadatas "above" it, but not the async transform on the metadata
     // itself. Therefore we need to push this clip before we push the
     // corresponding scroll layer, so that when we set an async scroll position
     // on the scroll layer, the clip isn't affected by it.
     if (const Maybe<LayerClip>& clip = metadata.GetScrollClip()) {
-      PushLayerClip(clip.ref(), aStackingContext);
+      PushLayerClip(clip.ref(), aStackingContext, aResources);
     }
 
     const FrameMetrics& fm = layer->GetFrameMetrics(i - 1);
     if (layer->GetIsFixedPosition() &&
         layer->GetFixedPositionScrollContainerId() == fm.GetScrollId()) {
       // If the layer contents are fixed for this metadata onwards, we need
       // to insert the layer's local clip at this point in the clip tree,
       // as a child of whatever's on the stack.
-      PushLayerLocalClip(aStackingContext);
+      PushLayerLocalClip(aStackingContext, aResources);
     }
 
     DefineAndPushScrollLayer(fm, aStackingContext);
   }
 
   // The scrolled clip on the layer is "inside" all of the scrollable metadatas
   // on that layer. That is, the clip scrolls along with the content in
   // child layers. So we need to apply this after pushing all the scroll layers,
   // which we do above.
   if (const Maybe<LayerClip>& scrolledClip = layer->GetScrolledClip()) {
-    PushLayerClip(scrolledClip.ref(), aStackingContext);
+    PushLayerClip(scrolledClip.ref(), aStackingContext, aResources);
   }
 
   // If the layer is marked as fixed-position, it is fixed relative to something
   // (the scroll layer referred to by GetFixedPositionScrollContainerId, hereafter
   // referred to as the "scroll container"). What this really means is that we
   // don't want this content to scroll with any scroll layer on the stack up to
   // and including the scroll container, but we do want it to scroll with any
   // ancestor scroll layers.
@@ -77,17 +78,17 @@ ScrollingLayersHelper::ScrollingLayersHe
   // to the ancestor of the scroll container.
   if (layer->GetIsFixedPosition()) {
     FrameMetrics::ViewID fixedFor = layer->GetFixedPositionScrollContainerId();
     Maybe<FrameMetrics::ViewID> scrollsWith = mBuilder->ParentScrollIdFor(fixedFor);
     Maybe<wr::WrClipId> clipId = mBuilder->TopmostClipId();
     // Default to 0 if there is no ancestor, because 0 refers to the root scrollframe.
     mBuilder->PushClipAndScrollInfo(scrollsWith.valueOr(0), clipId.ptrOr(nullptr));
   } else {
-    PushLayerLocalClip(aStackingContext);
+    PushLayerLocalClip(aStackingContext, aResources);
   }
 }
 
 ScrollingLayersHelper::ScrollingLayersHelper(nsDisplayItem* aItem,
                                              wr::DisplayListBuilder& aBuilder,
                                              const StackingContextHelper& aStackingContext,
                                              WebRenderLayerManager::ClipIdMap& aCache,
                                              bool aApzEnabled)
@@ -268,49 +269,51 @@ ScrollingLayersHelper::DefineAndPushScro
   mBuilder->DefineScrollLayer(aMetrics.GetScrollId(),
       aStackingContext.ToRelativeLayoutRect(contentRect),
       aStackingContext.ToRelativeLayoutRect(clipBounds));
   mBuilder->PushScrollLayer(aMetrics.GetScrollId());
   return true;
 }
 
 void
-ScrollingLayersHelper::PushLayerLocalClip(const StackingContextHelper& aStackingContext)
+ScrollingLayersHelper::PushLayerLocalClip(const StackingContextHelper& aStackingContext,
+                                          wr::IpcResourceUpdateQueue& aResources)
 {
   Layer* layer = mLayer->GetLayer();
   Maybe<ParentLayerRect> clip;
   if (const Maybe<ParentLayerIntRect>& rect = layer->GetClipRect()) {
     clip = Some(IntRectToRect(rect.ref()));
   } else if (layer->GetMaskLayer()) {
     // this layer has a mask, but no clip rect. so let's use the transformed
     // visible bounds as the clip rect.
     clip = Some(layer->GetLocalTransformTyped().TransformBounds(mLayer->Bounds()));
   }
   if (clip) {
-    Maybe<wr::WrImageMask> mask = mLayer->BuildWrMaskLayer(aStackingContext);
+    Maybe<wr::WrImageMask> mask = mLayer->BuildWrMaskLayer(aStackingContext, aResources);
     LayerRect clipRect = ViewAs<LayerPixel>(clip.ref(),
         PixelCastJustification::MovingDownToChildren);
     mBuilder->PushClip(mBuilder->DefineClip(
         aStackingContext.ToRelativeLayoutRect(clipRect), nullptr, mask.ptrOr(nullptr)));
     mPushedLayerLocalClip = true;
   }
 }
 
 void
 ScrollingLayersHelper::PushLayerClip(const LayerClip& aClip,
-                                     const StackingContextHelper& aSc)
+                                     const StackingContextHelper& aSc,
+                                     wr::IpcResourceUpdateQueue& aResources)
 {
   LayerRect clipRect = IntRectToRect(ViewAs<LayerPixel>(aClip.GetClipRect(),
         PixelCastJustification::MovingDownToChildren));
   Maybe<wr::WrImageMask> mask;
   if (Maybe<size_t> maskLayerIndex = aClip.GetMaskLayerIndex()) {
     Layer* maskLayer = mLayer->GetLayer()->GetAncestorMaskLayerAt(maskLayerIndex.value());
     WebRenderLayer* maskWrLayer = WebRenderLayer::ToWebRenderLayer(maskLayer);
     // TODO: check this transform is correct in all cases
-    mask = maskWrLayer->RenderMaskLayer(aSc, maskLayer->GetTransform());
+    mask = maskWrLayer->RenderMaskLayer(aSc, maskLayer->GetTransform(), aResources);
   }
   mBuilder->PushClip(mBuilder->DefineClip(
       aSc.ToRelativeLayoutRect(clipRect), nullptr, mask.ptrOr(nullptr)));
 }
 
 ScrollingLayersHelper::~ScrollingLayersHelper()
 {
   if (!mLayer) {
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ b/gfx/layers/wr/ScrollingLayersHelper.h
@@ -24,16 +24,17 @@ struct LayerClip;
 class StackingContextHelper;
 class WebRenderLayer;
 
 class MOZ_RAII ScrollingLayersHelper
 {
 public:
   ScrollingLayersHelper(WebRenderLayer* aLayer,
                         wr::DisplayListBuilder& aBuilder,
+                        wr::IpcResourceUpdateQueue& aResources,
                         const StackingContextHelper& aSc);
   ScrollingLayersHelper(nsDisplayItem* aItem,
                         wr::DisplayListBuilder& aBuilder,
                         const StackingContextHelper& aStackingContext,
                         WebRenderLayerManager::ClipIdMap& aCache,
                         bool aApzEnabled);
   ~ScrollingLayersHelper();
 
@@ -47,19 +48,21 @@ private:
                                  WebRenderLayerManager::ClipIdMap& aCache);
   void DefineAndPushChain(const DisplayItemClipChain* aChain,
                           wr::DisplayListBuilder& aBuilder,
                           const StackingContextHelper& aStackingContext,
                           int32_t aAppUnitsPerDevPixel,
                           WebRenderLayerManager::ClipIdMap& aCache);
   bool DefineAndPushScrollLayer(const FrameMetrics& aMetrics,
                                 const StackingContextHelper& aStackingContext);
-  void PushLayerLocalClip(const StackingContextHelper& aStackingContext);
+  void PushLayerLocalClip(const StackingContextHelper& aStackingContext,
+                          wr::IpcResourceUpdateQueue& aResources);
   void PushLayerClip(const LayerClip& aClip,
-                     const StackingContextHelper& aSc);
+                     const StackingContextHelper& aSc,
+                     wr::IpcResourceUpdateQueue& aResources);
 
   WebRenderLayer* mLayer;
   wr::DisplayListBuilder* mBuilder;
   bool mPushedLayerLocalClip;
   bool mPushedClipAndScroll;
   std::vector<wr::ScrollOrClipId> mPushedClips;
 };
 
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -102,20 +102,21 @@ void
 WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources)
 {
   if (!IPCOpen()) {
     aResources.Clear();
     return;
   }
 
   nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<ipc::Shmem> resourceData;
-  aResources.Flush(resourceUpdates, resourceData);
+  nsTArray<ipc::Shmem> smallShmems;
+  nsTArray<ipc::Shmem> largeShmems;
+  aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
-  this->SendUpdateResources(resourceUpdates, resourceData);
+  this->SendUpdateResources(resourceUpdates, Move(smallShmems), Move(largeShmems));
 }
 
 void
 WebRenderBridgeChild::EndTransaction(wr::DisplayListBuilder &aBuilder,
                                      wr::IpcResourceUpdateQueue& aResources,
                                      const gfx::IntSize& aSize,
                                      bool aIsSync,
                                      uint64_t aTransactionId,
@@ -131,30 +132,31 @@ WebRenderBridgeChild::EndTransaction(wr:
   ByteBuffer dlData(Move(dl.dl));
 
   TimeStamp fwdTime;
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   fwdTime = TimeStamp::Now();
 #endif
 
   nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<ipc::Shmem> resourceData;
-  aResources.Flush(resourceUpdates, resourceData);
+  nsTArray<ipc::Shmem> smallShmems;
+  nsTArray<ipc::Shmem> largeShmems;
+  aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
   if (aIsSync) {
     this->SendSetDisplayListSync(aSize, mParentCommands, mDestroyedActors,
                                  GetFwdTransactionId(), aTransactionId,
                                  contentSize, dlData, dl.dl_desc, aScrollData,
-                                 Move(resourceUpdates), Move(resourceData),
+                                 Move(resourceUpdates), Move(smallShmems), Move(largeShmems),
                                  mIdNamespace, aTxnStartTime, fwdTime);
   } else {
     this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
                              GetFwdTransactionId(), aTransactionId,
                              contentSize, dlData, dl.dl_desc, aScrollData,
-                             Move(resourceUpdates), Move(resourceData),
+                             Move(resourceUpdates), Move(smallShmems), Move(largeShmems),
                              mIdNamespace, aTxnStartTime, fwdTime);
   }
 
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -230,20 +230,21 @@ WebRenderBridgeParent::DeallocShmems(nsT
       DeallocShmem(shm);
     }
   }
   aShmems.Clear();
 }
 
 bool
 WebRenderBridgeParent::UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
-                                       const nsTArray<ipc::Shmem>& aResourceData,
+                                       const nsTArray<ipc::Shmem>& aSmallShmems,
+                                       const nsTArray<ipc::Shmem>& aLargeShmems,
                                        wr::ResourceUpdateQueue& aUpdates)
 {
-  wr::ShmSegmentsReader reader(aResourceData);
+  wr::ShmSegmentsReader reader(aSmallShmems, aLargeShmems);
 
   for (const auto& cmd : aResourceUpdates) {
     switch (cmd.type()) {
       case OpUpdateResource::TOpAddImage: {
         const auto& op = cmd.get_OpAddImage();
         wr::Vec_u8 bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
@@ -273,16 +274,23 @@ WebRenderBridgeParent::UpdateResources(c
         const auto& op = cmd.get_OpUpdateBlobImage();
         wr::Vec_u8 bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.UpdateBlobImage(op.key(), op.descriptor(), bytes);
         break;
       }
+      case OpUpdateResource::TOpAddExternalImage: {
+        const auto& op = cmd.get_OpAddExternalImage();
+        if (!AddExternalImage(op.externalImageId(), op.key(), aUpdates)) {
+          return false;
+        }
+        break;
+      }
       case OpUpdateResource::TOpAddRawFont: {
         const auto& op = cmd.get_OpAddRawFont();
         wr::Vec_u8 bytes;
         if (!reader.Read(op.bytes(), bytes)) {
           return false;
         }
         aUpdates.AddRawFont(op.key(), bytes, op.fontIndex());
         break;
@@ -312,34 +320,88 @@ WebRenderBridgeParent::UpdateResources(c
       }
       case OpUpdateResource::T__None: break;
     }
   }
 
   return true;
 }
 
+bool
+WebRenderBridgeParent::AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
+                                        wr::ResourceUpdateQueue& aResources)
+{
+  Range<const wr::ImageKey> keys(&aKey, 1);
+  // Check if key is obsoleted.
+  if (keys[0].mNamespace != mIdNamespace) {
+    return true;
+  }
+  MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(aExtId)).get());
+
+  RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(aExtId));
+  if (!host) {
+    NS_ERROR("CompositableHost does not exist");
+    return false;
+  }
+  if (!gfxEnv::EnableWebRenderRecording()) {
+    TextureHost* texture = host->GetAsTextureHostForComposite();
+    if (!texture) {
+      NS_ERROR("TextureHost does not exist");
+      return false;
+    }
+    WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
+    if (wrTexture) {
+      wrTexture->AddWRImage(aResources, keys, wrTexture->GetExternalImageKey());
+      return true;
+    }
+  }
+  RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
+  if (!dSurf) {
+    NS_ERROR("TextureHost does not return DataSourceSurface");
+    return false;
+  }
+
+  DataSourceSurface::MappedSurface map;
+  if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
+    NS_ERROR("DataSourceSurface failed to map");
+    return false;
+  }
+
+  IntSize size = dSurf->GetSize();
+  wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
+  wr::Vec_u8 data;
+  data.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
+  aResources.AddImage(keys[0], descriptor, data);
+  dSurf->Unmap();
+
+  return true;
+}
+
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                           nsTArray<ipc::Shmem>&& aResourceData)
+                                           nsTArray<ipc::Shmem>&& aSmallShmems,
+                                           nsTArray<ipc::Shmem>&& aLargeShmems)
 {
   if (mDestroyed) {
-    DeallocShmems(aResourceData);
+    DeallocShmems(aSmallShmems);
+    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   wr::ResourceUpdateQueue updates;
 
-  if (!UpdateResources(aResourceUpdates, aResourceData, updates)) {
-    DeallocShmems(aResourceData);
+  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, updates)) {
+    DeallocShmems(aSmallShmems);
+    DeallocShmems(aLargeShmems);
     IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
   }
 
   mApi->UpdateResources(updates);
-  DeallocShmems(aResourceData);
+  DeallocShmems(aSmallShmems);
+  DeallocShmems(aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -435,79 +497,110 @@ WebRenderBridgeParent::RecvSetDisplayLis
                                           InfallibleTArray<OpDestroy>&& aToDestroy,
                                           const uint64_t& aFwdTransactionId,
                                           const uint64_t& aTransactionId,
                                           const wr::LayoutSize& aContentSize,
                                           const wr::ByteBuffer& dl,
                                           const wr::BuiltDisplayListDescriptor& dlDesc,
                                           const WebRenderScrollData& aScrollData,
                                           nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                          nsTArray<ipc::Shmem>&& aResourceData,
+                                          nsTArray<ipc::Shmem>&& aSmallShmems,
+                                          nsTArray<ipc::Shmem>&& aLargeShmems,
                                           const wr::IdNamespace& aIdNamespace,
                                           const TimeStamp& aTxnStartTime,
                                           const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
-    DeallocShmems(aResourceData);
+    DeallocShmems(aSmallShmems);
+    DeallocShmems(aLargeShmems);
     return IPC_OK();
   }
 
   AutoProfilerTracing tracing("Paint", "SetDisplayList");
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
+  uint32_t wrEpoch = GetNextWrEpoch();
+
+
   wr::ResourceUpdateQueue resources;
-  UpdateResources(aResourceUpdates, aResourceData, resources);
+
+  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+  ProcessWebRenderParentCommands(aCommands, resources);
+
+  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, resources)) {
+    return IPC_FAIL(this, "Failed to deserialize resource updates");
+  }
 
-  uint32_t wrEpoch = GetNextWrEpoch();
-  ProcessWebRenderCommands(aSize, aCommands, wr::NewEpoch(wrEpoch),
-                           aContentSize, dl, dlDesc, resources, aIdNamespace);
+  // If id namespaces do not match, it means the command is obsolete, probably
+  // because the tab just moved to a new window.
+  // In that case do not send the commands to webrender.
+  if (mIdNamespace == aIdNamespace) {
+    if (mWidget) {
+      LayoutDeviceIntSize size = mWidget->GetClientSize();
+      mApi->SetWindowParameters(size);
+    }
+    gfx::Color color = mWidget ? gfx::Color(0.3f, 0.f, 0.f, 1.f) : gfx::Color(0.f, 0.f, 0.f, 0.f);
+    mApi->SetDisplayList(color, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
+                        mPipelineId, aContentSize,
+                        dlDesc, dl.mData, dl.mLength,
+                        resources);
+
+    ScheduleComposition();
+
+    if (ShouldParentObserveEpoch()) {
+      mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
+    }
+  }
+
   HoldPendingTransactionId(wrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
 
   mScrollData = aScrollData;
   UpdateAPZ();
 
   if (mIdNamespace != aIdNamespace) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     TimeStamp now = TimeStamp::Now();
     mCompositorBridge->DidComposite(wr::AsUint64(mPipelineId), now, now);
   }
 
-  DeallocShmems(aResourceData);
+  DeallocShmems(aSmallShmems);
+  DeallocShmems(aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetDisplayListSync(const gfx::IntSize &aSize,
                                               InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                               InfallibleTArray<OpDestroy>&& aToDestroy,
                                               const uint64_t& aFwdTransactionId,
                                               const uint64_t& aTransactionId,
                                               const wr::LayoutSize& aContentSize,
                                               const wr::ByteBuffer& dl,
                                               const wr::BuiltDisplayListDescriptor& dlDesc,
                                               const WebRenderScrollData& aScrollData,
                                               nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                              nsTArray<ipc::Shmem>&& aResourceData,
+                                              nsTArray<ipc::Shmem>&& aSmallShmems,
+                                              nsTArray<ipc::Shmem>&& aLargeShmems,
                                               const wr::IdNamespace& aIdNamespace,
                                               const TimeStamp& aTxnStartTime,
                                               const TimeStamp& aFwdTime)
 {
   return RecvSetDisplayList(aSize, Move(aCommands), Move(aToDestroy),
                             aFwdTransactionId, aTransactionId,
                             aContentSize, dl, dlDesc, aScrollData,
-                            Move(aResourceUpdates), Move(aResourceData),
+                            Move(aResourceUpdates), Move(aSmallShmems), Move(aLargeShmems),
                             aIdNamespace, aTxnStartTime, aFwdTime);
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands)
 {
   if (mDestroyed) {
     return IPC_OK();
@@ -520,63 +613,16 @@ WebRenderBridgeParent::RecvParentCommand
 
 void
 WebRenderBridgeParent::ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
                                                       wr::ResourceUpdateQueue& aResources)
 {
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
-      case WebRenderParentCommand::TOpAddExternalImage: {
-        const OpAddExternalImage& op = cmd.get_OpAddExternalImage();
-        Range<const wr::ImageKey> keys(&op.key(), 1);
-        // Check if key is obsoleted.
-        if (keys[0].mNamespace != mIdNamespace) {
-          break;
-        }
-        MOZ_ASSERT(mExternalImageIds.Get(wr::AsUint64(op.externalImageId())).get());
-
-        RefPtr<WebRenderImageHost> host = mExternalImageIds.Get(wr::AsUint64(op.externalImageId()));
-        if (!host) {
-          NS_ERROR("CompositableHost does not exist");
-          break;
-        }
-        if (!gfxEnv::EnableWebRenderRecording()) {
-          TextureHost* texture = host->GetAsTextureHostForComposite();
-          if (!texture) {
-            NS_ERROR("TextureHost does not exist");
-            break;
-          }
-          WebRenderTextureHost* wrTexture = texture->AsWebRenderTextureHost();
-          if (wrTexture) {
-            wrTexture->AddWRImage(aResources, keys, wrTexture->GetExternalImageKey());
-            break;
-          }
-        }
-        RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
-        if (!dSurf) {
-          NS_ERROR("TextureHost does not return DataSourceSurface");
-          break;
-        }
-
-        DataSourceSurface::MappedSurface map;
-        if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
-          NS_ERROR("DataSourceSurface failed to map");
-          break;
-        }
-
-        IntSize size = dSurf->GetSize();
-        wr::ImageDescriptor descriptor(size, map.mStride, dSurf->GetFormat());
-        wr::Vec_u8 data;
-        data.PushBytes(Range<uint8_t>(map.mData, size.height * map.mStride));
-        aResources.AddImage(keys[0], descriptor, data);
-
-        dSurf->Unmap();
-        break;
-      }
       case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
         const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
         mAsyncImageManager->UpdateAsyncImagePipeline(op.pipelineId(),
                                                       op.scBounds(),
                                                       op.scTransform(),
                                                       op.scaleToSize(),
                                                       op.filter(),
                                                       op.mixBlendMode());
@@ -609,50 +655,16 @@ WebRenderBridgeParent::ProcessWebRenderP
       default: {
         // other commands are handle on the child
         break;
       }
     }
   }
 }
 
-void
-WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
-                                                InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
-                                                const wr::LayoutSize& aContentSize, const wr::ByteBuffer& dl,
-                                                const wr::BuiltDisplayListDescriptor& dlDesc,
-                                                wr::ResourceUpdateQueue& aResourceUpdates,
-                                                const wr::IdNamespace& aIdNamespace)
-{
-  mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-  ProcessWebRenderParentCommands(aCommands, aResourceUpdates);
-
-  // The command is obsoleted.
-  // Do not set the command to webrender since it causes crash in webrender.
-  if (mIdNamespace != aIdNamespace) {
-    return;
-  }
-
-  if (mWidget) {
-    LayoutDeviceIntSize size = mWidget->GetClientSize();
-    mApi->SetWindowParameters(size);
-  }
-  gfx::Color color = mWidget ? gfx::Color(0.3f, 0.f, 0.f, 1.f) : gfx::Color(0.f, 0.f, 0.f, 0.f);
-  mApi->SetDisplayList(color, aEpoch, LayerSize(aSize.width, aSize.height),
-                       mPipelineId, aContentSize,
-                       dlDesc, dl.mData, dl.mLength,
-                       aResourceUpdates);
-
-  ScheduleComposition();
-
-  if (ShouldParentObserveEpoch()) {
-    mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
-  }
-}
-
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvGetSnapshot(PTextureParent* aTexture)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(!mPaused);
 
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -69,42 +69,45 @@ public:
 
   mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
 
   mozilla::ipc::IPCResult RecvCreate(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvShutdownSync() override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvUpdateResources(nsTArray<OpUpdateResource>&& aUpdates,
-                                              nsTArray<ipc::Shmem>&& aResourceData) override;
+                                              nsTArray<ipc::Shmem>&& aSmallShmems,
+                                              nsTArray<ipc::Shmem>&& aLargeShmems) override;
   mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntSize& aSize,
                                              InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                              InfallibleTArray<OpDestroy>&& aToDestroy,
                                              const uint64_t& aFwdTransactionId,
                                              const uint64_t& aTransactionId,
                                              const wr::LayoutSize& aContentSize,
                                              const wr::ByteBuffer& dl,
                                              const wr::BuiltDisplayListDescriptor& dlDesc,
                                              const WebRenderScrollData& aScrollData,
                                              nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                             nsTArray<ipc::Shmem>&& aResourceData,
+                                             nsTArray<ipc::Shmem>&& aSmallShmems,
+                                             nsTArray<ipc::Shmem>&& aLargeShmems,
                                              const wr::IdNamespace& aIdNamespace,
                                              const TimeStamp& aTxnStartTime,
                                              const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvSetDisplayListSync(const gfx::IntSize& aSize,
                                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                                  const uint64_t& aFwdTransactionId,
                                                  const uint64_t& aTransactionId,
                                                  const wr::LayoutSize& aContentSize,
                                                  const wr::ByteBuffer& dl,
                                                  const wr::BuiltDisplayListDescriptor& dlDesc,
                                                  const WebRenderScrollData& aScrollData,
                                                  nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                                 nsTArray<ipc::Shmem>&& aResourceData,
+                                                 nsTArray<ipc::Shmem>&& aSmallShmems,
+                                                 nsTArray<ipc::Shmem>&& aLargeShmems,
                                                  const wr::IdNamespace& aIdNamespace,
                                                  const TimeStamp& aTxnStartTime,
                                                  const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands) override;
   mozilla::ipc::IPCResult RecvGetSnapshot(PTextureParent* aTexture) override;
 
   mozilla::ipc::IPCResult RecvAddPipelineIdForCompositable(const wr::PipelineId& aPipelineIds,
                                                            const CompositableHandle& aHandle,
@@ -185,30 +188,26 @@ public:
 
 private:
   void DeallocShmems(nsTArray<ipc::Shmem>& aShmems);
 
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
-                       const nsTArray<ipc::Shmem>& aResourceData,
+                       const nsTArray<ipc::Shmem>& aSmallShmems,
+                       const nsTArray<ipc::Shmem>& aLargeShmems,
                        wr::ResourceUpdateQueue& aUpdates);
+  bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
+                        wr::ResourceUpdateQueue& aResources);
 
   uint64_t GetLayersId() const;
   void ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
                                       wr::ResourceUpdateQueue& aResources);
-  void ProcessWebRenderCommands(const gfx::IntSize &aSize,
-                                InfallibleTArray<WebRenderParentCommand>& commands,
-                                const wr::Epoch& aEpoch,
-                                const wr::LayoutSize& aContentSize,
-                                const wr::ByteBuffer& dl,
-                                const wr::BuiltDisplayListDescriptor& dlDesc,
-                                wr::ResourceUpdateQueue& aResourceUpdates,
-                                const wr::IdNamespace& aIdNamespace);
+
   void ClearResources();
   uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
   bool ShouldParentObserveEpoch();
   mozilla::ipc::IPCResult HandleShutdown();
 
   void AdvanceAnimations();
   void SampleAnimations(nsTArray<wr::WrOpacityProperty>& aOpacityArray,
                         nsTArray<wr::WrTransformProperty>& aTransformArray);
--- a/gfx/layers/wr/WebRenderCanvasLayer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -11,16 +11,17 @@
 #include "GLContext.h"
 #include "GLScreenBuffer.h"
 #include "LayersLogging.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "PersistentBufferProvider.h"
 #include "SharedSurface.h"
 #include "SharedSurfaceGL.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "WebRenderCanvasRenderer.h"
 
 namespace mozilla {
 namespace layers {
@@ -45,32 +46,35 @@ WebRenderCanvasLayer::RenderLayer(wr::Di
   MOZ_ASSERT(canvasRenderer);
   canvasRenderer->UpdateCompositableClient();
 
   Maybe<gfx::Matrix4x4> transform;
   if (canvasRenderer->NeedsYFlip()) {
     transform = Some(GetTransform().PreTranslate(0, mBounds.Height(), 0).PreScale(1, -1, 1));
   }
 
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this, transform);
 
   LayerRect rect(0, 0, mBounds.Width(), mBounds.Height());
   DumpLayerInfo("CanvasLayer", rect);
 
   wr::ImageRendering filter = wr::ToImageRendering(mSamplingFilter);
 
   if (gfxPrefs::LayersDump()) {
     printf_stderr("CanvasLayer %p texture-filter=%s\n",
                   this->GetLayer(),
                   Stringify(filter).c_str());
   }
 
+  // Eww. Re-creating image keys every time is bad. Probably not worth fixing here
+  // since layers-full webrender is going away soon-ish. But don't reproduce what
+  // you see here.
   wr::WrImageKey key = GenerateImageKey();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(canvasRenderer->GetExternalImageId().value(), key));
+  aResources.AddExternalImage(canvasRenderer->GetExternalImageId().value(), key);
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushImage(r, r, filter, key);
 }
 
 void
 WebRenderCanvasLayer::ClearCachedResources()
--- a/gfx/layers/wr/WebRenderCanvasLayer.h
+++ b/gfx/layers/wr/WebRenderCanvasLayer.h
@@ -28,17 +28,17 @@ public:
   }
 
   CanvasRenderer* CreateCanvasRendererInternal() override;
 
   virtual void ClearCachedResources() override;
 
 protected:
   virtual ~WebRenderCanvasLayer();
-
+  Maybe<wr::ImageKey> mKey;
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder,
                    wr::IpcResourceUpdateQueue& aResources,
                    const StackingContextHelper& aSc) override;
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderColorLayer.cpp
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -18,17 +18,17 @@ namespace layers {
 
 using namespace mozilla::gfx;
 
 void
 WebRenderColorLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                  wr::IpcResourceUpdateQueue& aResources,
                                  const StackingContextHelper& aSc)
 {
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect = Bounds();
   DumpLayerInfo("ColorLayer", rect);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushRect(r, r, wr::ToColorF(mColor));
 }
--- a/gfx/layers/wr/WebRenderContainerLayer.cpp
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -102,17 +102,17 @@ WebRenderContainerLayer::RenderLayer(wr:
     transformForSC = nullptr;
   }
 
   nsTArray<wr::WrFilterOp> filters;
   for (const CSSFilter& filter : this->GetFilterChain()) {
     filters.AppendElement(wr::ToWrFilterOp(filter));
   }
 
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this, animationsId, opacityForSC, transformForSC, filters);
 
   LayerRect rect = Bounds();
   DumpLayerInfo("ContainerLayer", rect);
 
   for (LayerPolygon& child : children) {
     if (child.layer->IsBackfaceHidden()) {
       continue;
@@ -121,17 +121,17 @@ WebRenderContainerLayer::RenderLayer(wr:
   }
 }
 
 void
 WebRenderRefLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                wr::IpcResourceUpdateQueue& aResources,
                                const StackingContextHelper& aSc)
 {
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
 
   ParentLayerRect bounds = GetLocalTransformTyped().TransformBounds(Bounds());
   // As with WebRenderTextLayer, because we don't push a stacking context for
   // this layer, WR doesn't know about the transform on this layer. Therefore
   // we need to apply that transform to the bounds before we pass it on to WR.
   // The conversion from ParentLayerPixel to LayerPixel below is a result of
   // changing the reference layer from "this layer" to the "the layer that
   // created aSc".
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
@@ -27,25 +27,24 @@ void
 WebRenderDisplayItemLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                        wr::IpcResourceUpdateQueue& aResources,
                                        const StackingContextHelper& aSc)
 {
   if (mVisibleRegion.IsEmpty()) {
     return;
   }
 
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
 
   if (mItem) {
     wr::LayoutSize contentSize; // this won't actually be used by anything
     wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize);
     // We might have recycled this layer. Throw away the old commands.
-    mParentCommands.Clear();
 
-    mItem->CreateWebRenderCommands(builder, aResources, aSc, mParentCommands, WrManager(),
+    mItem->CreateWebRenderCommands(builder, aResources, aSc, WrManager(),
                                    GetDisplayListBuilder());
     builder.Finalize(contentSize, mBuiltDisplayList);
   } else {
     // else we have an empty transaction and just use the
     // old commands.
     WebRenderLayerManager* manager = WrManager();
     MOZ_ASSERT(manager);
 
@@ -53,13 +52,12 @@ WebRenderDisplayItemLayer::RenderLayer(w
     // If this layer or our parent changed, this empty transaction won't work.
     if (manager->IsMutatedLayer(this) || manager->IsMutatedLayer(GetParent())) {
       manager->SetTransactionIncomplete();
       return;
     }
   }
 
   aBuilder.PushBuiltDisplayList(mBuiltDisplayList);
-  WrBridge()->AddWebRenderParentCommands(mParentCommands);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.h
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.h
@@ -32,15 +32,14 @@ protected:
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder,
                    wr::IpcResourceUpdateQueue& aResources,
                    const StackingContextHelper& aHelper) override;
 
 private:
   wr::BuiltDisplayList mBuiltDisplayList;
-  nsTArray<WebRenderParentCommand> mParentCommands;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERDisplayItemLayer_H
--- a/gfx/layers/wr/WebRenderImageLayer.cpp
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -5,30 +5,32 @@
 
 #include "WebRenderImageLayer.h"
 
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/TextureClientRecycleAllocator.h"
 #include "mozilla/layers/TextureWrapperImage.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace gfx;
 
 WebRenderImageLayer::WebRenderImageLayer(WebRenderLayerManager* aLayerManager)
   : ImageLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+  , mKey(Nothing())
   , mImageClientContainerType(CompositableType::UNKNOWN)
 {
   MOZ_COUNT_CTOR(WebRenderImageLayer);
 }
 
 WebRenderImageLayer::~WebRenderImageLayer()
 {
   MOZ_COUNT_DTOR(WebRenderImageLayer);
@@ -103,16 +105,53 @@ WebRenderImageLayer::SupportsAsyncUpdate
 {
   if (GetImageClientType() == CompositableType::IMAGE_BRIDGE &&
       mPipelineId.isSome()) {
     return true;
   }
   return false;
 }
 
+Maybe<wr::ImageKey>
+WebRenderImageLayer::UpdateImageKey(ImageClientSingle* aImageClient,
+                                    ImageContainer* aContainer,
+                                    Maybe<wr::ImageKey>& aOldKey,
+                                    wr::ExternalImageId& aExternalImageId,
+                                    wr::IpcResourceUpdateQueue& aResources)
+{
+  MOZ_ASSERT(aImageClient);
+  MOZ_ASSERT(aContainer);
+
+  uint32_t oldCounter = aImageClient->GetLastUpdateGenerationCounter();
+
+  bool ret = aImageClient->UpdateImage(aContainer, /* unused */0);
+  if (!ret || aImageClient->IsEmpty()) {
+    // Delete old key
+    if (aOldKey.isSome()) {
+      WrManager()->AddImageKeyForDiscard(aOldKey.value());
+    }
+    return Nothing();
+  }
+
+  // Reuse old key if generation is not updated.
+  if (oldCounter == aImageClient->GetLastUpdateGenerationCounter() && aOldKey.isSome()) {
+    return aOldKey;
+  }
+
+  // Delete old key, we are generating a new key.
+  // TODO: stop doing this!
+  if (aOldKey.isSome()) {
+    WrManager()->AddImageKeyForDiscard(aOldKey.value());
+  }
+
+  wr::WrImageKey key = GenerateImageKey();
+  aResources.AddExternalImage(aExternalImageId, key);
+  return Some(key);
+}
+
 void
 WebRenderImageLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                  wr::IpcResourceUpdateQueue& aResources,
                                  const StackingContextHelper& aSc)
 {
   if (!mContainer) {
      return;
   }
@@ -148,17 +187,17 @@ WebRenderImageLayer::RenderLayer(wr::Dis
 
   if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
     MOZ_ASSERT(!mImageClient);
     MOZ_ASSERT(mExternalImageId.isNothing());
 
     // Push IFrame for async image pipeline.
     // XXX Remove this once partial display list update is supported.
 
-    ScrollingLayersHelper scroller(this, aBuilder, aSc);
+    ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
 
     ParentLayerRect bounds = GetLocalTransformTyped().TransformBounds(Bounds());
 
     // We don't push a stacking context for this async image pipeline here.
     // Instead, we do it inside the iframe that hosts the image. As a result,
     // a bunch of the calculations normally done as part of that stacking
     // context need to be done manually and pushed over to the parent side,
     // where it will be done when we build the display list for the iframe.
@@ -204,22 +243,23 @@ WebRenderImageLayer::RenderLayer(wr::Dis
   Image* image = autoLock.GetImage();
   if (!image) {
     return;
   }
   gfx::IntSize size = image->GetSize();
   mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
                         mContainer,
                         mKey,
-                        mExternalImageId.ref());
+                        mExternalImageId.ref(),
+                        aResources);
   if (mKey.isNothing()) {
     return;
   }
 
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect(0, 0, size.width, size.height);
   if (mScaleMode != ScaleMode::SCALE_NONE) {
     NS_ASSERTION(mScaleMode == ScaleMode::STRETCH,
                  "No other scalemodes than stretch and none supported yet.");
     rect = LayerRect(0, 0, mScaleToSize.width, mScaleToSize.height);
   }
@@ -233,17 +273,18 @@ WebRenderImageLayer::RenderLayer(wr::Dis
                   Stringify(filter).c_str());
   }
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushImage(r, r, filter, mKey.value());
 }
 
 Maybe<wr::WrImageMask>
 WebRenderImageLayer::RenderMaskLayer(const StackingContextHelper& aSc,
-                                     const gfx::Matrix4x4& aTransform)
+                                     const gfx::Matrix4x4& aTransform,
+                                     wr::IpcResourceUpdateQueue& aResources)
 {
   if (!mContainer) {
      return Nothing();
   }
 
   CompositableType type = GetImageClientType();
   if (type == CompositableType::UNKNOWN) {
     return Nothing();
@@ -273,17 +314,18 @@ WebRenderImageLayer::RenderMaskLayer(con
   if (!image) {
     return Nothing();
   }
 
   MOZ_ASSERT(mImageClient->AsImageClientSingle());
   mKey = UpdateImageKey(mImageClient->AsImageClientSingle(),
                         mContainer,
                         mKey,
-                        mExternalImageId.ref());
+                        mExternalImageId.ref(),
+                        aResources);
   if (mKey.isNothing()) {
     return Nothing();
   }
 
   gfx::IntSize size = image->GetSize();
   wr::WrImageMask imageMask;
   imageMask.image = mKey.value();
   Rect maskRect = aTransform.TransformBounds(Rect(0, 0, size.width, size.height));
--- a/gfx/layers/wr/WebRenderImageLayer.h
+++ b/gfx/layers/wr/WebRenderImageLayer.h
@@ -30,24 +30,32 @@ protected:
   virtual ~WebRenderImageLayer();
 
 public:
   Layer* GetLayer() override { return this; }
   void RenderLayer(wr::DisplayListBuilder& aBuilder,
                    wr::IpcResourceUpdateQueue& aResources,
                    const StackingContextHelper& aSc) override;
   Maybe<wr::WrImageMask> RenderMaskLayer(const StackingContextHelper& aSc,
-                                         const gfx::Matrix4x4& aTransform) override;
+                                         const gfx::Matrix4x4& aTransform,
+                                         wr::IpcResourceUpdateQueue& aResources) override;
 
 protected:
   CompositableType GetImageClientType();
   void ClearWrResources();
 
   void AddWRVideoImage(size_t aChannelNumber);
 
+  Maybe<wr::ImageKey>
+  UpdateImageKey(ImageClientSingle* aImageClient,
+                 ImageContainer* aContainer,
+                 Maybe<wr::ImageKey>& aOldKey,
+                 wr::ExternalImageId& aExternalImageId,
+                 wr::IpcResourceUpdateQueue& aResources);
+
   wr::MaybeExternalImageId mExternalImageId;
   Maybe<wr::ImageKey> mKey;
   RefPtr<ImageClient> mImageClient;
   CompositableType mImageClientContainerType;
   Maybe<wr::PipelineId> mPipelineId;
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderLayer.cpp
+++ b/gfx/layers/wr/WebRenderLayer.cpp
@@ -36,22 +36,23 @@ WebRenderLayer::GenerateImageKey()
 {
   wr::WrImageKey key;
   key.mNamespace = WrBridge()->GetNamespace();
   key.mHandle = WrBridge()->GetNextResourceId();
   return key;
 }
 
 Maybe<wr::WrImageMask>
-WebRenderLayer::BuildWrMaskLayer(const StackingContextHelper& aRelativeTo)
+WebRenderLayer::BuildWrMaskLayer(const StackingContextHelper& aRelativeTo,
+                                 wr::IpcResourceUpdateQueue& aResources)
 {
   if (GetLayer()->GetMaskLayer()) {
     WebRenderLayer* maskLayer = ToWebRenderLayer(GetLayer()->GetMaskLayer());
     gfx::Matrix4x4 transform = maskLayer->GetLayer()->GetTransform();
-    return maskLayer->RenderMaskLayer(aRelativeTo, transform);
+    return maskLayer->RenderMaskLayer(aRelativeTo, transform, aResources);
   }
 
   return Nothing();
 }
 
 LayerRect
 WebRenderLayer::Bounds()
 {
@@ -76,51 +77,16 @@ WebRenderLayer::BoundsForStackingContext
   if (!transform.IsIdentity()) {
     // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
     bounds.MoveTo(transform.TransformPoint(bounds.TopLeft()));
   }
 
   return bounds;
 }
 
-Maybe<wr::ImageKey>
-WebRenderLayer::UpdateImageKey(ImageClientSingle* aImageClient,
-                               ImageContainer* aContainer,
-                               Maybe<wr::ImageKey>& aOldKey,
-                               wr::ExternalImageId& aExternalImageId)
-{
-  MOZ_ASSERT(aImageClient);
-  MOZ_ASSERT(aContainer);
-
-  uint32_t oldCounter = aImageClient->GetLastUpdateGenerationCounter();
-
-  bool ret = aImageClient->UpdateImage(aContainer, /* unused */0);
-  if (!ret || aImageClient->IsEmpty()) {
-    // Delete old key
-    if (aOldKey.isSome()) {
-      WrManager()->AddImageKeyForDiscard(aOldKey.value());
-    }
-    return Nothing();
-  }
-
-  // Reuse old key if generation is not updated.
-  if (oldCounter == aImageClient->GetLastUpdateGenerationCounter() && aOldKey.isSome()) {
-    return aOldKey;
-  }
-
-  // Delete old key, we are generating a new key.
-  if (aOldKey.isSome()) {
-    WrManager()->AddImageKeyForDiscard(aOldKey.value());
-  }
-
-  wr::WrImageKey key = GenerateImageKey();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(aExternalImageId, key));
-  return Some(key);
-}
-
 void
 WebRenderLayer::DumpLayerInfo(const char* aLayerType, const LayerRect& aRect)
 {
   if (!gfxPrefs::LayersDump()) {
     return;
   }
 
   Layer* layer = GetLayer();
--- a/gfx/layers/wr/WebRenderLayer.h
+++ b/gfx/layers/wr/WebRenderLayer.h
@@ -6,16 +6,19 @@
 #ifndef GFX_WEBRENDERLAYER_H
 #define GFX_WEBRENDERLAYER_H
 
 #include "Layers.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
+namespace wr {
+class IpcResourceUpdateQueue;
+}
 namespace layers {
 
 class ImageClientSingle;
 class StackingContextHelper;
 class WebRenderBridgeChild;
 class WebRenderLayerManager;
 
 typedef gfx::Matrix4x4Typed<LayerPixel, LayerPixel> BoundsTransformMatrix;
@@ -23,47 +26,44 @@ typedef gfx::Matrix4x4Typed<LayerPixel, 
 class WebRenderLayer
 {
 public:
   virtual Layer* GetLayer() = 0;
   virtual void RenderLayer(wr::DisplayListBuilder& aBuilder,
                            wr::IpcResourceUpdateQueue& aResources,
                            const StackingContextHelper& aSc) = 0;
   virtual Maybe<wr::WrImageMask> RenderMaskLayer(const StackingContextHelper& aSc,
-                                             const gfx::Matrix4x4& aTransform)
+                                                 const gfx::Matrix4x4& aTransform,
+                                                 wr::IpcResourceUpdateQueue& aResources)
   {
     MOZ_ASSERT(false);
     return Nothing();
   }
 
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
   static inline WebRenderLayer*
   ToWebRenderLayer(Layer* aLayer)
   {
     return static_cast<WebRenderLayer*>(aLayer->ImplData());
   }
 
-  Maybe<wr::ImageKey> UpdateImageKey(ImageClientSingle* aImageClient,
-                                     ImageContainer* aContainer,
-                                     Maybe<wr::ImageKey>& aOldKey,
-                                     wr::ExternalImageId& aExternalImageId);
-
   WebRenderLayerManager* WrManager();
   WebRenderBridgeChild* WrBridge();
   wr::WrImageKey GenerateImageKey();
 
   LayerRect Bounds();
   LayerRect BoundsForStackingContext();
 
   // Builds a WrImageMask from the mask layer on this layer, if there is one.
   // The |aRelativeTo| parameter should be a reference to the stacking context
   // that we want this mask to be relative to. This is usually the stacking
   // context of the *parent* layer of |this|, because that is what the mask
   // is relative to in the layer tree.
-  Maybe<wr::WrImageMask> BuildWrMaskLayer(const StackingContextHelper& aRelativeTo);
+  Maybe<wr::WrImageMask> BuildWrMaskLayer(const StackingContextHelper& aRelativeTo,
+                                          wr::IpcResourceUpdateQueue& aResources);
 
 protected:
   BoundsTransformMatrix BoundsTransform();
 
   void DumpLayerInfo(const char* aLayerType, const LayerRect& aRect);
 };
 
 } // namespace layers
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -336,17 +336,17 @@ WebRenderLayerManager::CreateWebRenderCo
       }
     }
 
     { // scope the ScrollingLayersHelper
       ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, AsyncPanZoomEnabled());
 
       // Note: this call to CreateWebRenderCommands can recurse back into
       // this function if the |item| is a wrapper for a sublist.
-      if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mParentCommands, this,
+      if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, this,
                                          aDisplayListBuilder)) {
         PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
       }
     }
 
     if (apzEnabled && forceNewLayerData) {
       // Pop the thing we pushed before the recursion, so the topmost item on
       // the stack is enclosing display item's ASR (or the stack is empty)
@@ -386,16 +386,17 @@ WebRenderLayerManager::EndTransactionWit
                          aDisplayList,
                          aDisplayListBuilder);
 }
 
 Maybe<wr::ImageKey>
 WebRenderLayerManager::CreateImageKey(nsDisplayItem* aItem,
                                       ImageContainer* aContainer,
                                       mozilla::wr::DisplayListBuilder& aBuilder,
+                                      mozilla::wr::IpcResourceUpdateQueue& aResources,
                                       const StackingContextHelper& aSc,
                                       gfx::IntSize& aSize)
 {
   RefPtr<WebRenderImageData> imageData = CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem);
   MOZ_ASSERT(imageData);
 
   if (aContainer->IsAsync()) {
     bool snap;
@@ -404,16 +405,19 @@ WebRenderLayerManager::CreateImageKey(ns
     LayerRect rect = ViewAs<LayerPixel>(
       LayoutDeviceRect::FromAppUnits(bounds, appUnitsPerDevPixel),
       PixelCastJustification::WebRenderHasUnitResolution);
     LayerRect scBounds(0, 0, rect.width, rect.Height());
     MaybeIntSize scaleToSize;
     if (!aContainer->GetScaleHint().IsEmpty()) {
       scaleToSize = Some(aContainer->GetScaleHint());
     }
+    // TODO!
+    // We appear to be using the image bridge for a lot (most/all?) of
+    // layers-free image handling and that breaks frame consistency.
     imageData->CreateAsyncImageWebRenderCommands(aBuilder,
                                                  aContainer,
                                                  aSc,
                                                  rect,
                                                  scBounds,
                                                  gfx::Matrix4x4(),
                                                  scaleToSize,
                                                  wr::ImageRendering::Auto,
@@ -423,28 +427,31 @@ WebRenderLayerManager::CreateImageKey(ns
 
   AutoLockImage autoLock(aContainer);
   if (!autoLock.HasImage()) {
     return Nothing();
   }
   mozilla::layers::Image* image = autoLock.GetImage();
   aSize = image->GetSize();
 
-  return imageData->UpdateImageKey(aContainer);
+  return imageData->UpdateImageKey(aContainer, aResources);
 }
 
 bool
 WebRenderLayerManager::PushImage(nsDisplayItem* aItem,
                                  ImageContainer* aContainer,
                                  mozilla::wr::DisplayListBuilder& aBuilder,
+                                 mozilla::wr::IpcResourceUpdateQueue& aResources,
                                  const StackingContextHelper& aSc,
                                  const LayerRect& aRect)
 {
   gfx::IntSize size;
-  Maybe<wr::ImageKey> key = CreateImageKey(aItem, aContainer, aBuilder, aSc, size);
+  Maybe<wr::ImageKey> key = CreateImageKey(aItem, aContainer,
+                                          aBuilder, aResources,
+                                          aSc, size);
   if (aContainer->IsAsync()) {
     // Async ImageContainer does not create ImageKey, instead it uses Pipeline.
     MOZ_ASSERT(key.isNothing());
     return true;
   }
   if (!key) {
     return false;
   }
@@ -610,17 +617,17 @@ WebRenderLayerManager::GenerateFallbackD
         if (!helper.UpdateImage()) {
           return nullptr;
         }
       }
 
       // Force update the key in fallback data since we repaint the image in this path.
       // If not force update, fallbackData may reuse the original key because it
       // doesn't know UpdateImageHelper already updated the image container.
-      if (!fallbackData->UpdateImageKey(imageContainer, true)) {
+      if (!fallbackData->UpdateImageKey(imageContainer, aResources, true)) {
         return nullptr;
       }
     }
 
     geometry = aItem->AllocateGeometry(aDisplayListBuilder);
     fallbackData->SetInvalid(false);
   }
 
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -60,21 +60,23 @@ public:
   virtual int32_t GetMaxTextureSize() const override;
 
   virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
   virtual bool BeginTransaction() override;
   virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
   Maybe<wr::ImageKey> CreateImageKey(nsDisplayItem* aItem,
                                      ImageContainer* aContainer,
                                      mozilla::wr::DisplayListBuilder& aBuilder,
+                                     mozilla::wr::IpcResourceUpdateQueue& aResources,
                                      const StackingContextHelper& aSc,
                                      gfx::IntSize& aSize);
   bool PushImage(nsDisplayItem* aItem,
                  ImageContainer* aContainer,
                  mozilla::wr::DisplayListBuilder& aBuilder,
+                 mozilla::wr::IpcResourceUpdateQueue& aResources,
                  const StackingContextHelper& aSc,
                  const LayerRect& aRect);
 
   already_AddRefed<WebRenderFallbackData>
   GenerateFallbackData(nsDisplayItem* aItem,
                        wr::DisplayListBuilder& aBuilder,
                        wr::IpcResourceUpdateQueue& aResourceUpdates,
                        nsDisplayListBuilder* aDisplayListBuilder,
--- a/gfx/layers/wr/WebRenderPaintedLayer.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayer.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/. */
 
 #include "WebRenderPaintedLayer.h"
 
 #include "LayersLogging.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/UpdateImageHelper.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 
@@ -86,26 +87,28 @@ WebRenderPaintedLayer::UpdateImageClient
     return false;
   }
 
   return true;
 }
 
 void
 WebRenderPaintedLayer::CreateWebRenderDisplayList(wr::DisplayListBuilder& aBuilder,
+                                                  wr::IpcResourceUpdateQueue& aResources,
                                                   const StackingContextHelper& aSc)
 {
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
 
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
 
   wr::WrImageKey key = GenerateImageKey();
-  WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
+  aResources.AddExternalImage(mExternalImageId.value(), key);
+  // TODO: reuse image keys!
   WrManager()->AddImageKeyForDiscard(key);
 
   wr::LayoutRect r = sc.ToRelativeLayoutRect(rect);
   aBuilder.PushImage(r, r, wr::ImageRendering::Auto, key);
 }
 
 void
 WebRenderPaintedLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
@@ -156,17 +159,17 @@ WebRenderPaintedLayer::RenderLayer(wr::D
     mPaintedRect = visibleBounds;
     SetValidRegion(visibleRegion);
   } else {
     // We have an empty transaction, just reuse the old image we had before.
     MOZ_ASSERT(mExternalImageId);
     MOZ_ASSERT(mImageContainer->HasCurrentImage());
   }
 
-  CreateWebRenderDisplayList(aBuilder, aSc);
+  CreateWebRenderDisplayList(aBuilder, aResources, aSc);
 }
 
 void
 WebRenderPaintedLayer::ClearCachedResources()
 {
   ClearWrResources();
   if (mImageClient) {
     mImageClient->FlushAllImages();
--- a/gfx/layers/wr/WebRenderPaintedLayer.h
+++ b/gfx/layers/wr/WebRenderPaintedLayer.h
@@ -52,16 +52,17 @@ public:
 
   RefPtr<ImageContainer> mImageContainer;
   RefPtr<ImageClient> mImageClient;
 
 private:
   bool SetupExternalImages();
   bool UpdateImageClient();
   void CreateWebRenderDisplayList(wr::DisplayListBuilder& aBuilder,
+                                  wr::IpcResourceUpdateQueue& aResources,
                                   const StackingContextHelper& aSc);
   void ClearWrResources();
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // GFX_WEBRENDERPAINTEDLAYER_H
--- a/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
+++ b/gfx/layers/wr/WebRenderPaintedLayerBlob.cpp
@@ -81,17 +81,17 @@ WebRenderPaintedLayerBlob::RenderLayer(w
     }
     mImageKey = Some(GenerateImageKey());
 
     wr::ImageDescriptor descriptor(imageSize, 0, dt->GetFormat());
     aResources.AddBlobImage(mImageKey.value(), descriptor, bytes.AsSlice());
     mImageBounds = visibleRegion.GetBounds();
   }
 
-  ScrollingLayersHelper scroller(this, aBuilder, aSc);
+  ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
   StackingContextHelper sc(aSc, aBuilder, this);
   LayerRect rect = Bounds();
   DumpLayerInfo("PaintedLayer", rect);
 
   aBuilder.PushImage(sc.ToRelativeLayoutRect(LayerRect(mImageBounds)),
                      sc.ToRelativeLayoutRect(rect),
                      wr::ImageRendering::Auto, mImageKey.value());
 }
--- a/gfx/layers/wr/WebRenderTextLayer.cpp
+++ b/gfx/layers/wr/WebRenderTextLayer.cpp
@@ -22,17 +22,17 @@ void
 WebRenderTextLayer::RenderLayer(wr::DisplayListBuilder& aBuilder,
                                 wr::IpcResourceUpdateQueue& aResources,
                                 const StackingContextHelper& aSc)
 {
     if (mBounds.IsEmpty()) {
         return;
     }
 
-    ScrollingLayersHelper scroller(this, aBuilder, aSc);
+    ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc);
 
     LayerRect rect = LayerRect::FromUnknownRect(
         // I am not 100% sure this is correct, but it probably is. Because:
         // the bounds are in layer space, and when gecko composites layers it
         // applies the transform to the layer before compositing. However with
         // WebRender compositing, we don't pass the transform on this layer to
         // WR, so WR has no way of knowing about the transformed bounds unless
         // we apply it here. The glyphs that we push to WR should already be
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderUserData.h"
 
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/WebRenderMessages.h"
+#include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "nsDisplayListInvalidation.h"
 #include "WebRenderCanvasRenderer.h"
 
 namespace mozilla {
 namespace layers {
 
 WebRenderUserData::WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
                                      WebRenderUserDataRefTable* aTable)
@@ -64,17 +65,19 @@ WebRenderImageData::~WebRenderImageData(
   }
 
   if (mPipelineId) {
     WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
   }
 }
 
 Maybe<wr::ImageKey>
-WebRenderImageData::UpdateImageKey(ImageContainer* aContainer, bool aForceUpdate)
+WebRenderImageData::UpdateImageKey(ImageContainer* aContainer,
+                                   wr::IpcResourceUpdateQueue& aResources,
+                                   bool aForceUpdate)
 {
   CreateImageClientIfNeeded();
   CreateExternalImageIfNeeded();
 
   if (mContainer != aContainer) {
     mContainer = aContainer;
   }
 
@@ -99,22 +102,23 @@ WebRenderImageData::UpdateImageKey(Image
   }
 
   // Reuse old key if generation is not updated.
   if (!aForceUpdate && oldCounter == imageClient->GetLastUpdateGenerationCounter() && mKey) {
     return mKey;
   }
 
   // Delete old key, we are generating a new key.
+  // TODO(nical): noooo... we need to reuse image keys.
   if (mKey) {
     mWRManager->AddImageKeyForDiscard(mKey.value());
   }
 
   wr::WrImageKey key = WrBridge()->GetNextImageKey();
-  mWRManager->WrBridge()->AddWebRenderParentCommand(OpAddExternalImage(mExternalImageId.value(), key));
+  aResources.AddExternalImage(mExternalImageId.value(), key);
   mKey = Some(key);
 
   return mKey;
 }
 
 already_AddRefed<ImageClient>
 WebRenderImageData::GetImageClient()
 {
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -8,16 +8,20 @@
 
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/layers/AnimationInfo.h"
 
 class nsDisplayItemGeometry;
 
 namespace mozilla {
+namespace wr {
+class IpcResourceUpdateQueue;
+}
+
 namespace layers {
 class CanvasLayer;
 class ImageClient;
 class ImageContainer;
 class WebRenderBridgeChild;
 class WebRenderCanvasData;
 class WebRenderCanvasRendererAsync;
 class WebRenderImageData;
@@ -74,17 +78,19 @@ public:
 
   virtual WebRenderImageData* AsImageData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eImage; }
   static UserDataType Type() { return UserDataType::eImage; }
   Maybe<wr::ImageKey> GetKey() { return mKey; }
   void SetKey(const wr::ImageKey& aKey) { mKey = Some(aKey); }
   already_AddRefed<ImageClient> GetImageClient();
 
-  Maybe<wr::ImageKey> UpdateImageKey(ImageContainer* aContainer, bool aForceUpdate = false);
+  Maybe<wr::ImageKey> UpdateImageKey(ImageContainer* aContainer,
+                                     wr::IpcResourceUpdateQueue& aResources,
+                                     bool aForceUpdate = false);
 
   void CreateAsyncImageWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                          ImageContainer* aContainer,
                                          const StackingContextHelper& aSc,
                                          const LayerRect& aBounds,
                                          const LayerRect& aSCBounds,
                                          const gfx::Matrix4x4& aSCTransform,
                                          const gfx::MaybeIntSize& aScaleToSize,
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -694,37 +694,41 @@ gfxDWriteFontEntry::IsCJKFont()
 {
     if (mIsCJK != UNINITIALIZED_VALUE) {
         return mIsCJK;
     }
 
     mIsCJK = false;
 
     const uint32_t kOS2Tag = TRUETYPE_TAG('O','S','/','2');
-    AutoTArray<uint8_t, 128> buffer;
-    if (CopyFontTable(kOS2Tag, buffer) != NS_OK) {
+    hb_blob_t* blob = GetFontTable(kOS2Tag);
+    if (!blob) {
         return mIsCJK;
     }
+    // |blob| is an owning reference, but is not RAII-managed, so it must be
+    // explicitly freed using |hb_blob_destroy| before we return. (Beware of
+    // adding any early-return codepaths!)
 
+    uint32_t len;
+    const OS2Table* os2 =
+        reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len));
     // ulCodePageRange bit definitions for the CJK codepages,
     // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
     const uint32_t CJK_CODEPAGE_BITS =
         (1 << 17) | // codepage 932 - JIS/Japan
         (1 << 18) | // codepage 936 - Chinese (simplified)
         (1 << 19) | // codepage 949 - Korean Wansung
         (1 << 20) | // codepage 950 - Chinese (traditional)
         (1 << 21);  // codepage 1361 - Korean Johab
-
-    if (buffer.Length() >= offsetof(OS2Table, sxHeight)) {
-        const OS2Table* os2 =
-            reinterpret_cast<const OS2Table*>(buffer.Elements());
+    if (len >= offsetof(OS2Table, sxHeight)) {
         if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
             mIsCJK = true;
         }
     }
+    hb_blob_destroy(blob);
 
     return mIsCJK;
 }
 
 void
 gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                            FontListSizes* aSizes) const
 {
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -20,16 +20,39 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
+#if defined(MOZ_CRASHREPORTER)
+
+#include "nsExceptionHandler.h"
+#include "nsPrintfCString.h"
+
+#define ENSURE_HR_SUCCEEDED(hr) \
+  if (FAILED(hr)) { \
+    nsPrintfCString location("ENSURE_HR_SUCCEEDED \"%s\": %u", __FILE__, __LINE__); \
+    nsPrintfCString hrAsStr("0x%08X", hr); \
+    CrashReporter::AnnotateCrashReport(location, hrAsStr); \
+    MOZ_DIAGNOSTIC_ASSERT(SUCCEEDED(hr)); \
+    return hr; \
+  }
+
+#else
+
+#define ENSURE_HR_SUCCEEDED(hr) \
+  if (FAILED(hr)) { \
+    return hr; \
+  }
+
+#endif // defined(MOZ_CRASHREPORTER)
+
 namespace mozilla {
 namespace mscom {
 namespace detail {
 
 class LiveSet final
 {
 public:
   LiveSet()
@@ -407,60 +430,56 @@ Interceptor::GetInitialInterceptorForIID
   MOZ_ASSERT(!IsProxy(aTarget.get()));
 
   if (aTargetIid == IID_IUnknown) {
     // We must lock ourselves so that nothing can race with us once we have been
     // published to the live set.
     AutoLock lock(*this);
 
     HRESULT hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, Move(aTarget));
-    if (FAILED(hr)) {
-      return hr;
-    }
+    ENSURE_HR_SUCCEEDED(hr);
 
-    return QueryInterface(aTargetIid, aOutInterceptor);
+    hr = QueryInterface(aTargetIid, aOutInterceptor);
+    ENSURE_HR_SUCCEEDED(hr);
+    return hr;
   }
 
   // Raise the refcount for stabilization purposes during aggregation
   RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
         static_cast<WeakReferenceSupport*>(this)));
 
   RefPtr<IUnknown> unkInterceptor;
   HRESULT hr = CreateInterceptor(aTargetIid, kungFuDeathGrip,
                                  getter_AddRefs(unkInterceptor));
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   RefPtr<ICallInterceptor> interceptor;
   hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
                                       getter_AddRefs(interceptor));
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   hr = interceptor->RegisterSink(mEventSink);
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   // We must lock ourselves so that nothing can race with us once we have been
   // published to the live set.
   AutoLock lock(*this);
 
   hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid, Move(aTarget));
-  if (FAILED(hr)) {
+  ENSURE_HR_SUCCEEDED(hr);
+
+  if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) {
+    hr = unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
+    ENSURE_HR_SUCCEEDED(hr);
     return hr;
   }
 
-  if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) {
-    return unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
-  }
-
-  return GetInterceptorForIID(aTargetIid, aOutInterceptor);
+  hr = GetInterceptorForIID(aTargetIid, aOutInterceptor);
+  ENSURE_HR_SUCCEEDED(hr);
+  return hr;
 }
 
 /**
  * This method contains the core guts of the handling of QueryInterface calls
  * that are delegated to us from the ICallInterceptor.
  *
  * @param aIid ID of the desired interface
  * @param aOutInterceptor The resulting emulated vtable that corresponds to
@@ -500,66 +519,63 @@ Interceptor::GetInterceptorForIID(REFIID
   // (1a) A COM interceptor already exists for this interface, so all we need
   // to do is run a QI on it.
   if (unkInterceptor) {
     // Technically we didn't actually execute a QI on the target interface, but
     // for logging purposes we would like to record the fact that this interface
     // was requested.
     InterceptorLog::QI(S_OK, mTarget.get(), aIid, interfaceForQILog);
 
-    return unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
+    HRESULT hr = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
+    ENSURE_HR_SUCCEEDED(hr);
+    return hr;
   }
 
   // (2) Obtain a new target interface.
 
   // (2a) First, make sure that the target interface is available
   // NB: We *MUST* query the correct interface! ICallEvents::Invoke casts its
   // pvReceiver argument directly to the required interface! DO NOT assume
   // that COM will use QI or upcast/downcast!
   HRESULT hr;
 
   STAUniquePtr<IUnknown> targetInterface;
   IUnknown* rawTargetInterface = nullptr;
   hr = QueryInterfaceTarget(interceptorIid, (void**)&rawTargetInterface);
   targetInterface.reset(rawTargetInterface);
   InterceptorLog::QI(hr, mTarget.get(), aIid, targetInterface.get());
   MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
-  if (FAILED(hr)) {
+  if (hr == E_NOINTERFACE) {
     return hr;
   }
+  ENSURE_HR_SUCCEEDED(hr);
 
   // We *really* shouldn't be adding interceptors to proxies
   MOZ_ASSERT(aIid != IID_IMarshal);
 
   // (3) Create a new COM interceptor to that interface that delegates its
   // IUnknown to |this|.
 
   // Raise the refcount for stabilization purposes during aggregation
   RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>(
         static_cast<WeakReferenceSupport*>(this)));
 
   hr = CreateInterceptor(interceptorIid, kungFuDeathGrip,
                          getter_AddRefs(unkInterceptor));
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   // (4) Obtain the interceptor's ICallInterceptor interface and register our
   // event sink.
   RefPtr<ICallInterceptor> interceptor;
   hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
                                       (void**)getter_AddRefs(interceptor));
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   hr = interceptor->RegisterSink(mEventSink);
-  if (FAILED(hr)) {
-    return hr;
-  }
+  ENSURE_HR_SUCCEEDED(hr);
 
   // (5) Now that we have this new COM interceptor, insert it into the map.
 
   { // Scope for lock
     MutexAutoLock lock(mMutex);
     // We might have raced with another thread, so first check that we don't
     // already have an entry for this
     MapEntry* entry = Lookup(interceptorIid);
@@ -571,17 +587,19 @@ Interceptor::GetInterceptorForIID(REFIID
       // the map and its refcounting might not be thread-safe.
       IUnknown* rawTargetInterface = targetInterface.release();
       mInterceptorMap.AppendElement(MapEntry(interceptorIid,
                                              unkInterceptor,
                                              rawTargetInterface));
     }
   }
 
-  return unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
+  hr = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
+  ENSURE_HR_SUCCEEDED(hr);
+  return hr;
 }
 
 HRESULT
 Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput)
 {
   // NB: This QI needs to run on the main thread because the target object
   // is probably Gecko code that is not thread-safe. Note that this main
   // thread invocation is *synchronous*.
@@ -633,26 +651,22 @@ Interceptor::ThreadSafeQueryInterface(RE
       if (XRE_IsContentProcess()) {
         hr = FastMarshaler::Create(static_cast<IWeakReferenceSource*>(this),
                                    getter_AddRefs(mStdMarshalUnk));
       } else {
         hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
                                  SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
       }
 
-      if (FAILED(hr)) {
-        return hr;
-      }
+      ENSURE_HR_SUCCEEDED(hr);
     }
 
     if (!mStdMarshal) {
       hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
-      if (FAILED(hr)) {
-        return hr;
-      }
+      ENSURE_HR_SUCCEEDED(hr);
 
       // mStdMarshal is weak, so drop its refcount
       mStdMarshal->Release();
     }
 
     RefPtr<IMarshal> marshal(this);
     marshal.forget(aOutInterface);
     return S_OK;
@@ -663,19 +677,18 @@ Interceptor::ThreadSafeQueryInterface(RE
     intcpt.forget(aOutInterface);
     return S_OK;
   }
 
   if (aIid == IID_IDispatch) {
     STAUniquePtr<IDispatch> disp;
     IDispatch* rawDisp = nullptr;
     HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
-    if (FAILED(hr)) {
-      return hr;
-    }
+    ENSURE_HR_SUCCEEDED(hr);
+
     disp.reset(rawDisp);
     return DispatchForwarder::Create(this, disp, aOutInterface);
   }
 
   return GetInterceptorForIID(aIid, (void**)aOutInterface);
 }
 
 ULONG
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -418,23 +418,16 @@ XPCShellEnvironment::Init()
     // unbuffer stdout so that output is in the correct order; note that stderr
     // is unbuffered by default
     setbuf(stdout, 0);
 
     AutoSafeJSContext cx;
 
     mGlobalHolder.init(cx);
 
-    nsCOMPtr<nsIXPConnect> xpc =
-      do_GetService(nsIXPConnect::GetCID());
-    if (!xpc) {
-        NS_ERROR("failed to get nsXPConnect service!");
-        return false;
-    }
-
     nsCOMPtr<nsIPrincipal> principal;
     nsCOMPtr<nsIScriptSecurityManager> securityManager =
         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv) && securityManager) {
         rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal));
         if (NS_FAILED(rv)) {
             fprintf(stderr, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
         }
@@ -450,28 +443,27 @@ XPCShellEnvironment::Init()
     }
 
     JS::CompartmentOptions options;
     options.creationOptions().setSystemZone();
     options.behaviors().setVersion(JSVERSION_DEFAULT);
     if (xpc::SharedMemoryEnabled())
         options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
 
-    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    rv = xpc->InitClassesWithNewWrappedGlobal(cx,
+    JS::Rooted<JSObject*> globalObj(cx);
+    rv = xpc::InitClassesWithNewWrappedGlobal(cx,
                                               static_cast<nsIGlobalObject *>(backstagePass),
                                               principal, 0,
                                               options,
-                                              getter_AddRefs(holder));
+                                              &globalObj);
     if (NS_FAILED(rv)) {
         NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
         return false;
     }
 
-    JS::Rooted<JSObject*> globalObj(cx, holder->GetJSObject());
     if (!globalObj) {
         NS_ERROR("Failed to get global JSObject!");
         return false;
     }
     JSAutoCompartment ac(cx, globalObj);
 
     backstagePass->SetGlobalObject(globalObj);
 
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -683,17 +683,17 @@ struct ZoneStats
 
     ~ZoneStats() {
         // |allStrings| is usually deleted and set to nullptr before this
         // destructor runs. But there are failure cases due to OOMs that may
         // prevent that, so it doesn't hurt to try again here.
         js_delete(allStrings);
     }
 
-    bool initStrings(JSRuntime* rt);
+    bool initStrings();
 
     void addSizes(const ZoneStats& other) {
         MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
         unusedGCThings.addSizes(other.unusedGCThings);
         stringInfo.add(other.stringInfo);
         shapeInfo.add(other.shapeInfo);
     }
@@ -803,17 +803,17 @@ struct CompartmentStats
 
     ~CompartmentStats() {
         // |allClasses| is usually deleted and set to nullptr before this
         // destructor runs. But there are failure cases due to OOMs that may
         // prevent that, so it doesn't hurt to try again here.
         js_delete(allClasses);
     }
 
-    bool initClasses(JSRuntime* rt);
+    bool initClasses();
 
     void addSizes(const CompartmentStats& other) {
         MOZ_ASSERT(isTotals);
         FOR_EACH_SIZE(ADD_OTHER_SIZE)
         classInfo.add(other.classInfo);
     }
 
     size_t sizeOfLiveGCThings() const {
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -241,17 +241,17 @@ function getDuplicateVariantRE() {
     //               / (DIGIT 3alphanum)
     var variant = "(?:" + alphanum + "{5,8}|(?:" + DIGIT + alphanum + "{3}))";
 
     // Match a langtag that contains a duplicate variant.
     var duplicateVariant =
         // Match everything in a langtag prior to any variants, and maybe some
         // of the variants as well (which makes this pattern inefficient but
         // not wrong, for our purposes);
-        "(?:" + alphanum + "{2,8}-)+" +
+        "^(?:" + alphanum + "{2,8}-)+" +
         // a variant, parenthesised so that we can refer back to it later;
         "(" + variant + ")-" +
         // zero or more subtags at least two characters long (thus stopping
         // before extension and privateuse components);
         "(?:" + alphanum + "{2,8}-)*" +
         // and the same variant again
         "\\1" +
         // ...but not followed by any characters that would turn it into a
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -630,17 +630,17 @@ MapObject::set(JSContext* cx, HandleObje
     }
 
     return true;
 }
 
 MapObject*
 MapObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
 {
-    auto map = cx->make_unique<ValueMap>(cx->runtime(),
+    auto map = cx->make_unique<ValueMap>(cx->zone(),
                                          cx->compartment()->randomHashCodeScrambler());
     if (!map || !map->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     MapObject* mapObj = NewObjectWithClassProto<MapObject>(cx,  proto);
     if (!mapObj)
@@ -1313,17 +1313,17 @@ SetObject::add(JSContext* cx, HandleObje
         return false;
     }
     return true;
 }
 
 SetObject*
 SetObject::create(JSContext* cx, HandleObject proto /* = nullptr */)
 {
-    auto set = cx->make_unique<ValueSet>(cx->runtime(),
+    auto set = cx->make_unique<ValueSet>(cx->zone(),
                                          cx->compartment()->randomHashCodeScrambler());
     if (!set || !set->init()) {
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     SetObject* obj = NewObjectWithClassProto<SetObject>(cx, proto);
     if (!obj)
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -76,21 +76,21 @@ template <class Key, class Value, class 
 class OrderedHashMap;
 
 template <class T, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashSet;
 
 typedef OrderedHashMap<HashableValue,
                        HeapPtr<Value>,
                        HashableValue::Hasher,
-                       RuntimeAllocPolicy> ValueMap;
+                       ZoneAllocPolicy> ValueMap;
 
 typedef OrderedHashSet<HashableValue,
                        HashableValue::Hasher,
-                       RuntimeAllocPolicy> ValueSet;
+                       ZoneAllocPolicy> ValueSet;
 
 template <typename ObjectT>
 class OrderedHashTableRef;
 
 struct UnbarrieredHashPolicy;
 
 class MapObject : public NativeObject {
   public:
@@ -125,17 +125,17 @@ class MapObject : public NativeObject {
     // Set call for public JSAPI exposure. Does not actually return map object
     // as stated in spec, expects caller to return a value. for instance, with
     // webidl maplike/setlike, should return interface object.
     static MOZ_MUST_USE bool set(JSContext *cx, HandleObject obj, HandleValue key, HandleValue val);
     static MOZ_MUST_USE bool clear(JSContext *cx, HandleObject obj);
     static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
                                       MutableHandleValue iter);
 
-    using UnbarrieredTable = OrderedHashMap<Value, Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
+    using UnbarrieredTable = OrderedHashMap<Value, Value, UnbarrieredHashPolicy, ZoneAllocPolicy>;
     friend class OrderedHashTableRef<MapObject>;
 
     static void sweepAfterMinorGC(FreeOp* fop, MapObject* mapobj);
 
   private:
     static const ClassSpec classSpec_;
     static const ClassOps classOps_;
 
@@ -229,17 +229,17 @@ class SetObject : public NativeObject {
     static SetObject* create(JSContext *cx, HandleObject proto = nullptr);
     static uint32_t size(JSContext *cx, HandleObject obj);
     static MOZ_MUST_USE bool has(JSContext *cx, HandleObject obj, HandleValue key, bool* rval);
     static MOZ_MUST_USE bool clear(JSContext *cx, HandleObject obj);
     static MOZ_MUST_USE bool iterator(JSContext *cx, IteratorKind kind, HandleObject obj,
                                       MutableHandleValue iter);
     static MOZ_MUST_USE bool delete_(JSContext *cx, HandleObject obj, HandleValue key, bool *rval);
 
-    using UnbarrieredTable = OrderedHashSet<Value, UnbarrieredHashPolicy, RuntimeAllocPolicy>;
+    using UnbarrieredTable = OrderedHashSet<Value, UnbarrieredHashPolicy, ZoneAllocPolicy>;
     friend class OrderedHashTableRef<SetObject>;
 
     static void sweepAfterMinorGC(FreeOp* fop, SetObject* setobj);
 
   private:
     static const ClassSpec classSpec_;
     static const ClassOps classOps_;
 
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -6,17 +6,16 @@
 
 #ifndef builtin_ModuleObject_h
 #define builtin_ModuleObject_h
 
 #include "jsapi.h"
 #include "jsatom.h"
 
 #include "builtin/SelfHostingDefines.h"
-#include "gc/Zone.h"
 #include "js/GCVector.h"
 #include "js/Id.h"
 #include "vm/NativeObject.h"
 #include "vm/ProxyObject.h"
 
 namespace js {
 
 class ModuleEnvironmentObject;
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -3051,17 +3051,17 @@ BlockOnPromise(JSContext* cx, HandleValu
     if (promiseObj && promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
         // |promise| is an unwrapped Promise, and |then| is the original
         // |Promise.prototype.then|, inline it here.
         // 25.4.5.3., step 3.
         RootedObject PromiseCtor(cx);
         if (!GetBuiltinConstructor(cx, JSProto_Promise, &PromiseCtor))
             return false;
 
-        RootedObject C(cx, SpeciesConstructor(cx, PromiseCtor, JSProto_Promise, IsPromiseSpecies));
+        RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
         if (!C)
             return false;
 
         RootedObject resultPromise(cx, blockedPromise_);
         RootedObject resolveFun(cx);
         RootedObject rejectFun(cx);
 
         // By default, the blocked promise is added as an extra entry to the
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9785,18 +9785,16 @@ BytecodeEmitter::emitIncOrDec(ParseNode*
         return emitPropIncDec(pn);
       case PNK_ELEM:
         return emitElemIncDec(pn);
       case PNK_CALL:
         return emitCallIncDec(pn);
       default:
         return emitNameIncDec(pn);
     }
-
-    return true;
 }
 
 // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
 // the comment on emitSwitch.
 MOZ_NEVER_INLINE bool
 BytecodeEmitter::emitLabeledStatement(const LabeledStatement* pn)
 {
     /*
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -787,17 +787,17 @@ static void
 DumpName(GenericPrinter& out, const CharT* s, size_t len)
 {
     if (len == 0)
         out.put("#<zero-length name>");
 
     for (size_t i = 0; i < len; i++) {
         char16_t c = s[i];
         if (c > 32 && c < 127)
-            fputc(c, stderr);
+            out.putChar(c);
         else if (c <= 255)
             out.printf("\\x%02x", unsigned(c));
         else
             out.printf("\\u%04x", unsigned(c));
     }
 }
 
 void
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1657,16 +1657,28 @@ ObjectDenseElementsMayBeMarkable(NativeO
         for (unsigned i = 0; i < nobj->getDenseInitializedLength(); i++)
             MOZ_ASSERT(!elements[i].isGCThing());
     }
 #endif
 
     return mayBeMarkable;
 }
 
+static inline void
+CheckForCompartmentMismatch(JSObject* obj, JSObject* obj2)
+{
+#ifdef DEBUG
+    if (MOZ_UNLIKELY(obj->compartment() != obj2->compartment())) {
+        fprintf(stderr, "Compartment mismatch in pointer from %s object slot to %s object\n",
+                obj->getClass()->name, obj2->getClass()->name);
+        MOZ_CRASH("Compartment mismatch");
+    }
+#endif
+}
+
 inline void
 GCMarker::processMarkStackTop(SliceBudget& budget)
 {
     /*
      * The function uses explicit goto and implements the scanning of the
      * object directly. It allows to eliminate the tail recursion and
      * significantly improve the marking performance, see bug 641025.
      */
@@ -1727,17 +1739,17 @@ GCMarker::processMarkStackTop(SliceBudge
             return;
         }
 
         const Value& v = *vp++;
         if (v.isString()) {
             traverseEdge(obj, v.toString());
         } else if (v.isObject()) {
             JSObject* obj2 = &v.toObject();
-            MOZ_ASSERT(obj->compartment() == obj2->compartment());
+            CheckForCompartmentMismatch(obj, obj2);
             if (mark(obj2)) {
                 // Save the rest of this value array for later and start scanning obj2's children.
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
         } else if (v.isSymbol()) {
             traverseEdge(obj, v.toSymbol());
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -453,16 +453,17 @@ class HeapCheckTracerBase : public JS::C
 {
   public:
     explicit HeapCheckTracerBase(JSRuntime* rt, WeakMapTraceKind weakTraceKind);
     bool init();
     bool traceHeap(AutoLockForExclusiveAccess& lock);
     virtual void checkCell(Cell* cell) = 0;
 
   protected:
+    void dumpCellInfo(Cell* cell);
     void dumpCellPath();
 
     Cell* parentCell() {
         return parentIndex == -1 ? nullptr : stack[parentIndex].thing.asCell();
     }
 
     size_t failures;
 
@@ -551,24 +552,35 @@ HeapCheckTracerBase::traceHeap(AutoLockF
             TraceChildren(this, item.thing);
         }
     }
 
     return !oom;
 }
 
 void
+HeapCheckTracerBase::dumpCellInfo(Cell* cell)
+{
+    auto kind = cell->getTraceKind();
+    fprintf(stderr, "%s", GCTraceKindToAscii(kind));
+    if (kind == JS::TraceKind::Object)
+        fprintf(stderr, " %s", static_cast<JSObject*>(cell)->getClass()->name);
+    fprintf(stderr, " %p", cell);
+}
+
+void
 HeapCheckTracerBase::dumpCellPath()
 {
     const char* name = contextName();
     for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
         const WorkItem& parent = stack[index];
         Cell* cell = parent.thing.asCell();
-        fprintf(stderr, "  from %s %p %s edge\n",
-                GCTraceKindToAscii(cell->getTraceKind()), cell, name);
+        fprintf(stderr, "  from ");
+        dumpCellInfo(cell);
+        fprintf(stderr, " %s edge\n", name);
         name = parent.name;
     }
     fprintf(stderr, "  from root %s\n", name);
 }
 
 #endif // defined(JSGC_HASH_TABLE_CHECKS) || defined(DEBUG)
 
 #ifdef JSGC_HASH_TABLE_CHECKS
@@ -643,26 +655,24 @@ CheckGrayMarkingTracer::CheckGrayMarking
     // Weak gray->black edges are allowed.
     setTraceWeakEdges(false);
 }
 
 void
 CheckGrayMarkingTracer::checkCell(Cell* cell)
 {
     Cell* parent = parentCell();
-    if (!cell->isTenured() || !parent || !parent->isTenured())
+    if (!parent)
         return;
 
-    TenuredCell* tenuredCell = &cell->asTenured();
-    TenuredCell* tenuredParent = &parent->asTenured();
-    if (tenuredParent->isMarkedBlack() && tenuredCell->isMarkedGray())
-    {
+    if (parent->isMarkedBlack() && cell->isMarkedGray()) {
         failures++;
-        fprintf(stderr, "Found black to gray edge to %s %p\n",
-                GCTraceKindToAscii(cell->getTraceKind()), cell);
+        fprintf(stderr, "Found black to gray edge to ");
+        dumpCellInfo(cell);
+        fprintf(stderr, "\n");
         dumpCellPath();
     }
 }
 
 bool
 CheckGrayMarkingTracer::check(AutoLockForExclusiveAccess& lock)
 {
     if (!traceHeap(lock))
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -99,18 +99,16 @@ Zone::init(bool isSystemArg)
            markedAtoms().init() &&
            atomCache().init() &&
            regExps.init();
 }
 
 void
 Zone::setNeedsIncrementalBarrier(bool needs)
 {
-    MOZ_ASSERT_IF(needs && isAtomsZone(),
-                  !runtimeFromActiveCooperatingThread()->hasHelperThreadZones());
     MOZ_ASSERT_IF(needs, canCollect());
     needsIncrementalBarrier_ = needs;
 }
 
 void
 Zone::beginSweepTypes(FreeOp* fop, bool releaseTypes)
 {
     AutoClearTypeInferenceStateOnOOM oom(this);
@@ -293,25 +291,24 @@ Zone::hasMarkedCompartments()
             return true;
     }
     return false;
 }
 
 bool
 Zone::canCollect()
 {
+    // The atoms zone cannot be collected while off-thread parsing is taking
+    // place.
+    if (isAtomsZone())
+        return !runtimeFromAnyThread()->hasHelperThreadZones();
+
     // Zones that will be or are currently used by other threads cannot be
     // collected.
-    if (!isAtomsZone() && group()->createdForHelperThread())
-        return false;
-
-    JSRuntime* rt = runtimeFromAnyThread();
-    if (isAtomsZone() && rt->hasHelperThreadZones())
-        return false;
-    return true;
+    return !group()->createdForHelperThread();
 }
 
 void
 Zone::notifyObservingDebuggers()
 {
     for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
         JSRuntime* rt = runtimeFromAnyThread();
         RootedGlobalObject global(TlsContext.get(), comps->unsafeUnbarrieredMaybeGlobal());
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -247,17 +247,17 @@ struct Zone : public JS::shadow::Zone,
     void changeGCState(GCState prev, GCState next) {
         MOZ_ASSERT(CurrentThreadIsHeapBusy());
         MOZ_ASSERT(gcState() == prev);
         MOZ_ASSERT_IF(next != NoGC, canCollect());
         gcState_ = next;
     }
 
     bool isCollecting() const {
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
+        MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
         return isCollectingFromAnyThread();
     }
 
     bool isCollectingFromAnyThread() const {
         if (CurrentThreadIsHeapCollecting())
             return gcState_ != NoGC;
         else
             return needsIncrementalBarrier();
@@ -430,17 +430,17 @@ struct Zone : public JS::shadow::Zone,
 
     JS::WeakCache<TypeDescrObjectSet>& typeDescrObjects() { return typeDescrObjects_.ref(); }
 
     bool addTypeDescrObject(JSContext* cx, HandleObject obj);
 
     bool triggerGCForTooMuchMalloc() {
         JSRuntime* rt = runtimeFromAnyThread();
 
-        if (CurrentThreadCanAccessRuntime(rt)) {
+        if (js::CurrentThreadCanAccessRuntime(rt)) {
             return rt->gc.triggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC,
                                         gcMallocCounter.bytes(), gcMallocCounter.maxBytes());
         }
         return false;
     }
 
     void resetGCMallocBytes() { gcMallocCounter.reset(); }
     void setGCMaxMallocBytes(size_t value) { gcMallocCounter.setMax(value); }
@@ -615,17 +615,17 @@ struct Zone : public JS::shadow::Zone,
         return uniqueIds().has(cell);
     }
 
     // Transfer an id from another cell. This must only be called on behalf of a
     // moving GC. This method is infallible.
     void transferUniqueId(js::gc::Cell* tgt, js::gc::Cell* src) {
         MOZ_ASSERT(src != tgt);
         MOZ_ASSERT(!IsInsideNursery(tgt));
-        MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
+        MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(runtimeFromActiveCooperatingThread()));
         MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
         MOZ_ASSERT(!uniqueIds().has(tgt));
         uniqueIds().rekeyIfMoved(src, tgt);
     }
 
     // Remove any unique id associated with this Cell.
     void removeUniqueId(js::gc::Cell* cell) {
         MOZ_ASSERT(js::CurrentThreadCanAccessZone(this));
@@ -654,16 +654,38 @@ struct Zone : public JS::shadow::Zone,
     }
     void setKeepShapeTables(bool b) {
         keepShapeTables_ = b;
     }
 
     // Delete an empty compartment after its contents have been merged.
     void deleteEmptyCompartment(JSCompartment* comp);
 
+    /*
+     * This variation of calloc will call the large-allocation-failure callback
+     * on OOM and retry the allocation.
+     */
+    template <typename T>
+    T* pod_callocCanGC(size_t numElems) {
+        T* p = pod_calloc<T>(numElems);
+        if (MOZ_LIKELY(!!p))
+            return p;
+        size_t bytes;
+        if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
+            reportAllocationOverflow();
+            return nullptr;
+        }
+        JSRuntime* rt = runtimeFromActiveCooperatingThread();
+        p = static_cast<T*>(rt->onOutOfMemoryCanGC(js::AllocFunction::Calloc, bytes));
+        if (!p)
+            return nullptr;
+        updateMallocCounter(bytes);
+        return p;
+    }
+
   private:
     js::ZoneGroupData<js::jit::JitZone*> jitZone_;
 
     js::ActiveThreadData<bool> gcScheduled_;
     js::ZoneGroupData<bool> gcPreserveCode_;
     js::ZoneGroupData<bool> keepShapeTables_;
 
     // Allow zones to be linked into a list
@@ -689,27 +711,27 @@ class ZoneGroupsIter
     ZoneGroup** it;
     ZoneGroup** end;
 
   public:
     explicit ZoneGroupsIter(JSRuntime* rt) : iterMarker(&rt->gc) {
         it = rt->gc.groups().begin();
         end = rt->gc.groups().end();
 
-        if (!done() && (*it)->createdForHelperThread())
+        if (!done() && (*it)->usedByHelperThread())
             next();
     }
 
     bool done() const { return it == end; }
 
     void next() {
         MOZ_ASSERT(!done());
         do {
             it++;
-        } while (!done() && (*it)->createdForHelperThread());
+        } while (!done() && (*it)->usedByHelperThread());
     }
 
     ZoneGroup* get() const {
         MOZ_ASSERT(!done());
         return *it;
     }
 
     operator ZoneGroup*() const { return get(); }
@@ -887,71 +909,57 @@ class CompartmentsIterT
     }
 
     operator JSCompartment*() const { return get(); }
     JSCompartment* operator->() const { return get(); }
 };
 
 typedef CompartmentsIterT<ZonesIter> CompartmentsIter;
 
-/*
- * Allocation policy that uses Zone::pod_malloc and friends, so that memory
- * pressure is accounted for on the zone. This is suitable for memory associated
- * with GC things allocated in the zone.
- *
- * Since it doesn't hold a JSContext (those may not live long enough), it can't
- * report out-of-memory conditions itself; the caller must check for OOM and
- * take the appropriate action.
- *
- * FIXME bug 647103 - replace these *AllocPolicy names.
- */
-class ZoneAllocPolicy
+template <typename T>
+inline T*
+ZoneAllocPolicy::maybe_pod_malloc(size_t numElems)
 {
-    Zone* const zone;
-
-  public:
-    MOZ_IMPLICIT ZoneAllocPolicy(Zone* zone) : zone(zone) {}
+    return zone->maybe_pod_malloc<T>(numElems);
+}
 
-    template <typename T>
-    T* maybe_pod_malloc(size_t numElems) {
-        return zone->maybe_pod_malloc<T>(numElems);
-    }
+template <typename T>
+inline T*
+ZoneAllocPolicy::maybe_pod_calloc(size_t numElems)
+{
+    return zone->maybe_pod_calloc<T>(numElems);
+}
 
-    template <typename T>
-    T* maybe_pod_calloc(size_t numElems) {
-        return zone->maybe_pod_calloc<T>(numElems);
-    }
+template <typename T>
+inline T*
+ZoneAllocPolicy::maybe_pod_realloc(T* p, size_t oldSize, size_t newSize)
+{
+    return zone->maybe_pod_realloc<T>(p, oldSize, newSize);
+}
 
-    template <typename T>
-    T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
-        return zone->maybe_pod_realloc<T>(p, oldSize, newSize);
-    }
-
-    template <typename T>
-    T* pod_malloc(size_t numElems) {
-        return zone->pod_malloc<T>(numElems);
-    }
+template <typename T>
+inline T*
+ZoneAllocPolicy::pod_malloc(size_t numElems)
+{
+    return zone->pod_malloc<T>(numElems);
+}
 
-    template <typename T>
-    T* pod_calloc(size_t numElems) {
-        return zone->pod_calloc<T>(numElems);
-    }
+template <typename T>
+inline T*
+ZoneAllocPolicy::pod_calloc(size_t numElems)
+{
+    return zone->pod_calloc<T>(numElems);
+}
 
-    template <typename T>
-    T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
-        return zone->pod_realloc<T>(p, oldSize, newSize);
-    }
-
-    void free_(void* p) { js_free(p); }
-    void reportAllocOverflow() const {}
-
-    MOZ_MUST_USE bool checkSimulatedOOM() const {
-        return !js::oom::ShouldFailWithOOM();
-    }
-};
+template <typename T>
+inline T*
+ZoneAllocPolicy::pod_realloc(T* p, size_t oldSize, size_t newSize)
+{
+    return zone->pod_realloc<T>(p, oldSize, newSize);
+}
 
 /*
  * Provides a delete policy that can be used for objects which have their
  * lifetime managed by the GC so they can be safely destroyed outside of GC.
  *
  * This is necessary for example when initializing such an object may fail after
  * the initial allocation. The partially-initialized object must be destroyed,
  * but it may not be safe to do so at the current time as the store buffer may
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1401141.js
@@ -0,0 +1,13 @@
+if (!('gczeal' in this))
+    quit();
+if (helperThreadCount() == 0)
+    quit();
+
+gczeal(15,1);
+setGCCallback({
+  action: "majorGC",
+});
+gcslice(3);
+var lfGlobal = newGlobal();
+lfGlobal.offThreadCompileScript("");
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1401014.js
@@ -0,0 +1,52 @@
+// Prevent optimizing top-level
+with ({}) { }
+
+
+// Unboxed object constructor candidate
+function Thing() {
+    this.a = {};    // Object || null
+    this.b = {};    // Object || null
+}
+
+(new Thing());
+(new Thing()).a = null;
+(new Thing()).b = null;
+
+
+var arr = new Array(1000);
+arr[0];
+
+var ctx = new Thing();
+
+function funPsh(t, x) {
+    t.a = x;
+}
+
+function funBug(t, i) {
+    t.b = t.a;      // GETPROP t.a
+    t.a = null;     // SETPROP t.a
+    arr[i] = 0;     // Bailout on uninitialized elements
+    return t.b;
+}
+
+// Ion compile
+for (var i = 0; i < 20000; ++i) {
+    funBug(ctx, 0);
+    funPsh(ctx, {});
+}
+
+// Invalidate
+let tmp = { a: null, b: {} };
+funBug(tmp, 0);
+
+// Ion compile
+for (var i = 0; i < 20000; ++i) {
+    funBug(ctx, 0);
+    funPsh(ctx, {});
+}
+
+// Trigger bailout
+let res = funBug(ctx, 500);
+
+// Result should not be clobbered by |t.a = null|
+assertEq(res === null, false);
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -24,18 +24,16 @@
 using namespace js;
 using namespace js::jit;
 
 using mozilla::IsInRange;
 
 uint32_t
 jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
 {
-    AutoUnsafeCallWithABI unsafe;
-
     JSContext* cx = TlsContext.get();
     MOZ_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
     MOZ_ASSERT(IsInRange(FAKE_EXITFP_FOR_BAILOUT, 0, 0x1000) &&
                IsInRange(FAKE_EXITFP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
                "Fake exitfp pointer should be within the first page.");
 
@@ -101,18 +99,16 @@ jit::Bailout(BailoutStack* sp, BaselineB
 
     return retval;
 }
 
 uint32_t
 jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
                          BaselineBailoutInfo** bailoutInfo)
 {
-    AutoUnsafeCallWithABI unsafe;
-
     sp->checkInvariants();
 
     JSContext* cx = TlsContext.get();
 
     // We don't have an exit frame.
     cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
 
     JitActivationIterator jitActivations(cx);
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -535,20 +535,18 @@ BaselineInspector::hasSeenDoubleResult(j
 
     const ICEntry& entry = icEntryFromPC(pc);
     ICStub* stub = entry.fallbackStub();
 
     MOZ_ASSERT(stub->isUnaryArith_Fallback() || stub->isBinaryArith_Fallback());
 
     if (stub->isUnaryArith_Fallback())
         return stub->toUnaryArith_Fallback()->sawDoubleResult();
-    else
-        return stub->toBinaryArith_Fallback()->sawDoubleResult();
 
-    return false;
+    return stub->toBinaryArith_Fallback()->sawDoubleResult();
 }
 
 JSObject*
 BaselineInspector::getTemplateObject(jsbytecode* pc)
 {
     if (!hasBaselineScript())
         return nullptr;
 
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -1675,17 +1675,17 @@ static bool
 CanAttachDenseElementHole(JSObject* obj, bool ownProp)
 {
     // Make sure the objects on the prototype don't have any indexed properties
     // or that such properties can't appear without a shape change.
     // Otherwise returning undefined for holes would obviously be incorrect,
     // because we would have to lookup a property on the prototype instead.
     do {
         // The first two checks are also relevant to the receiver object.
-        if (obj->isIndexed())
+        if (obj->isNative() && obj->as<NativeObject>().isIndexed())
             return false;
 
         if (ClassCanHaveExtraProperties(obj->getClass()))
             return false;
 
         // Don't need to check prototype for OwnProperty checks
         if (ownProp)
             return true;
@@ -3144,17 +3144,17 @@ SetPropIRGenerator::tryAttachSetDenseEle
 
 static bool
 CanAttachAddElement(JSObject* obj, bool isInit)
 {
     // Make sure the objects on the prototype don't have any indexed properties
     // or that such properties can't appear without a shape change.
     do {
         // The first two checks are also relevant to the receiver object.
-        if (obj->isIndexed())
+        if (obj->isNative() && obj->as<NativeObject>().isIndexed())
             return false;
 
         const Class* clasp = obj->getClass();
         if ((clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) &&
             (clasp->getAddProperty() ||
              clasp->getResolve() ||
              clasp->getOpsLookupProperty() ||
              clasp->getOpsSetProperty()))
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7907,38 +7907,38 @@ JitCompartment::generateStringConcatStub
 #ifdef MOZ_VTUNE
     vtune::MarkStub(code, "StringConcatStub");
 #endif
 
     return code;
 }
 
 JitCode*
-JitRuntime::generateMallocStub(JSContext* cx)
+JitZone::generateMallocStub(JSContext* cx)
 {
     const Register regReturn = CallTempReg0;
     const Register regNBytes = CallTempReg0;
 
     MacroAssembler masm(cx);
 
     AllocatableRegisterSet regs(RegisterSet::Volatile());
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
     regs.takeUnchecked(regNBytes);
     LiveRegisterSet save(regs.asLiveSet());
     masm.PushRegsInMask(save);
 
     const Register regTemp = regs.takeAnyGeneral();
-    const Register regRuntime = regTemp;
+    const Register regZone = regTemp;
     MOZ_ASSERT(regTemp != regNBytes);
 
     masm.setupUnalignedABICall(regTemp);
-    masm.movePtr(ImmPtr(cx->runtime()), regRuntime);
-    masm.passABIArg(regRuntime);
+    masm.movePtr(ImmPtr(cx->zone()), regZone);
+    masm.passABIArg(regZone);
     masm.passABIArg(regNBytes);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, MallocWrapper));
     masm.storeCallWordResult(regReturn);
 
     masm.PopRegsInMask(save);
     masm.ret();
 
     Linker linker(masm);
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/Ion.h"
 
+#include "jit/JitCompartment.h"
+
 #include "jscompartmentinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 JSRuntime*
 CompileRuntime::runtime()
 {
@@ -136,16 +138,22 @@ CompileZone::get(Zone* zone)
 }
 
 CompileRuntime*
 CompileZone::runtime()
 {
     return CompileRuntime::get(zone()->runtimeFromAnyThread());
 }
 
+JitCode*
+CompileZone::mallocStub()
+{
+    return zone()->jitZone()->mallocStub();
+}
+
 bool
 CompileZone::isAtomsZone()
 {
     return zone()->isAtomsZone();
 }
 
 #ifdef DEBUG
 const void*
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -63,16 +63,18 @@ class CompileZone
     Zone* zone();
 
   public:
     static CompileZone* get(Zone* zone);
 
     CompileRuntime* runtime();
     bool isAtomsZone();
 
+    JitCode* mallocStub();
+
 #ifdef DEBUG
     const void* addressOfIonBailAfter();
 #endif
 
     const void* addressOfJSContext();
     const void* addressOfNeedsIncrementalBarrier();
     const void* addressOfFreeList(gc::AllocKind allocKind);
     const void* addressOfNurseryPosition();
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -315,21 +315,16 @@ JitRuntime::initialize(JSContext* cx, Au
     if (!shapePreBarrier_)
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for ObjectGroup");
     objectGroupPreBarrier_ = generatePreBarrier(cx, MIRType::ObjectGroup);
     if (!objectGroupPreBarrier_)
         return false;
 
-    JitSpew(JitSpew_Codegen, "# Emitting malloc stub");
-    mallocStub_ = generateMallocStub(cx);
-    if (!mallocStub_)
-        return false;
-
     JitSpew(JitSpew_Codegen, "# Emitting free stub");
     freeStub_ = generateFreeStub(cx);
     if (!freeStub_)
         return false;
 
     JitSpew(JitSpew_Codegen, "# Emitting VM function wrappers");
     for (VMFunction* fun = VMFunction::functions; fun; fun = fun->next) {
         JitSpew(JitSpew_Codegen, "# VM function wrapper");
@@ -433,17 +428,17 @@ JitCompartment::JitCompartment()
 JitCompartment::~JitCompartment()
 {
     js_delete(stubCodes_);
 }
 
 bool
 JitCompartment::initialize(JSContext* cx)
 {
-    stubCodes_ = cx->new_<ICStubCodeMap>(cx->runtime());
+    stubCodes_ = cx->new_<ICStubCodeMap>(cx->zone());
     if (!stubCodes_)
         return false;
 
     if (!stubCodes_->init()) {
         ReportOutOfMemory(cx);
         return false;
     }
 
@@ -465,16 +460,23 @@ JitCompartment::ensureIonStubsExist(JSCo
 bool
 JitZone::init(JSContext* cx)
 {
     if (!baselineCacheIRStubCodes_.init()) {
         ReportOutOfMemory(cx);
         return false;
     }
 
+    JitSpew(JitSpew_Codegen, "# Emitting malloc stub");
+    mallocStub_ = generateMallocStub(cx);
+    if (!mallocStub_) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
     return true;
 }
 
 void
 jit::FreeIonBuilder(IonBuilder* builder)
 {
     // The builder is allocated into its LifoAlloc, so destroying that will
     // destroy the builder and all other data accumulated during compilation,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12023,20 +12023,22 @@ IonBuilder::setPropTryInlineAccess(bool*
             return Ok();
 
         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
         if (needsPostBarrier(value))
             current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
-        storeUnboxedProperty(obj, property->offset, property->type, value);
+        MInstruction* store = storeUnboxedProperty(obj, property->offset, property->type, value);
 
         current->push(value);
 
+        MOZ_TRY(resumeAfter(store));
+
         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
         *emitted = true;
         return Ok();
     }
 
     MOZ_ASSERT(receivers.length() > 1);
     spew("Inlining polymorphic SETPROP");
 
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -250,18 +250,16 @@ ControlFlowGenerator::traverseBytecode()
             }
             if (!current)
                 return true;
         }
 
         JSOp op = JSOp(*pc);
         pc += CodeSpec[op].length;
     }
-
-    return true;
 }
 
 ControlFlowGenerator::ControlStatus
 ControlFlowGenerator::snoopControlFlow(JSOp op)
 {
     switch (op) {
       case JSOP_POP:
       case JSOP_NOP: {
--- a/js/src/jit/JSJitFrameIter.cpp
+++ b/js/src/jit/JSJitFrameIter.cpp
@@ -20,16 +20,18 @@ JSJitFrameIter::JSJitFrameIter(const Jit
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(activation)
 {
     if (activation_->bailoutData()) {
         current_ = activation_->bailoutData()->fp();
         frameSize_ = activation_->bailoutData()->topFrameSize();
         type_ = JitFrame_Bailout;
+    } else {
+        MOZ_ASSERT(!TlsContext.get()->inUnsafeCallWithABI);
     }
 }
 
 JSJitFrameIter::JSJitFrameIter(JSContext* cx)
   : JSJitFrameIter(cx->activation()->asJit())
 {
 }
 
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -124,18 +124,17 @@ class JitRuntime
 
     // Thunk that calls the GC pre barrier.
     ExclusiveAccessLockWriteOnceData<JitCode*> valuePreBarrier_;
     ExclusiveAccessLockWriteOnceData<JitCode*> stringPreBarrier_;
     ExclusiveAccessLockWriteOnceData<JitCode*> objectPreBarrier_;
     ExclusiveAccessLockWriteOnceData<JitCode*> shapePreBarrier_;
     ExclusiveAccessLockWriteOnceData<JitCode*> objectGroupPreBarrier_;
 
-    // Thunk to call malloc/free.
-    ExclusiveAccessLockWriteOnceData<JitCode*> mallocStub_;
+    // Thunk to call free.
     ExclusiveAccessLockWriteOnceData<JitCode*> freeStub_;
 
     // Thunk called to finish compilation of an IonScript.
     ExclusiveAccessLockWriteOnceData<JitCode*> lazyLinkStub_;
 
     // Thunk used by the debugger for breakpoint and step mode.
     ExclusiveAccessLockWriteOnceData<JitCode*> debugTrapHandler_;
 
@@ -160,17 +159,16 @@ class JitRuntime
     JitCode* generateExceptionTailStub(JSContext* cx, void* handler);
     JitCode* generateBailoutTailStub(JSContext* cx);
     JitCode* generateEnterJIT(JSContext* cx, EnterJitType type);
     JitCode* generateArgumentsRectifier(JSContext* cx, void** returnAddrOut);
     JitCode* generateBailoutTable(JSContext* cx, uint32_t frameClass);
     JitCode* generateBailoutHandler(JSContext* cx);
     JitCode* generateInvalidator(JSContext* cx);
     JitCode* generatePreBarrier(JSContext* cx, MIRType type);
-    JitCode* generateMallocStub(JSContext* cx);
     JitCode* generateFreeStub(JSContext* cx);
     JitCode* generateDebugTrapHandler(JSContext* cx);
     JitCode* generateBaselineDebugModeOSRHandler(JSContext* cx, uint32_t* noFrameRegPopOffsetOut);
     JitCode* generateVMWrapper(JSContext* cx, const VMFunction& f);
 
     bool generateTLEventVM(JSContext* cx, MacroAssembler& masm, const VMFunction& f, bool enter);
 
     inline bool generateTLEnterVM(JSContext* cx, MacroAssembler& masm, const VMFunction& f) {
@@ -281,20 +279,16 @@ class JitRuntime
           case MIRType::String: return stringPreBarrier_;
           case MIRType::Object: return objectPreBarrier_;
           case MIRType::Shape: return shapePreBarrier_;
           case MIRType::ObjectGroup: return objectGroupPreBarrier_;
           default: MOZ_CRASH();
         }
     }
 
-    JitCode* mallocStub() const {
-        return mallocStub_;
-    }
-
     JitCode* freeStub() const {
         return freeStub_;
     }
 
     JitCode* lazyLinkStub() const {
         return lazyLinkStub_;
     }
 
@@ -415,16 +409,19 @@ class JitZone
     // Map CacheIRStubKey to shared JitCode objects.
     using BaselineCacheIRStubCodeMap = GCHashMap<CacheIRStubKey,
                                                  ReadBarrieredJitCode,
                                                  CacheIRStubKey,
                                                  SystemAllocPolicy,
                                                  IcStubCodeMapGCPolicy<CacheIRStubKey>>;
     BaselineCacheIRStubCodeMap baselineCacheIRStubCodes_;
 
+    // Thunk to call malloc.
+    WriteOnceData<JitCode*> mallocStub_;
+
   public:
     MOZ_MUST_USE bool init(JSContext* cx);
     void sweep(FreeOp* fop);
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* jitZone,
                                 size_t* baselineStubsOptimized,
                                 size_t* cachedCFG) const;
@@ -468,16 +465,23 @@ class JitZone
             return false;
         IonCacheIRStubInfoSet::AddPtr p = ionCacheIRStubInfoSet_.lookupForAdd(lookup);
         MOZ_ASSERT(!p);
         return ionCacheIRStubInfoSet_.add(p, Move(key));
     }
     void purgeIonCacheIRStubInfo() {
         ionCacheIRStubInfoSet_.finish();
     }
+
+    JitCode* mallocStub() const {
+        return mallocStub_;
+    }
+
+  private:
+    JitCode* generateMallocStub(JSContext* cx);
 };
 
 enum class BailoutReturnStub {
     GetProp,
     GetPropSuper,
     SetProp,
     Call,
     New,
@@ -487,17 +491,17 @@ enum class BailoutReturnStub {
 class JitCompartment
 {
     friend class JitActivation;
 
     // Map ICStub keys to ICStub shared code objects.
     using ICStubCodeMap = GCHashMap<uint32_t,
                                     ReadBarrieredJitCode,
                                     DefaultHasher<uint32_t>,
-                                    RuntimeAllocPolicy,
+                                    ZoneAllocPolicy,
                                     IcStubCodeMapGCPolicy<uint32_t>>;
     ICStubCodeMap* stubCodes_;
 
     // Keep track of offset into various baseline stubs' code at return
     // point from called script.
     struct BailoutReturnStubInfo
     {
         void* addr;
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/LIR.h"
 
+#include "mozilla/ScopeExit.h"
+
 #include <ctype.h>
 
 #include "jsprf.h"
 
 #include "jit/JitSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 
@@ -230,20 +232,28 @@ LRecoverInfo::appendOperands(Node* ins)
     return true;
 }
 
 bool
 LRecoverInfo::appendDefinition(MDefinition* def)
 {
     MOZ_ASSERT(def->isRecoveredOnBailout());
     def->setInWorklist();
+    auto clearWorklistFlagOnFailure = mozilla::MakeScopeExit([&] {
+        def->setNotInWorklist();
+    });
 
     if (!appendOperands(def))
         return false;
-    return instructions_.append(def);
+
+    if (!instructions_.append(def))
+        return false;
+
+    clearWorklistFlagOnFailure.release();
+    return true;
 }
 
 bool
 LRecoverInfo::appendResumePoint(MResumePoint* rp)
 {
     // Stores should be recovered first.
     for (auto iter(rp->storesBegin()), end(rp->storesEnd()); iter != end; ++iter) {
         if (!appendDefinition(iter->operand))
@@ -257,31 +267,33 @@ LRecoverInfo::appendResumePoint(MResumeP
         return false;
 
     return instructions_.append(rp);
 }
 
 bool
 LRecoverInfo::init(MResumePoint* rp)
 {
+    // Before exiting this function, remove temporary flags from all definitions
+    // added in the vector.
+    auto clearWorklistFlags = mozilla::MakeScopeExit([&] {
+        for (MNode** it = begin(); it != end(); it++) {
+            if (!(*it)->isDefinition())
+                continue;
+            (*it)->toDefinition()->setNotInWorklist();
+        }
+    });
+
     // Sort operations in the order in which we need to restore the stack. This
     // implies that outer frames, as well as operations needed to recover the
     // current frame, are located before the current frame. The inner-most
     // resume point should be the last element in the list.
     if (!appendResumePoint(rp))
         return false;
 
-    // Remove temporary flags from all definitions.
-    for (MNode** it = begin(); it != end(); it++) {
-        if (!(*it)->isDefinition())
-            continue;
-
-        (*it)->toDefinition()->setNotInWorklist();
-    }
-
     MOZ_ASSERT(mir() == rp);
     return true;
 }
 
 LSnapshot::LSnapshot(LRecoverInfo* recoverInfo, BailoutKind kind)
   : numSlots_(TotalOperandCount(recoverInfo) * BOX_PIECES),
     slots_(nullptr),
     recoverInfo_(recoverInfo),
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -847,17 +847,17 @@ MacroAssembler::callMallocStub(size_t nb
     const Register regNBytes = CallTempReg0;
 
     MOZ_ASSERT(nbytes > 0);
     MOZ_ASSERT(nbytes <= INT32_MAX);
 
     if (regNBytes != result)
         push(regNBytes);
     move32(Imm32(nbytes), regNBytes);
-    call(GetJitContext()->runtime->jitRuntime()->mallocStub());
+    call(GetJitContext()->compartment->zone()->mallocStub());
     if (regNBytes != result) {
         movePtr(regNBytes, result);
         pop(regNBytes);
     }
     branchTest32(Assembler::Zero, result, result, fail);
 }
 
 void
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -537,20 +537,20 @@ InterruptCheck(JSContext* cx)
         JitRuntime::AutoPreventBackedgePatching apbp(rt);
         cx->zone()->group()->jitZoneGroup->patchIonBackedges(cx, JitZoneGroup::BackedgeLoopHeader);
     }
 
     return CheckForInterrupt(cx);
 }
 
 void*
-MallocWrapper(JSRuntime* rt, size_t nbytes)
+MallocWrapper(JS::Zone* zone, size_t nbytes)
 {
     AutoUnsafeCallWithABI unsafe;
-    return rt->pod_malloc<uint8_t>(nbytes);
+    return zone->pod_malloc<uint8_t>(nbytes);
 }
 
 JSObject*
 NewCallObject(JSContext* cx, HandleShape shape, HandleObjectGroup group)
 {
     JSObject* obj = CallObject::create(cx, shape, group);
     if (!obj)
         return nullptr;
@@ -1232,17 +1232,16 @@ LeaveWith(JSContext* cx, BaselineFrame* 
     frame->popOffEnvironmentChain<WithEnvironmentObject>();
     return true;
 }
 
 bool
 InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
                         uint32_t numStackValues)
 {
-    AutoUnsafeCallWithABI unsafe;
     return frame->initForOsr(interpFrame, numStackValues);
 }
 
 JSObject*
 CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
                       HandleObject owner, int32_t offset)
 {
     MOZ_ASSERT(descr->is<TypeDescr>());
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -674,17 +674,17 @@ JSString* StringFromCodePoint(JSContext*
 
 MOZ_MUST_USE bool
 SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
             bool strict, jsbytecode* pc);
 
 MOZ_MUST_USE bool
 InterruptCheck(JSContext* cx);
 
-void* MallocWrapper(JSRuntime* rt, size_t nbytes);
+void* MallocWrapper(JS::Zone* zone, size_t nbytes);
 JSObject* NewCallObject(JSContext* cx, HandleShape shape, HandleObjectGroup group);
 JSObject* NewSingletonCallObject(JSContext* cx, HandleShape shape);
 JSObject* NewStringObject(JSContext* cx, HandleString str);
 
 bool OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out);
 bool OperatorInI(JSContext* cx, uint32_t index, HandleObject obj, bool* out);
 
 MOZ_MUST_USE bool
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -292,17 +292,18 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         masm.push(framePtr); // BaselineFrame
         masm.push(r0); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(r11); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
         Register jitcode = regs.takeAny();
         masm.pop(jitcode);
         masm.pop(framePtr);
 
         MOZ_ASSERT(jitcode != ReturnReg);
 
         Label error;
@@ -429,17 +430,18 @@ JitRuntime::generateInvalidator(JSContex
     masm.mov(sp, r1);
     const int sizeOfBailoutInfo = sizeof(void*)*2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.mov(sp, r2);
     masm.setupAlignedABICall();
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.passABIArg(r2);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.ma_ldr(DTRAddr(sp, DtrOffImm(0)), r2);
     {
         ScratchRegisterScope scratch(masm);
         masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1, scratch);
     }
     // Remove the return address, the IonScript, the register state
     // (InvaliationBailoutStack) and the space that was allocated for the return
@@ -671,17 +673,18 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // Decrement sp by another 4, so we keep alignment. Not Anymore! Pushing
     // both the snapshotoffset as well as the: masm.as_sub(sp, sp, Imm8(4));
 
     // Set the old (4-byte aligned) value of the sp as the first argument.
     masm.passABIArg(r0);
     masm.passABIArg(r1);
 
     // Sp % 8 == 0
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
     masm.ma_ldr(DTRAddr(sp, DtrOffImm(0)), r2);
     {
         ScratchRegisterScope scratch(masm);
         masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp, scratch);
     }
 
     // Common size of a bailout frame.
     uint32_t bailoutFrameSize = 0
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -194,17 +194,18 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         masm.push(BaselineFrameReg, reg_code);
 
         // Initialize the frame, including filling in the slots.
         masm.setupUnalignedABICall(r19);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame.
         masm.passABIArg(reg_osrFrame); // InterpreterFrame.
         masm.passABIArg(reg_osrNStack);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
         masm.pop(r19, BaselineFrameReg);
         MOZ_ASSERT(r19 != ReturnReg);
 
         masm.addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter()));
         masm.addPtr(Imm32(BaselineFrame::Size()), BaselineFrameReg);
 
         Label error;
@@ -297,17 +298,18 @@ JitRuntime::generateInvalidator(JSContex
     masm.Sub(x2, masm.GetStackPointer64(), Operand(sizeof(size_t) + sizeof(void*)));
     masm.moveToStackPtr(r2);
 
     masm.setupUnalignedABICall(r10);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
     masm.passABIArg(r2);
 
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.pop(r2, r1);
 
     masm.Add(masm.GetStackPointer64(), masm.GetStackPointer64(), x1);
     masm.Add(masm.GetStackPointer64(), masm.GetStackPointer64(),
              Operand(sizeof(InvalidationBailoutStack)));
     masm.syncStackPtr();
 
@@ -494,17 +496,18 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // Make space for the BaselineBailoutInfo* outparam.
     const int sizeOfBailoutInfo = sizeof(void*) * 2;
     masm.reserveStack(sizeOfBailoutInfo);
     masm.moveStackPtrTo(r1);
 
     masm.setupUnalignedABICall(r2);
     masm.passABIArg(r0);
     masm.passABIArg(r1);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.Ldr(x2, MemOperand(masm.GetStackPointer64(), 0));
     masm.addToStackPtr(Imm32(sizeOfBailoutInfo));
 
     static const uint32_t BailoutDataSize = sizeof(void*) * Registers::Total +
                                             sizeof(double) * FloatRegisters::TotalPhys;
 
     if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -260,17 +260,18 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
         regs.add(OsrFrameReg);
         regs.take(JSReturnOperand);
         Register jitcode = regs.takeAny();
         masm.loadPtr(Address(StackPointer, 0), jitcode);
         masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr);
         masm.freeStack(2 * sizeof(uintptr_t));
 
@@ -386,17 +387,18 @@ JitRuntime::generateInvalidator(JSContex
     masm.ma_addu(a1, StackPointer, Imm32(sizeof(uintptr_t)));
     // Pass pointer to BailoutInfo
     masm.movePtr(StackPointer, a2);
 
     masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
     masm.passABIArg(a2);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.loadPtr(Address(StackPointer, 0), a2);
     masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), a1);
     // Remove the return address, the IonScript, the register state
     // (InvaliationBailoutStack) and the space that was allocated for the
     // return value.
     masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer);
     // remove the space that this frame was using before the bailout
@@ -635,17 +637,18 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // Put pointer to BailoutInfo
     masm.subPtr(Imm32(bailoutInfoOutParamSize), StackPointer);
     masm.storePtr(ImmPtr(nullptr), Address(StackPointer, 0));
     masm.movePtr(StackPointer, a1);
 
     masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     // Get BailoutInfo pointer
     masm.loadPtr(Address(StackPointer, 0), a2);
 
     // Remove both the bailout frame and the topmost Ion frame's stack.
     if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
         // Load frameSize from stack
         masm.loadPtr(Address(StackPointer,
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -277,17 +277,18 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
-        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr));
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::InitBaselineFrameForOsr), MoveOp::GENERAL,
+                         CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
         regs.add(OsrFrameReg);
         Register jitcode = regs.takeAny();
         masm.loadPtr(Address(StackPointer, 0), jitcode);
         masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), framePtr);
         masm.freeStack(2 * sizeof(uintptr_t));
 
         Label error;
@@ -384,17 +385,18 @@ JitRuntime::generateInvalidator(JSContex
     masm.ma_daddu(a1, StackPointer, Imm32(sizeof(uintptr_t)));
     // Pass pointer to BailoutInfo
     masm.movePtr(StackPointer, a2);
 
     masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
     masm.passABIArg(a2);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, InvalidationBailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     masm.loadPtr(Address(StackPointer, 0), a2);
     masm.loadPtr(Address(StackPointer, sizeof(uintptr_t)), a1);
     // Remove the return address, the IonScript, the register state
     // (InvaliationBailoutStack) and the space that was allocated for the
     // return value.
     masm.addPtr(Imm32(sizeof(InvalidationBailoutStack) + 2 * sizeof(uintptr_t)), StackPointer);
     // remove the space that this frame was using before the bailout
@@ -628,17 +630,18 @@ GenerateBailoutThunk(JSContext* cx, Macr
     // Put pointer to BailoutInfo
     static const uint32_t sizeOfBailoutInfo = sizeof(uintptr_t) * 2;
     masm.subPtr(Imm32(sizeOfBailoutInfo), StackPointer);
     masm.movePtr(StackPointer, a1);
 
     masm.setupAlignedABICall();
     masm.passABIArg(a0);
     masm.passABIArg(a1);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout));
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, Bailout), MoveOp::GENERAL,
+                     CheckUnsafeCallWithABI::DontCheckOther);
 
     // Get BailoutInfo pointer
     masm.loadPtr(Address(StackPointer, 0), a2);
 
     // Stack is:
     //     [frame]
     //     snapshotOffset
     //     frameSize
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -706,17 +706,17 @@ CodeGeneratorShared::createNativeToBytec
         }
 
         // Otherwise, we must have reached the top without finding any siblings.
         MOZ_ASSERT(tree->isOutermostCaller());
         break;
     }
 
     // Allocate array for list.
-    JSScript** data = cx->runtime()->pod_malloc<JSScript*>(scriptList.length());
+    JSScript** data = cx->zone()->pod_malloc<JSScript*>(scriptList.length());
     if (!data)
         return false;
 
     for (uint32_t i = 0; i < scriptList.length(); i++)
         data[i] = scriptList[i];
 
     // Success.
     nativeToBytecodeScriptListLength_ = scriptList.length();
@@ -753,17 +753,17 @@ CodeGeneratorShared::generateCompactNati
         js_free(nativeToBytecodeScriptList_);
         return false;
     }
 
     MOZ_ASSERT(tableOffset > 0);
     MOZ_ASSERT(numRegions > 0);
 
     // Writer is done, copy it to