Merge mozilla-central to inbound. a=merge CLOSED TREE
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 01 Dec 2018 16:33:38 +0200
changeset 505564 e987e55bf874e2147dc799ecd8b686c6c711d722
parent 505563 74ec1377930745c3f3fdf9492c46001552ff14c2 (current diff)
parent 505524 b3085cd7b7c20a55894eb3dcd8c92ab2bc0f03f3 (diff)
child 505565 67a76c51ecbe36ef4ffb4774d137966b8e5676c1
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -852,16 +852,19 @@ pref("browser.sessionstore.debug", false
 pref("browser.sessionstore.debug.no_auto_updates", false);
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
 // Maximum number of bytes of DOMSessionStorage data we collect per origin.
 pref("browser.sessionstore.dom_storage_limit", 2048);
 // Amount of failed SessionFile writes until we restart the worker.
 pref("browser.sessionstore.max_write_failures", 5);
 
+// Whether to warn the user when quitting, even though their tabs will be restored.
+pref("browser.sessionstore.warnOnQuit", false);
+
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
 // Whether history is enabled or not.
 pref("places.history.enabled", true);
 
 // the (maximum) number of the recent visits to sample
 // when calculating frecency
@@ -1785,13 +1788,13 @@ pref("prio.publicKeyB", "26E6674E65425B8
 pref("toolkit.coverage.enabled", false);
 pref("toolkit.coverage.endpoint.base", "https://coverage.mozilla.org");
 // Whether or not Prio-encoded Telemetry will be sent along with the main ping.
 #if defined(NIGHTLY_BUILD) && defined(MOZ_LIBPRIO)
 pref("prio.enabled", true);
 #endif
 
 // Discovery prefs
-pref("browser.discovery.enabled", false);
+pref("browser.discovery.enabled", true);
 pref("browser.discovery.containers.enabled", true);
 pref("browser.discovery.sites", "addons.mozilla.org");
 
 pref("browser.engagement.recent_visited_origins.expiry", 86400); // 24 * 60 * 60 (24 hours in seconds)
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2656,17 +2656,17 @@ window._gBrowser = {
       [contextTab];
     for (let tab of tabs) {
       if (tab._tPos < this.tabs.length - 1) {
         this.moveTabTo(tab, this.tabs.length - 1);
       }
     }
   },
 
-  warnAboutClosingTabs(tabsToClose, aCloseTabs, aOptionalMessage) {
+  warnAboutClosingTabs(tabsToClose, aCloseTabs) {
     if (tabsToClose <= 1)
       return true;
 
     const pref = aCloseTabs == this.closingTabsEnum.ALL ?
       "browser.tabs.warnOnClose" : "browser.tabs.warnOnCloseOtherTabs";
     var shouldPrompt = Services.prefs.getBoolPref(pref);
     if (!shouldPrompt)
       return true;
@@ -2677,34 +2677,30 @@ window._gBrowser = {
     var warnOnClose = { value: true };
 
     // focus the window before prompting.
     // this will raise any minimized window, which will
     // make it obvious which window the prompt is for and will
     // solve the problem of windows "obscuring" the prompt.
     // see bug #350299 for more details
     window.focus();
-    var warningMessage;
-    if (aOptionalMessage) {
-      warningMessage = aOptionalMessage;
-    } else {
-      warningMessage =
-        PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
-          .replace("#1", tabsToClose);
-    }
+    let warningMessage = gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple");
+    warningMessage = PluralForm.get(tabsToClose, warningMessage).replace("#1", tabsToClose);
+    let flags = (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
+      (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1);
+    let checkboxLabel = aCloseTabs == this.closingTabsEnum.ALL ?
+      gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null;
     var buttonPressed =
       ps.confirmEx(window,
-        gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
+        gTabBrowserBundle.GetStringFromName("tabs.closeTitleTabs"),
         warningMessage,
-        (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0) +
-        (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
+        flags,
         gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
         null, null,
-        aCloseTabs == this.closingTabsEnum.ALL ?
-        gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
+        checkboxLabel,
         warnOnClose);
     var reallyClose = (buttonPressed == 0);
 
     // don't set the pref unless they press OK and it's false
     if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
       Services.prefs.setBoolPref(pref, false);
 
     return reallyClose;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1753,19 +1753,23 @@ BrowserGlue.prototype = {
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
     // 1. There is only 1 tab open in 1 window
-    // 2. browser.warnOnQuit or browser.warnOnClose == false
+    // 2. browser.warnOnQuit == false
     // 3. The browser is currently in Private Browsing mode
     // 4. The browser will be restarted.
+    // 5. The user has automatic session restore enabled and
+    //    browser.sessionstore.warnOnQuit is not set to true.
+    // 6. The user doesn't have automatic session restore enabled
+    //    and browser.tabs.warnOnClose is not set to true.
     //
     // Otherwise, we will show the "closing multiple tabs" dialog.
     //
     // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
     // "the last window is closing but we're not quitting (a non-browser window is open)"
     // and also "we're quitting by closing the last window".
 
     if (aQuitType == "restart" || aQuitType == "os-restart")
@@ -1787,38 +1791,75 @@ BrowserGlue.prototype = {
 
     if (pagecount < 2)
       return;
 
     if (!aQuitType)
       aQuitType = "quit";
 
     // browser.warnOnQuit is a hidden global boolean to override all quit prompts
-    // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
-    if (!Services.prefs.getBoolPref("browser.warnOnQuit") ||
-        !Services.prefs.getBoolPref("browser.tabs.warnOnClose"))
+    if (!Services.prefs.getBoolPref("browser.warnOnQuit"))
       return;
 
+    // If we're going to automatically restore the session, only warn if the user asked for that.
+    let sessionWillBeRestored = Services.prefs.getIntPref("browser.startup.page") == 3 ||
+                                Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
+    // In the sessionWillBeRestored case, we only check the sessionstore-specific pref:
+    if (sessionWillBeRestored) {
+      if (!Services.prefs.getBoolPref("browser.sessionstore.warnOnQuit", false)) {
+        return;
+      }
+    // Otherwise, we check browser.tabs.warnOnClose
+    } else if (!Services.prefs.getBoolPref("browser.tabs.warnOnClose")) {
+      return;
+    }
+
     let win = BrowserWindowTracker.getTopWindow();
 
-    // warnAboutClosingTabs checks browser.tabs.warnOnClose and returns if it's
-    // ok to close the window. It doesn't actually close the window.
-    if (windowcount == 1) {
-      aCancelQuit.data =
-        !win.gBrowser.warnAboutClosingTabs(pagecount, win.gBrowser.closingTabsEnum.ALL);
-    } else {
-      // More than 1 window. Compose our own message.
+    let warningMessage;
+    // More than 1 window. Compose our own message.
+    if (windowcount > 1) {
       let tabSubstring = gTabbrowserBundle.GetStringFromName("tabs.closeWarningMultipleWindowsTabSnippet");
       tabSubstring = PluralForm.get(pagecount, tabSubstring).replace(/#1/, pagecount);
-      let windowString = gTabbrowserBundle.GetStringFromName("tabs.closeWarningMultipleWindows");
+
+      let stringID = sessionWillBeRestored ? "tabs.closeWarningMultipleWindowsSessionRestore"
+                                           : "tabs.closeWarningMultipleWindows";
+      let windowString = gTabbrowserBundle.GetStringFromName(stringID);
       windowString = PluralForm.get(windowcount, windowString).replace(/#1/, windowcount);
-      windowString = windowString.replace(/%(?:1\$)?S/i, tabSubstring);
-      aCancelQuit.data =
-        !win.gBrowser.warnAboutClosingTabs(pagecount, win.gBrowser.closingTabsEnum.ALL, windowString);
+      warningMessage = windowString.replace(/%(?:1\$)?S/i, tabSubstring);
+    } else {
+      let stringID = sessionWillBeRestored ? "tabs.closeWarningMultipleSessionRestore"
+                                           : "tabs.closeWarningMultiple";
+      warningMessage = gTabbrowserBundle.GetStringFromName(stringID);
+      warningMessage = PluralForm.get(pagecount, warningMessage).replace("#1", pagecount);
     }
+
+    let warnOnClose = {value: true};
+    let titleId = AppConstants.platform == "win" ? "tabs.closeAndQuitTitleTabsWin"
+                                                 : "tabs.closeAndQuitTitleTabs";
+    let flags = (Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+      (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1);
+    // Only display the checkbox in the non-sessionrestore case.
+    let checkboxLabel = !sessionWillBeRestored ?
+      gTabbrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null;
+
+    // buttonPressed will be 0 for closing, 1 for cancel (don't close/quit)
+    let buttonPressed = Services.prompt.confirmEx(win,
+      gTabbrowserBundle.GetStringFromName(titleId),
+      warningMessage, flags,
+      gTabbrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
+      null, null,
+      checkboxLabel,
+      warnOnClose);
+    // If the user has unticked the box, and has confirmed closing, stop showing
+    // the warning.
+    if (!sessionWillBeRestored && buttonPressed == 0 && !warnOnClose.value) {
+      Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+    }
+    aCancelQuit.data = buttonPressed != 0;
   },
 
   _showUpdateNotification: function BG__showUpdateNotification() {
     Services.prefs.clearUserPref("app.update.postupdate");
 
     var um = Cc["@mozilla.org/updates/update-manager;1"].
              getService(Ci.nsIUpdateManager);
     try {
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -72,16 +72,17 @@ if (AppConstants.MOZ_DEV_EDITION) {
   ChromeUtils.defineModuleGetter(this, "FxAccounts",
     "resource://gre/modules/FxAccounts.jsm");
 }
 
 Preferences.addAll([
   // Startup
   { id: "browser.startup.page", type: "int" },
   { id: "browser.privatebrowsing.autostart", type: "bool" },
+  { id: "browser.sessionstore.warnOnQuit", type: "bool" },
 
   // Downloads
   { id: "browser.download.useDownloadDir", type: "bool" },
   { id: "browser.download.folderList", type: "int" },
   { id: "browser.download.dir", type: "file" },
 
   /* Tab preferences
   Preferences:
@@ -730,24 +731,32 @@ var gMainPane = {
    * on the value of the browser.privatebrowsing.autostart pref.
    */
   updateBrowserStartupUI() {
     const pbAutoStartPref = Preferences.get("browser.privatebrowsing.autostart");
     const startupPref = Preferences.get("browser.startup.page");
 
     let newValue;
     let checkbox = document.getElementById("browserRestoreSession");
+    let warnOnQuitCheckbox = document.getElementById("browserRestoreSessionQuitWarning");
     if (pbAutoStartPref.value || startupPref.locked) {
       checkbox.setAttribute("disabled", "true");
+      warnOnQuitCheckbox.setAttribute("disabled", "true");
     } else {
       checkbox.removeAttribute("disabled");
     }
     newValue = pbAutoStartPref.value ? false : startupPref.value === this.STARTUP_PREF_RESTORE_SESSION;
     if (checkbox.checked !== newValue) {
       checkbox.checked = newValue;
+      let warnOnQuitPref = Preferences.get("browser.sessionstore.warnOnQuit");
+      if (newValue && !warnOnQuitPref.locked && !pbAutoStartPref.value) {
+        warnOnQuitCheckbox.removeAttribute("disabled");
+      } else {
+        warnOnQuitCheckbox.setAttribute("disabled", "true");
+      }
     }
   },
 
   initBrowserLocale() {
     gMainPane.setBrowserLocales(Services.locale.appLocaleAsBCP47);
   },
 
   /**
@@ -885,24 +894,30 @@ var gMainPane = {
     this.showConfirmLanguageChangeMessageBar(locales);
   },
 
   onBrowserRestoreSessionChange(event) {
     const value = event.target.checked;
     const startupPref = Preferences.get("browser.startup.page");
     let newValue;
 
+    let warnOnQuitCheckbox = document.getElementById("browserRestoreSessionQuitWarning");
     if (value) {
       // We need to restore the blank homepage setting in our other pref
       if (startupPref.value === this.STARTUP_PREF_BLANK) {
         Preferences.get("browser.startup.homepage").value = "about:blank";
       }
       newValue = this.STARTUP_PREF_RESTORE_SESSION;
+      let warnOnQuitPref = Preferences.get("browser.sessionstore.warnOnQuit");
+      if (!warnOnQuitPref.locked) {
+        warnOnQuitCheckbox.removeAttribute("disabled");
+      }
     } else {
       newValue = this.STARTUP_PREF_HOMEPAGE;
+      warnOnQuitCheckbox.setAttribute("disabled", "true");
     }
     startupPref.value = newValue;
   },
 
   // TABS
 
   /*
    * Preferences:
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -41,16 +41,22 @@
       </deck>
     </hbox>
   </vbox>
 #endif
 
   <vbox id="startupPageBox">
     <checkbox id="browserRestoreSession"
               data-l10n-id="startup-restore-previous-session"/>
+    <hbox class="indent">
+      <checkbox id="browserRestoreSessionQuitWarning"
+                preference="browser.sessionstore.warnOnQuit"
+                disabled="true"
+                data-l10n-id="startup-restore-warn-on-quit"/>
+    </hbox>
   </vbox>
 
 #ifdef HAVE_SHELL_SERVICE
   <vbox id="defaultBrowserBox">
     <checkbox id="alwaysCheckDefault" preference="browser.shell.checkDefaultBrowser"
               data-l10n-id="always-check-default"/>
     <deck id="setDefaultPane">
       <hbox align="center" class="indent">
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -24,16 +24,18 @@ const PREF_UPLOAD_ENABLED = "datareporti
 
 const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode";
 const TRACKING_PROTECTION_PREFS = ["privacy.trackingprotection.enabled",
                                    "privacy.trackingprotection.pbmode.enabled"];
 
 const PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled";
 const PREF_NORMANDY_ENABLED = "app.normandy.enabled";
 
+const PREF_ADDON_RECOMMENDATIONS_ENABLED = "browser.discovery.enabled";
+
 XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
   try {
     let alertsService = Cc["@mozilla.org/alerts-service;1"]
       .getService(Ci.nsIAlertsService)
       .QueryInterface(Ci.nsIAlertsDoNotDisturb);
     // This will throw if manualDoNotDisturb isn't implemented.
     alertsService.manualDoNotDisturb;
     return alertsService;
@@ -119,16 +121,17 @@ Preferences.addAll([
 
 ]);
 
 // Study opt out
 if (AppConstants.MOZ_DATA_REPORTING) {
   Preferences.addAll([
     // Preference instances for prefs that we need to monitor while the page is open.
     { id: PREF_OPT_OUT_STUDIES_ENABLED, type: "bool" },
+    { id: PREF_ADDON_RECOMMENDATIONS_ENABLED, type: "bool" },
     { id: PREF_UPLOAD_ENABLED, type: "bool" },
   ]);
 }
 
 // Data Choices tab
 if (AppConstants.NIGHTLY_BUILD) {
   Preferences.add({ id: "browser.chrome.errorReporter.enabled", type: "bool" });
 }
@@ -136,16 +139,45 @@ if (AppConstants.MOZ_CRASHREPORTER) {
   Preferences.add({ id: "browser.crashReports.unsubmittedCheck.autoSubmit2", type: "bool" });
 }
 
 function setEventListener(aId, aEventType, aCallback) {
   document.getElementById(aId)
     .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
 }
 
+function dataCollectionCheckboxHandler({checkbox, pref, matchPref = () => true, isDisabled = () => false}) {
+  function updateCheckbox() {
+    let collectionEnabled = Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false);
+
+    if (collectionEnabled && matchPref()) {
+      if (Services.prefs.getBoolPref(pref, false)) {
+        checkbox.setAttribute("checked", "true");
+      } else {
+        checkbox.removeAttribute("checked");
+      }
+      checkbox.setAttribute("preference", pref);
+    } else {
+      checkbox.removeAttribute("preference");
+      checkbox.removeAttribute("checked");
+    }
+
+    // We can't use checkbox.disabled here because the XBL binding may not be present,
+    // in which case setting the property won't work properly.
+    if (!collectionEnabled || Services.prefs.prefIsLocked(pref) || isDisabled()) {
+      checkbox.setAttribute("disabled", "true");
+    } else {
+      checkbox.removeAttribute("disabled");
+    }
+  }
+
+  Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateCheckbox);
+  updateCheckbox();
+}
+
 var gPrivacyPane = {
   _pane: null,
 
   /**
    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
    */
   _shouldPromptForRestart: true,
 
@@ -378,16 +410,17 @@ var gPrivacyPane = {
       }
       if (AppConstants.MOZ_CRASHREPORTER) {
         this.initSubmitCrashes();
       }
       this.initSubmitHealthReport();
       setEventListener("submitHealthReportBox", "command",
         gPrivacyPane.updateSubmitHealthReport);
       this.initOptOutStudyCheckbox();
+      this.initAddonRecommendationsCheckbox();
     }
     this._initA11yState();
     let signonBundle = document.getElementById("signonBundle");
     let pkiBundle = document.getElementById("pkiBundle");
     appendSearchKeywords("showPasswords", [
       signonBundle.getString("loginsDescriptionAll2"),
     ]);
     appendSearchKeywords("viewSecurityDevicesButton", [
@@ -1531,69 +1564,53 @@ var gPrivacyPane = {
 
 
   /**
    * Initialize the opt-out-study preference checkbox into about:preferences and
    * handles events coming from the UI for it.
    */
   initOptOutStudyCheckbox(doc) {
     const allowedByPolicy = Services.policies.isAllowed("Shield");
-    const checkbox = document.getElementById("optOutStudiesEnabled");
-
-    function updateStudyCheckboxState() {
-      // The checkbox should be disabled if any of the below are true. This
-      // prevents the user from changing the value in the box.
-      //
-      // * the policy forbids shield
-      // * the Shield Study preference is locked
-      // * the FHR pref is false
-      //
-      // The checkbox should match the value of the preference only if all of
-      // these are true. Otherwise, the checkbox should remain unchecked. This
-      // is because in these situations, Shield studies are always disabled, and
-      // so showing a checkbox would be confusing.
-      //
-      // * the policy allows Shield
-      // * the FHR pref is true
-      // * Normandy is enabled
-
-      const checkboxMatchesPref = (
-        allowedByPolicy &&
-        Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false) &&
-        Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false)
-      );
 
-      if (checkboxMatchesPref) {
-        if (Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false)) {
-          checkbox.setAttribute("checked", "checked");
-        } else {
-          checkbox.removeAttribute("checked");
-        }
-        checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED);
-      } else {
-        checkbox.removeAttribute("preference");
-        checkbox.removeAttribute("checked");
-      }
+    // The checkbox should be disabled if any of the below are true. This
+    // prevents the user from changing the value in the box.
+    //
+    // * the policy forbids shield
+    // * the Shield Study preference is locked
+    // * the FHR pref is false
+    //
+    // The checkbox should match the value of the preference only if all of
+    // these are true. Otherwise, the checkbox should remain unchecked. This
+    // is because in these situations, Shield studies are always disabled, and
+    // so showing a checkbox would be confusing.
+    //
+    // * the policy allows Shield
+    // * the FHR pref is true
+    // * Normandy is enabled
+    dataCollectionCheckboxHandler({
+      checkbox: document.getElementById("optOutStudiesEnabled"),
+      matchPref: () => (
+        allowedByPolicy &&
+        Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false)
+      ),
+      isDisabled: () => !allowedByPolicy,
+      pref: PREF_OPT_OUT_STUDIES_ENABLED,
+    });
+  },
 
-      const isDisabled = (
-        !allowedByPolicy ||
-        Services.prefs.prefIsLocked(PREF_OPT_OUT_STUDIES_ENABLED) ||
-        !Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false)
-      );
+  initAddonRecommendationsCheckbox() {
+    // Setup the learn more link.
+    const url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "personalized-addons";
+    document.getElementById("addonRecommendationLearnMore").setAttribute("href", url);
 
-      // We can't use checkbox.disabled here because the XBL binding may not be present,
-      // in which case setting the property won't work properly.
-      if (isDisabled) {
-        checkbox.setAttribute("disabled", "true");
-      } else {
-        checkbox.removeAttribute("disabled");
-      }
-    }
-    Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateStudyCheckboxState);
-    updateStudyCheckboxState();
+    // Setup the checkbox.
+    dataCollectionCheckboxHandler({
+      checkbox: document.getElementById("addonRecommendationEnabled"),
+      pref: PREF_ADDON_RECOMMENDATIONS_ENABLED,
+    });
   },
 
   observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "sitedatamanager:updating-sites":
         // While updating, we want to disable this section and display loading message until updated
         this.toggleSiteData(false);
         this.showSiteDataLoading();
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -694,16 +694,25 @@
                     class="tail-with-learn-more"
                     data-l10n-id="collection-studies"/>
           <label id="viewShieldStudies"
                  href="about:studies"
                  useoriginprincipal="true"
                  class="learnMore text-link"
                  data-l10n-id="collection-studies-link"/>
         </hbox>
+
+        <hbox align="center">
+          <checkbox id="addonRecommendationEnabled"
+                    class="tail-with-learn-more"
+                    data-l10n-id="addon-recommendations"/>
+          <label id="addonRecommendationLearnMore"
+                 class="learnMore text-link"
+                 data-l10n-id="addon-recommendations-link"/>
+        </hbox>
       </vbox>
     </description>
 #ifndef MOZ_TELEMETRY_REPORTING
   <description id="TelemetryDisabledDesc"
     class="indent tip-caption" control="telemetryGroup"
     data-l10n-id="collection-health-report-disabled"/>
 #endif
 
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -157,16 +157,19 @@ is-not-default = { -brand-short-name } i
 set-as-my-default-browser =
     .label = Make Default…
     .accesskey = D
 
 startup-restore-previous-session =
     .label = Restore previous session
     .accesskey = s
 
+startup-restore-warn-on-quit =
+    .label = Warn you when quitting the browser
+
 disable-extension =
     .label = Disable Extension
 
 tabs-group-header = Tabs
 
 ctrl-tab-recently-used-order =
     .label = Ctrl+Tab cycles through tabs in recently used order
     .accesskey = T
@@ -945,16 +948,20 @@ collection-health-report =
     .label = Allow { -brand-short-name } to send technical and interaction data to { -vendor-short-name }
     .accesskey = r
 collection-health-report-link = Learn more
 
 collection-studies =
     .label = Allow { -brand-short-name } to install and run studies
 collection-studies-link = View { -brand-short-name } studies
 
+addon-recommendations =
+    .label = Allow { -brand-short-name } to make personalized extension recommendations
+addon-recommendations-link = Learn more
+
 # This message is displayed above disabled data sharing options in developer builds
 # or builds with no Telemetry support available.
 collection-health-report-disabled = Data reporting is disabled for this build configuration
 
 collection-browser-errors =
     .label = Allow { -brand-short-name } to send browser error reports (including error messages) to { -vendor-short-name }
     .accesskey = b
 collection-browser-errors-link = Learn more
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -1029,21 +1029,16 @@ you can use these alternative items. Oth
 <!ENTITY trackingProtection.block6.label "Turn on Blocking for This Site">
 <!ENTITY trackingProtection.block6.accesskey "T">
 <!ENTITY trackingProtection.reload2.label "Reload Page">
 <!ENTITY trackingProtection.reload2.accesskey "R">
 
 <!ENTITY pluginNotification.showAll.label "Show All">
 <!ENTITY pluginNotification.showAll.accesskey "S">
 
-<!-- LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.properties -->
-<!ENTITY pluginActivateNow.label "Allow Now">
-<!ENTITY pluginActivateAlways.label "Allow and Remember">
-<!ENTITY pluginBlockNow.label "Block Plugin">
-
 <!-- LOCALIZATION NOTE: (pluginNotification.width): This is used to determine the
      width of the plugin popup notification that can appear if a plugin has been
      blocked on a page. Should be wide enough to fit the pluginActivateNow.label
      and pluginActivateAlways.label strings above on a single line. This must be
      a CSS length value. -->
 <!ENTITY pluginNotification.width "28em">
 
 <!ENTITY uiTour.infoPanel.close "Close">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -293,32 +293,16 @@ crashedpluginsMessage.learnMore=Learn More…
 keywordURIFixup.message=Did you mean to go to %S?
 keywordURIFixup.goTo=Yes, take me to %S
 keywordURIFixup.goTo.accesskey=Y
 keywordURIFixup.dismiss=No thanks
 keywordURIFixup.dismiss.accesskey=N
 
 pluginInfo.unknownPlugin=Unknown
 
-# LOCALIZATION NOTE (pluginActivateNow.label, pluginActivateAlways.label, pluginBlockNow.label): These should be the same as the matching strings in browser.dtd
-# LOCALIZATION NOTE (pluginActivateNow.label): This button will enable the
-# plugin in the current session for an short time (about an hour), auto-renewed
-# if the site keeps using the plugin.
-pluginActivateNow.label=Allow Now
-pluginActivateNow.accesskey=N
-# LOCALIZATION NOTE (pluginActivateAlways.label): This button will enable the
-# plugin for a long while (90 days), auto-renewed if the site keeps using the
-# plugin.
-pluginActivateAlways.label=Allow and Remember
-pluginActivateAlways.accesskey=R
-pluginBlockNow.label=Block Plugin
-pluginBlockNow.accesskey=B
-pluginContinue.label=Continue Allowing
-pluginContinue.accesskey=C
-
 # Flash activation doorhanger UI
 flashActivate.message=Do you want to allow Adobe Flash to run on this site? Only allow Adobe Flash on sites you trust.
 flashActivate.outdated.message=Do you want to allow an outdated version of Adobe Flash to run on this site? An outdated version can affect browser performance and security.
 flashActivate.remember=Remember this decision
 flashActivate.noAllow=Don’t Allow
 flashActivate.allow=Allow
 flashActivate.noAllow.accesskey=D
 flashActivate.allow.accesskey=A
--- a/browser/locales/en-US/chrome/browser/tabbrowser.properties
+++ b/browser/locales/en-US/chrome/browser/tabbrowser.properties
@@ -1,35 +1,52 @@
 # 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/.
 
 tabs.emptyTabTitle=New Tab
 tabs.emptyPrivateTabTitle=Private Browsing
 tabs.closeTab=Close Tab
 tabs.close=Close
-tabs.closeWarningTitle=Confirm close
+tabs.closeTitleTabs=Close tabs?
+tabs.closeAndQuitTitleTabs=Quit and close tabs?
+tabs.closeAndQuitTitleTabsWin=Exit and close tabs?
 # LOCALIZATION NOTE (tabs.closeWarningMultiple):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple tabs.
 tabs.closeWarningMultiple=;You are about to close #1 tabs. Are you sure you want to continue?
+# LOCALIZATION NOTE (tabs.closeWarningMultipleSessionRestore):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The singular form is not considered since this string is used only for
+# multiple tabs.
+tabs.closeWarningMultipleSessionRestore=;You are about to close #1 tabs. These tabs will be restored when you restart. Are you sure you want to continue?
 tabs.closeButtonMultiple=Close tabs
 tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs
 
 # LOCALIZATION NOTE (tabs.closeWarningMultipleWindows):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
-# The singular form is not considered since this string is used only for
+# The forms for 0 or 1 items are not considered since this string is used only for
 # multiple windows. The %S replacement form will be replaced with the contents
 # of tabs.closeWarningMultipleWindowsTabSnippet, which will contain the number
 # of tabs in these windows.
 # Note that every one of these plural forms must contain one instance of '%S'.
 tabs.closeWarningMultipleWindows=;You are about to close #1 windows %S. Are you sure you want to continue?
+# LOCALIZATION NOTE (tabs.closeWarningMultipleWindowsSessionRestore):
+# Semicolon-separated list of plural forms. See:
+# http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# The forms for 0 or 1 items are not considered since this string is used only for
+# multiple windows. The %S replacement form will be replaced with the contents
+# of tabs.closeWarningMultipleWindowsTabSnippet, which will contain the number
+# of tabs in these windows.
+# Note that every one of these plural forms must contain one instance of '%S'.
+tabs.closeWarningMultipleWindowsSessionRestore=;You are about to close #1 windows %S. These tabs will be restored when you restart. Are you sure you want to continue?
 
 # LOCALIZATION NOTE (tabs.closeWarningMultipleWindowsTabSnippet):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The singular form is not considered since this string is used only for
 # multiple windows which must contain multiple tabs (in total).
 # This string will be inserted in tabs.closeWarningMultipleWindows
 tabs.closeWarningMultipleWindowsTabSnippet=;with #1 tabs
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
@@ -32,12 +32,12 @@ async function test() {
   await checkEvaluateInTopFrame(client, "number", 7);
   await resumeToLine(client, 21);
   await checkEvaluateInTopFrame(client, "number", 8);
   await resumeToLine(client, 21);
   await checkEvaluateInTopFrame(client, "number", 9);
   await resumeToLine(client, 21);
   await checkEvaluateInTopFrame(client, "number", 10);
 
-  await toolbox.destroy();
+  await toolbox.closeToolbox();
   await gBrowser.removeTab(tab);
   finish();
 }
--- a/devtools/client/framework/components/ToolboxToolbar.js
+++ b/devtools/client/framework/components/ToolboxToolbar.js
@@ -400,19 +400,17 @@ class ToolboxToolbar extends Component {
     const closeButtonId = "toolbox-close";
 
     const closeButton = canCloseToolbox
       ? button({
         id: closeButtonId,
         onFocus: () => focusButton(closeButtonId),
         className: "devtools-button",
         title: L10N.getStr("toolbox.closebutton.tooltip"),
-        onClick: () => {
-          closeToolbox();
-        },
+        onClick: () => closeToolbox(),
         tabIndex: focusedButton === "toolbox-close" ? "0" : "-1",
       })
       : null;
 
     return div({id: "toolbox-controls"},
       meatballMenuButton,
       closeButton
     );
--- a/devtools/client/framework/target.js
+++ b/devtools/client/framework/target.js
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
+const Services = require("Services");
 
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/debugger-client", true);
 loader.lazyRequireGetter(this, "gDevTools",
   "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "getFront", "devtools/shared/protocol", true);
 
@@ -467,16 +468,22 @@ Target.prototype = {
   get isMultiProcess() {
     return !this.window;
   },
 
   get canRewind() {
     return this.activeTab && this.activeTab.traits.canRewind;
   },
 
+  isReplayEnabled() {
+    return Services.prefs.getBoolPref("devtools.recordreplay.mvp.enabled")
+      && this.canRewind
+      && this.isLocalTab;
+  },
+
   getExtensionPathName(url) {
     // Return the url if the target is not a webextension.
     if (!this.isWebExtension) {
       throw new Error("Target is not a WebExtension");
     }
 
     try {
       const parsedURL = new URL(url);
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -68,16 +68,21 @@ loader.lazyRequireGetter(this, "createEd
 loader.lazyGetter(this, "domNodeConstants", () => {
   return require("devtools/shared/dom-node-constants");
 });
 
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
 });
 
+loader.lazyGetter(this, "reloadAndRecordTab",
+  () => require("devtools/client/webreplay/menu.js").reloadAndRecordTab);
+loader.lazyGetter(this, "reloadAndStopRecordingTab",
+  () => require("devtools/client/webreplay/menu.js").reloadAndStopRecordingTab);
+
 /**
  * A "Toolbox" is the component that holds all the tools for one specific
  * target. Visually, it's a document that includes the tools tabs and all
  * the iframes where the tool panels will be living in.
  *
  * @param {object} target
  *        The object the toolbox is debugging.
  * @param {string} selectedTool
@@ -124,16 +129,17 @@ function Toolbox(target, selectedTool, h
 
   this._toolRegistered = this._toolRegistered.bind(this);
   this._toolUnregistered = this._toolUnregistered.bind(this);
   this._onWillNavigate = this._onWillNavigate.bind(this);
   this._refreshHostTitle = this._refreshHostTitle.bind(this);
   this.toggleNoAutohide = this.toggleNoAutohide.bind(this);
   this._updateFrames = this._updateFrames.bind(this);
   this._splitConsoleOnKeypress = this._splitConsoleOnKeypress.bind(this);
+  this.closeToolbox = this.closeToolbox.bind(this);
   this.destroy = this.destroy.bind(this);
   this.highlighterUtils = getHighlighterUtils(this);
   this._highlighterReady = this._highlighterReady.bind(this);
   this._highlighterHidden = this._highlighterHidden.bind(this);
   this._applyCacheSettings = this._applyCacheSettings.bind(this);
   this._applyServiceWorkersTestingSettings =
     this._applyServiceWorkersTestingSettings.bind(this);
   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
@@ -925,17 +931,17 @@ Toolbox.prototype = {
                  });
     this.shortcuts.on(L10N.getStr("toolbox.toggleHost.key"),
                  event => {
                    this.switchToPreviousHost();
                    event.preventDefault();
                  });
 
     // Close toolbox key-shortcut handler
-    const onClose = event => this.destroy();
+    const onClose = event => this.closeToolbox();
     this.shortcuts.on(L10N.getStr("toolbox.toggleToolboxF12.key"), onClose);
 
     // CmdOrCtrl+W is registered only when the toolbox is running in
     // detached window. In the other case the entire browser tab
     // is closed when the user uses this shortcut.
     if (this.hostType == "window") {
       this.shortcuts.on(L10N.getStr("toolbox.closeToolbox.key"), onClose);
     }
@@ -1145,17 +1151,17 @@ Toolbox.prototype = {
     // Ensure the toolbar doesn't try to render until the tool is ready.
     const element = this.React.createElement(this.ToolboxController, {
       L10N,
       currentToolId: this.currentToolId,
       selectTool: this.selectTool,
       toggleOptions: this.toggleOptions,
       toggleSplitConsole: this.toggleSplitConsole,
       toggleNoAutohide: this.toggleNoAutohide,
-      closeToolbox: this.destroy,
+      closeToolbox: this.closeToolbox,
       focusButton: this._onToolbarFocus,
       toolbox: this,
       showDebugTargetInfo: this._showDebugTargetInfo,
       deviceDescription: this._deviceDescription,
       onTabsOrderUpdated: this._onTabsOrderUpdated,
     });
 
     this.component = this.ReactDOM.render(element, this._componentMount);
@@ -2174,17 +2180,16 @@ Toolbox.prototype = {
   },
 
   /**
    * Tells the target tab to reload.
    */
   reloadTarget: function(force) {
     if (this.target.canRewind) {
       // Recording tabs need to be reloaded in a new content process.
-      const { reloadAndRecordTab } = require("devtools/client/webreplay/menu");
       reloadAndRecordTab();
     } else {
       this.target.activeTab.reload({ force: force });
     }
   },
 
   /**
    * Loads the tool next to the currently selected tool.
@@ -2796,16 +2801,24 @@ Toolbox.prototype = {
    * Get the toolbox's notification component
    *
    * @return The notification box component.
    */
   getNotificationBox: function() {
     return this.notificationBox;
   },
 
+  closeToolbox: async function() {
+    const shouldStopRecording = this.target.isReplayEnabled();
+    await this.destroy();
+    if (shouldStopRecording) {
+      reloadAndStopRecordingTab();
+    }
+  },
+
   /**
    * Remove all UI elements, detach from target and clear up
    */
   destroy: function() {
     // If several things call destroy then we give them all the same
     // destruction promise so we're sure to destroy only once
     if (this._destroyer) {
       return this._destroyer;
--- a/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
+++ b/devtools/client/inspector/flexbox/components/FlexItemSizingProperties.js
@@ -108,21 +108,20 @@ class FlexItemSizingProperties extends P
           title,
           property
         ),
         this.renderSize(mainBaseSize)
       )
     );
   }
 
-  renderFlexibilitySection(flexItemSizing, properties, computedStyle) {
+  renderFlexibilitySection(flexItemSizing, mainFinalSize, properties, computedStyle) {
     const {
       mainDeltaSize,
       mainBaseSize,
-      mainFinalSize,
       lineGrowthState,
     } = flexItemSizing;
 
     // Don't display anything if all interesting sizes are 0.
     if (!mainFinalSize && !mainBaseSize && !mainDeltaSize) {
       return null;
     }
 
@@ -244,17 +243,17 @@ class FlexItemSizingProperties extends P
             null
         ),
         this.renderSize(mainMaxSize),
         this.renderReasons(reasons)
       )
     );
   }
 
-  renderFinalSizeSection({ mainFinalSize }) {
+  renderFinalSizeSection(mainFinalSize) {
     return (
       dom.li({ className: "section final no-property" },
         dom.span({ className: "name" },
           getStr("flexbox.itemSizing.finalSizeSectionHeader")
         ),
         this.renderSize(mainFinalSize)
       )
     );
@@ -277,23 +276,23 @@ class FlexItemSizingProperties extends P
       mainMinSize,
     } = flexItemSizing;
     const dimension = mainAxisDirection.startsWith("horizontal") ? "width" : "height";
 
     // Calculate the final size. This is base + delta, then clamped by min or max.
     let mainFinalSize = mainBaseSize + mainDeltaSize;
     mainFinalSize = Math.max(mainFinalSize, mainMinSize);
     mainFinalSize = Math.min(mainFinalSize, mainMaxSize);
-    flexItemSizing.mainFinalSize = mainFinalSize;
 
     return (
       dom.ul({ className: "flex-item-sizing" },
         this.renderBaseSizeSection(flexItemSizing, properties, dimension),
-        this.renderFlexibilitySection(flexItemSizing, properties, computedStyle),
+        this.renderFlexibilitySection(flexItemSizing, mainFinalSize, properties,
+          computedStyle),
         this.renderMinimumSizeSection(flexItemSizing, properties, dimension),
         this.renderMaximumSizeSection(flexItemSizing, properties, dimension),
-        this.renderFinalSizeSection(flexItemSizing)
+        this.renderFinalSizeSection(mainFinalSize)
       )
     );
   }
 }
 
 module.exports = FlexItemSizingProperties;
--- a/devtools/client/inspector/flexbox/components/Flexbox.js
+++ b/devtools/client/inspector/flexbox/components/Flexbox.js
@@ -105,17 +105,17 @@ class Flexbox extends PureComponent {
       );
     }
 
     const {
       flexItemShown,
     } = flexContainer;
 
     return (
-      dom.div({ id: "layout-flexbox-container" },
+      dom.div({ className: "layout-flexbox-wrapper" },
         Header({
           flexContainer,
           getSwatchColorPickerTooltip,
           onHideBoxModelHighlighter,
           onSetFlexboxOverlayColor,
           onShowBoxModelHighlighterForNode,
           onToggleFlexboxHighlighter,
           setSelectedNode,
--- a/devtools/client/inspector/flexbox/flexbox.js
+++ b/devtools/client/inspector/flexbox/flexbox.js
@@ -300,17 +300,18 @@ class FlexboxInspector {
    * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
    * updates the flexbox panel because the shape of the flexbox on the page may have
    * changed.
    */
   async onReflow() {
     if (!this.isPanelVisible() ||
         !this.store ||
         !this.selection.nodeFront ||
-        !this.hasGetCurrentFlexbox) {
+        !this.hasGetCurrentFlexbox ||
+        this._isUpdating) {
       return;
     }
 
     try {
       const flexContainer = await this.getFlexContainerProps(this.selection.nodeFront);
 
       // Clear the flexbox panel if there is no flex container for the current node
       // selection.
@@ -404,23 +405,30 @@ class FlexboxInspector {
     this.highlighters.toggleFlexboxHighlighter(node, "layout");
     this.store.dispatch(updateFlexboxHighlighted(node !==
       this.highlighters.flexboxHighlighterShow));
   }
 
   /**
    * Handler for "new-root" event fired by the inspector and "new-node-front" event fired
    * by the inspector selection. Updates the flexbox panel if it is visible.
+   *
+   * @param  {Object}
+   *         This callback is sometimes executed on "new-node-front" events which means
+   *         that a first param is passed here (the nodeFront), which we don't care about.
+   * @param  {String} reason
+   *         On "new-node-front" events, a reason is passed here, and we need it to detect
+   *         if this update was caused by a node selection from the markup-view.
    */
-  onUpdatePanel() {
+  onUpdatePanel(_, reason) {
     if (!this.isPanelVisible()) {
       return;
     }
 
-    this.update();
+    this.update(null, null, reason === "treepanel");
   }
 
   /**
    * Track usage of the tool via telemetry.
    *
    * @param  {Boolean} isContainerInfoShown
    *         Whether the flex container accordion is displayed.
    * @param  {Boolean} isItemInfoShown
@@ -446,37 +454,43 @@ class FlexboxInspector {
    * with new flexbox data.
    *
    * @param  {Object|null} flexContainer
    *         An object consisting of the current flex container's flex items and
    *         properties.
    * @param  {Object|null} flexItemContainer
    *         An object consisting of the parent flex container's flex items and
    *         properties.
+   * @param  {Boolean} initiatedByMarkupViewSelection
+   *         True if the update was due to a node selection in the markup-view.
    */
-  async update(flexContainer, flexItemContainer) {
+  async update(flexContainer, flexItemContainer, initiatedByMarkupViewSelection) {
+    this._isUpdating = true;
+
     // Stop refreshing if the inspector or store is already destroyed or no node is
     // selected.
     if (!this.inspector ||
         !this.store ||
         !this.selection.nodeFront ||
         !this.hasGetCurrentFlexbox) {
+      this._isUpdating = false;
       return;
     }
 
     try {
       // Fetch the current flexbox if no flexbox front was passed into this update.
       if (!flexContainer) {
         flexContainer = await this.getFlexContainerProps(this.selection.nodeFront);
       }
 
       // Clear the flexbox panel if there is no flex container for the current node
       // selection.
       if (!flexContainer) {
         this.store.dispatch(clearFlexbox());
+        this._isUpdating = false;
         return;
       }
 
       if (!flexItemContainer && flexContainer.nodeFront === this.selection.nodeFront) {
         flexItemContainer = await this.getFlexContainerProps(this.selection.nodeFront,
           true);
       }
 
@@ -484,25 +498,28 @@ class FlexboxInspector {
         flexContainer.nodeFront === this.highlighters.flexboxHighlighterShown;
       const color = await this.getOverlayColor();
 
       this.store.dispatch(updateFlexbox({
         color,
         flexContainer,
         flexItemContainer,
         highlighted,
+        initiatedByMarkupViewSelection,
       }));
 
       const isContainerInfoShown = !flexContainer.flexItemShown || !!flexItemContainer;
       const isItemInfoShown = !!flexContainer.flexItemShown || !!flexItemContainer;
       this.sendTelemetryProbes(isContainerInfoShown, isItemInfoShown);
     } catch (e) {
       // This call might fail if called asynchrously after the toolbox is finished
       // closing.
     }
+
+    this._isUpdating = false;
   }
 }
 
 /**
  * For a given flex container object, returns the flex container properties that can be
  * used to check if 2 flex container objects are the same.
  *
  * @param  {Object|null} flexContainer
--- a/devtools/client/inspector/flexbox/reducers/flexbox.js
+++ b/devtools/client/inspector/flexbox/reducers/flexbox.js
@@ -46,16 +46,20 @@ const INITIAL_FLEXBOX = {
     isFlexItemContainer: true,
     // The NodeFront of the parent flex container.
     nodeFront: null,
     // The computed styles properties of the parent flex container.
     properties: null,
   },
   // Whether or not the flexbox highlighter is highlighting the flex container.
   highlighted: false,
+  // Whether or not the node selection that led to the flexbox tool being shown came from
+  // the user selecting a node in the markup-view (whereas, say, selecting in the flex
+  // items list)
+  initiatedByMarkupViewSelection: false,
 };
 
 const reducers = {
 
   [CLEAR_FLEXBOX](flexbox, _) {
     return INITIAL_FLEXBOX;
   },
 
--- a/devtools/client/inspector/flexbox/test/browser.ini
+++ b/devtools/client/inspector/flexbox/test/browser.ini
@@ -15,16 +15,17 @@ support-files =
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_flexbox_accordion_state.js]
 [browser_flexbox_container_and_item.js]
+[browser_flexbox_container_and_item_accordion_state.js]
 [browser_flexbox_container_and_item_updates_on_change.js]
 [browser_flexbox_container_element_rep.js]
 [browser_flexbox_container_properties.js]
 [browser_flexbox_empty_state.js]
 [browser_flexbox_highlighter_color_picker_on_ESC.js]
 [browser_flexbox_highlighter_color_picker_on_RETURN.js]
 [browser_flexbox_highlighter_opened_telemetry.js]
 [browser_flexbox_item_list_01.js]
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_accordion_state.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_accordion_state.js
@@ -18,18 +18,18 @@ add_task(async function() {
   await testAccordionStateAfterClickingHeader(doc);
   await testAccordionStateAfterSwitchingSidebars(inspector, doc);
   await testAccordionStateAfterReopeningLayoutView(toolbox);
 
   Services.prefs.clearUserPref(FLEXBOX_OPENED_PREF);
 });
 
 function testAccordionStateAfterClickingHeader(doc) {
-  const header = doc.querySelector(".flexbox-pane ._header");
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const header = doc.querySelector(".flex-accordion ._header");
+  const content = doc.querySelector(".flex-accordion ._content");
 
   info("Checking initial state of the flexbox panel.");
   is(content.style.display, "block", "The flexbox panel content is 'display: block'.");
   ok(Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref on by default.`);
 
   info("Clicking the flexbox header to hide the flexbox panel.");
   header.click();
@@ -38,17 +38,17 @@ function testAccordionStateAfterClicking
   is(content.style.display, "none", "The flexbox panel content is 'display: none'.");
   ok(!Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref off.`);
 }
 
 function testAccordionStateAfterSwitchingSidebars(inspector, doc) {
   info("Checking the flexbox accordion state is persistent after switching sidebars.");
 
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const content = doc.querySelector(".flex-accordion ._content");
 
   info("Selecting the computed view.");
   inspector.sidebar.select("computedview");
 
   info("Selecting the layout view.");
   inspector.sidebar.select("layoutview");
 
   info("Checking the state of the flexbox panel.");
@@ -62,15 +62,15 @@ async function testAccordionStateAfterRe
     + "the layout view.");
 
   info("Closing the toolbox.");
   await toolbox.destroy();
 
   info("Re-opening the layout view.");
   const { flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
-  const content = doc.querySelector(".flexbox-pane ._content");
+  const content = doc.querySelector(".flex-accordion ._content");
 
   info("Checking the state of the flexbox panel.");
   ok(!content, "The flexbox panel content is not rendered.");
   ok(!Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
     `${FLEXBOX_OPENED_PREF} is pref off.`);
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_container_and_item_accordion_state.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the order in which accordion items are shown for container-item elements.
+// For those combined types, the container accordion is shown first if the selection came
+// from the markup-view, because we assume in this case that users do want to see the
+// element selected as a container first.
+// However when users select an item in the list of items in the container accordion (or
+// in the item selector dropdown), then the item accordion should be shown first.
+
+const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { inspector, flexboxInspector } = await openLayoutView();
+  const { document: doc } = flexboxInspector;
+
+  info("Select a flex container-only node");
+  await selectNode("#container-only", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-header-container-properties").length);
+
+  info("Check that there is only 1 accordion for displayed");
+  let accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 1, "There's only 1 accordion");
+  ok(accordions[0].classList.contains("container"),
+     "The accordion is the container type");
+
+  info("Select a flex container+item node by clicking in the markup-view");
+  await clickOnNodeInMarkupView("#container-and-item", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 2);
+
+  info("Check that the 2 accordions are displayed, with container type being first");
+  accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 2, "There are 2 accordions");
+  ok(accordions[0].classList.contains("container"),
+     "The first accordion is the container type");
+  ok(accordions[1].classList.contains("item"),
+     "The second accordion is the item type");
+
+  info("Select the container-only node again");
+  await selectNode("#container-only", inspector);
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 1);
+
+  info("Click on the container+item node right there in the accordion item list");
+  doc.querySelector(".flex-item-list button").click();
+  await waitUntil(() => doc.querySelectorAll(".flex-accordion").length === 2);
+
+  info("Check that the 2 accordions are displayed again, with item type being first");
+  accordions = doc.querySelectorAll(".flex-accordion");
+  is(accordions.length, 2, "There are 2 accordions again");
+  ok(accordions[0].classList.contains("item"),
+     "The first accordion is the item type");
+  ok(accordions[1].classList.contains("container"),
+     "The second accordion is the container type");
+});
+
+async function clickOnNodeInMarkupView(selector, inspector) {
+  const { selection, markup } = inspector;
+
+  await markup.expandAll(selection.nodeFront);
+  const nodeFront = await getNodeFront(selector, inspector);
+  const markupContainer = markup.getContainer(nodeFront);
+
+  const onSelected = inspector.once("inspector-updated");
+  EventUtils.synthesizeMouseAtCenter(markupContainer.tagLine, {type: "mousedown"},
+    markup.doc.defaultView);
+  EventUtils.synthesizeMouseAtCenter(markupContainer.tagLine, {type: "mouseup"},
+    markup.doc.defaultView);
+  await onSelected;
+}
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_empty_state.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_empty_state.js
@@ -11,13 +11,13 @@ const TEST_URI = `
 
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { highlighters } = inspector;
 
   info("Checking the initial state of the Flexbox Inspector.");
-  ok(doc.querySelector(".flexbox-pane .devtools-sidepanel-no-result",
+  ok(doc.querySelector(".flex-accordion .devtools-sidepanel-no-result",
     "A message is displayede when no flex container is selected."));
   ok(!highlighters.flexboxHighlighterShown,
     "No flexbox highlighter exists in the highlighters overlay.");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_ESC.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_ESC.js
@@ -12,17 +12,17 @@ add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector, layoutView } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
 
   const onColorSwatchRendered = waitForDOM(doc,
-    "#layout-flexbox-container .layout-color-swatch");
+    ".layout-flexbox-wrapper .layout-color-swatch");
   await selectNode("#container", inspector);
   const [swatch] = await onColorSwatchRendered;
 
   info("Checking the initial state of the Flexbox Inspector color picker.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
     "The color swatch's background is correct.");
   is(store.getState().flexbox.color, "#9400FF", "The flexbox color state is correct.");
 
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_RETURN.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_highlighter_color_picker_on_RETURN.js
@@ -12,17 +12,17 @@ add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector, layoutView } = await openLayoutView();
   const { document: doc } = flexboxInspector;
   const { highlighters, store } = inspector;
   const cPicker = layoutView.swatchColorPickerTooltip;
   const spectrum = cPicker.spectrum;
 
   const onColorSwatchRendered = waitForDOM(doc,
-    "#layout-flexbox-container .layout-color-swatch");
+    ".layout-flexbox-wrapper .layout-color-swatch");
   await selectNode("#container", inspector);
   const [swatch] = await onColorSwatchRendered;
 
   const checkbox = doc.getElementById("flexbox-checkbox-toggle");
 
   info("Checking the initial state of the Flexbox Inspector color picker.");
   ok(!checkbox.checked, "Flexbox highlighter toggle is unchecked.");
   is(swatch.style.backgroundColor, "rgb(148, 0, 255)",
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_has_correct_layout.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_has_correct_layout.js
@@ -33,24 +33,21 @@ const TEST_DATA = [{
                         "60fr [final-end min] 140fr [basis-end delta-end]",
 }];
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
-  for (const {selector, expectedGridTemplate} of TEST_DATA) {
+  for (const { selector, expectedGridTemplate } of TEST_DATA) {
     info(`Checking the grid template for the flex item outline for ${selector}`);
 
-    const flexOutline = await selectNodeAndGetFlexOutline(selector, inspector, doc);
+    await selectNode(selector, inspector);
+    await waitUntil(() => {
+      const flexOutline = doc.querySelector(".flex-outline");
+      return flexOutline &&
+             flexOutline.style.gridTemplateColumns === expectedGridTemplate;
+    });
 
-    is(flexOutline.style.gridTemplateColumns, expectedGridTemplate,
-       "Grid template is correct");
+    ok(true, "Grid template is correct");
   }
 });
-
-async function selectNodeAndGetFlexOutline(selector, inspector, doc) {
-  const onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline");
-  await selectNode(selector, inspector);
-  const [flexOutlineContainer] = await onFlexItemOutlineRendered;
-  return flexOutlineContainer;
-}
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_renders_basisfinal_points_correctly.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_renders_basisfinal_points_correctly.js
@@ -7,31 +7,30 @@
 // Test that the flex item outline renders the basis and final points as a single point
 // if their sizes are equal. If not, then render as separate points.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   info("Select a flex item whose basis size matches its final size.");
-  let onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
+  let onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(".item", inspector);
-  let [flexOutlineContainer] = await onFlexItemOutlineRendered;
+  await onUpdate;
 
-  const [basisFinalPoint] = [...flexOutlineContainer.querySelectorAll(
-    ".flex-outline-point.basisfinal")];
+  const [basisFinalPoint] = [...doc.querySelectorAll(".flex-outline-point.basisfinal")];
 
   ok(basisFinalPoint, "The basis/final point exists");
 
   info("Select a flex item whose basis size is different than its final size.");
-  onFlexItemOutlineRendered = waitForDOM(doc, ".flex-outline-container");
+  onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(".shrinking .item", inspector);
-  [flexOutlineContainer] = await onFlexItemOutlineRendered;
+  await onUpdate;
 
-  const [basis, final] = [...flexOutlineContainer.querySelectorAll(
+  const [basis, final] = [...doc.querySelectorAll(
     ".flex-outline-point.basis, .flex-outline-point.final")];
 
   ok(basis, "The basis point exists");
   ok(final, "The final point exists");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_rotates_for_column.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_item_outline_rotates_for_column.js
@@ -24,16 +24,18 @@ add_task(async function() {
   // Check that the outline is wider than it is tall in the configuration.
   let bounds = flexOutline.getBoxQuads()[0].getBounds();
   ok(bounds.width > bounds.height, "The outline looks like a row");
 
   // Select a flex item in the column flexbox layout.
   onFlexItemOutlineRendered = waitForDOM(doc,
     ".flex-outline-container .flex-outline");
   await selectNode(".container.column .item", inspector);
-  ([flexOutline] = await onFlexItemOutlineRendered);
-
-  ok(flexOutline.classList.contains("column"), "The flex outline has the column class");
+  await waitUntil(() => {
+    flexOutline = doc.querySelector(".flex-outline-container .flex-outline.column");
+    return flexOutline;
+  });
+  ok(true, "The flex outline has the column class");
 
   // Check that the outline is taller than it is wide in the configuration.
   bounds = flexOutline.getBoxQuads()[0].getBounds();
   ok(bounds.height > bounds.width, "The outline looks like a column");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_non_flex_item_is_not_shown.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_non_flex_item_is_not_shown.js
@@ -16,13 +16,13 @@ const TEST_URI = `
 add_task(async function() {
   await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   info("Select the container's supposed flex item.");
   await selectNode("#item", inspector);
   const noFlexContainerOrItemSelected =
-    doc.querySelector(".flexbox-pane .devtools-sidepanel-no-result");
+    doc.querySelector(".flex-accordion .devtools-sidepanel-no-result");
 
   ok(noFlexContainerOrItemSelected,
     "The flexbox pane shows a message to select a flex container or item to continue.");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_pseudo_elements_are_listed.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_pseudo_elements_are_listed.js
@@ -10,17 +10,17 @@ const TEST_URI = URL_ROOT + "doc_flexbox
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   // Select the flex container in the inspector.
   const onItemsListRendered = waitForDOM(doc,
-    "#layout-flexbox-container .flex-item-list");
+    ".layout-flexbox-wrapper .flex-item-list");
   await selectNode(".container", inspector);
   const [flexItemList] = await onItemsListRendered;
 
   const items = [...flexItemList.querySelectorAll("button .objectBox")];
   is(items.length, 2, "There are 2 items displayed in the list");
 
   is(items[0].textContent, "::before", "The first item is ::before");
   is(items[1].textContent, "::after", "The second item is ::after");
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_flexibility_not_displayed_when_useless.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_flexibility_not_displayed_when_useless.js
@@ -7,26 +7,26 @@
 // Test that the flexibility section in the flex item sizing properties is not displayed
 // when the item did not grow or shrink.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   info("Select an item with flex:0 and wait for the sizing info to be rendered");
-  let onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  let onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode("#did-not-grow-or-shrink div", inspector);
-  let [flexSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
-  let flexSections = flexSizingContainer.querySelectorAll(".section.flexibility");
+  let flexSections = doc.querySelectorAll(".flex-item-sizing .section.flexibility");
   is(flexSections.length, 0, "The flexibility section was not found in the DOM");
 
   info("Select a more complex item which also doesn't flex and wait for the sizing info");
-  onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode("#just-enough-space-for-clamped-items div:last-child", inspector);
-  ([flexSizingContainer] = await onFlexItemSizingRendered);
+  await onUpdate;
 
-  flexSections = flexSizingContainer.querySelectorAll(".section.flexibility");
+  flexSections = doc.querySelectorAll(".flex-item-sizing .section.flexibility");
   is(flexSections.length, 0, "The flexibility section was not found in the DOM");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_do_not_show_unspecified_min_dimension.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_do_not_show_unspecified_min_dimension.js
@@ -4,30 +4,31 @@ http://creativecommons.org/publicdomain/
 
 "use strict";
 
 // Test that a flex item's min width/height value is not displayed if it's unspecified in
 // the CSS.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_unauthored_min_dimension.html";
 
-async function checkFlexItemCSSProperty(inspector, doc, selector) {
+async function checkFlexItemCSSProperty(inspector, store, doc, selector) {
   info("Select the container's flex item sizing info.");
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexItemSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info("Check that the minimum size section does not display minimum dimension text.");
-  const [sectionMinRowItem] = [...flexItemSizingContainer.querySelectorAll(
-    ".section.min")];
+  const [sectionMinRowItem] = [...doc.querySelectorAll(".flex-item-sizing .section.min")];
   const minDimension = sectionMinRowItem.querySelector(".css-property-link");
 
   ok(!minDimension, "Minimum dimension property should not be displayed.");
 }
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
-  await checkFlexItemCSSProperty(inspector, doc, "#flex-item-with-unauthored-min-width");
-  await checkFlexItemCSSProperty(inspector, doc, "#flex-item-with-unauthored-min-height");
+  await checkFlexItemCSSProperty(inspector, store, doc,
+    "#flex-item-with-unauthored-min-width");
+  await checkFlexItemCSSProperty(inspector, store, doc,
+    "#flex-item-with-unauthored-min-height");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_for_different_writing_modes.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_for_different_writing_modes.js
@@ -6,33 +6,36 @@ http://creativecommons.org/publicdomain/
 
 // Test that the flex item sizing info shows the correct dimension values for different
 // writing modes. For vertical writing modes, row items should display height values and
 // column items should display width values. The opposite is true for horizontal mode
 // where rows display width values and columns display height.
 
 const TEST_URI = URL_ROOT + "doc_flexbox_writing_modes.html";
 
-async function checkFlexItemDimension(inspector, doc, selector, expectedDimension) {
+async function checkFlexItemDimension(inspector, store, doc, selector, expected) {
   info("Select the container's flex item.");
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexItemSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info("Check that the minimum size section shows the correct dimension.");
-  const [sectionMinRowItem] = [...flexItemSizingContainer.querySelectorAll(
-    ".section.min")];
+  const [sectionMinRowItem] = [...doc.querySelectorAll(".flex-item-sizing .section.min")];
   const minDimension = sectionMinRowItem.querySelector(".css-property-link");
 
-  ok(minDimension.textContent.includes(expectedDimension),
+  ok(minDimension.textContent.includes(expected),
      "The flex item sizing has the correct dimension value.");
 }
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
-  await checkFlexItemDimension(inspector, doc, ".row.vertical.item", "min-height");
-  await checkFlexItemDimension(inspector, doc, ".column.vertical.item", "min-width");
-  await checkFlexItemDimension(inspector, doc, ".row.horizontal.item", "min-width");
-  await checkFlexItemDimension(inspector, doc, ".column.horizontal.item", "min-height");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".row.vertical.item", "min-height");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".column.vertical.item", "min-width");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".row.horizontal.item", "min-width");
+  await checkFlexItemDimension(inspector, store, doc,
+    ".column.horizontal.item", "min-height");
 });
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_has_correct_sections.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_sizing_info_has_correct_sections.js
@@ -22,33 +22,34 @@ const TEST_DATA = [{
 }, {
   selector: ".growing.is-clamped .item",
   expectedSections: ["Base Size", "Flexibility", "Maximum Size", "Final Size"],
 }];
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
-  const { document: doc } = flexboxInspector;
+  const { document: doc, store } = flexboxInspector;
 
   for (const { selector, expectedSections } of TEST_DATA) {
     info(`Checking the list of sections for the flex item ${selector}`);
-    const sections = await selectNodeAndGetFlexSizingSections(selector, inspector, doc);
+    const sections = await selectNodeAndGetFlexSizingSections(
+      selector, store, inspector, doc);
 
     is(sections.length, expectedSections.length, "Correct number of sections found");
     expectedSections.forEach((expectedSection, i) => {
       ok(sections[i].includes(expectedSection),
          `The ${expectedSection} section was found`);
     });
   }
 });
 
-async function selectNodeAndGetFlexSizingSections(selector, inspector, doc) {
-  const onFlexItemSizingRendered = waitForDOM(doc, "ul.flex-item-sizing");
+async function selectNodeAndGetFlexSizingSections(selector, store, inspector, doc) {
+  const onUpdate = waitUntilAction(store, "UPDATE_FLEXBOX");
   await selectNode(selector, inspector);
-  const [flexSizingContainer] = await onFlexItemSizingRendered;
+  await onUpdate;
 
   info(`Getting the list of displayed sections for ${selector}`);
-  const allSections = [...flexSizingContainer.querySelectorAll(".section .name")];
+  const allSections = [...doc.querySelectorAll("ul.flex-item-sizing .section .name")];
   const allSectionTitles = allSections.map(el => el.textContent);
 
   return allSectionTitles;
 }
--- a/devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_listed.js
+++ b/devtools/client/inspector/flexbox/test/browser_flexbox_text_nodes_are_listed.js
@@ -10,17 +10,17 @@ const TEST_URI = URL_ROOT + "doc_flexbox
 
 add_task(async function() {
   await addTab(TEST_URI);
   const { inspector, flexboxInspector } = await openLayoutView();
   const { document: doc } = flexboxInspector;
 
   // Select the flex container in the inspector.
   const onItemsListRendered = waitForDOM(doc,
-    "#layout-flexbox-container .flex-item-list");
+    ".layout-flexbox-wrapper .flex-item-list");
   await selectNode(".container", inspector);
   const [flexItemList] = await onItemsListRendered;
 
   const items = [...flexItemList.querySelectorAll("button .objectBox")];
   is(items.length, 3, "There are 3 items displayed in the list");
 
   is(items[0].textContent, "#text", "The first item is a text node");
   is(items[2].textContent, "#text", "The third item is a text node");
--- a/devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
+++ b/devtools/client/inspector/flexbox/test/doc_flexbox_specific_cases.html
@@ -109,8 +109,13 @@
 <div id="just-enough-space-for-clamped-items">
  <div></div>
  <div></div>
 </div>
 <div id="wanted-to-shrink-more-than-basis">
   <div>item wants to shrink more than its basis</div>
   <div></div>
 </div>
+<div class="container" id="container-only">
+  <div class="container" id="container-and-item">
+    <div>This item is inside a container-item element</div>
+  </div>
+</div>
--- a/devtools/client/inspector/flexbox/test/head.js
+++ b/devtools/client/inspector/flexbox/test/head.js
@@ -1,14 +1,16 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
-/* import-globals-from ../../../shared/test/telemetry-test-helpers.js */
 /* import-globals-from ../../test/head.js */
+/* import-globals-from ../../../inspector/rules/test/head.js */
+/* import-globals-from ../../../inspector/test/shared-head.js */
+/* import-globals-from ../../../shared/test/shared-redux-head.js */
 "use strict";
 
 // Import the inspector's head.js first (which itself imports shared-head.js).
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
   this);
 
 // Load the shared Redux helpers into this compartment.
--- a/devtools/client/inspector/layout/components/Accordion.js
+++ b/devtools/client/inspector/layout/components/Accordion.js
@@ -52,18 +52,19 @@ class Accordion extends PureComponent {
       item.onToggled();
     }
 
     this.setState({ opened, created });
   }
 
   renderContainer(item, i) {
     const { opened, created } = this.state;
-    const containerClassName =
-          item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
+    const containerClassName = item.className
+      ? item.className
+      : item.header.toLowerCase().replace(/\s/g, "-") + "-pane";
     let arrowClassName = "arrow theme-twisty";
     if (opened[i]) {
       arrowClassName += " open";
     }
 
     return div(
       { className: containerClassName, key: i },
 
--- a/devtools/client/inspector/layout/components/LayoutApp.js
+++ b/devtools/client/inspector/layout/components/LayoutApp.js
@@ -108,52 +108,59 @@ class LayoutApp extends PureComponent {
         onToggled: () => {
           const opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
           Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
         },
       },
     ];
 
     if (Services.prefs.getBoolPref(FLEXBOX_ENABLED_PREF)) {
+      const { flexContainer, flexItemContainer } = this.props.flexbox;
+      const opened = Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
+
       // Since the flexbox panel is hidden behind a pref. We insert the flexbox container
       // to the first index of the accordion item list.
       items.splice(0, 0, {
+        className: `flex-accordion ${flexContainer.flexItemShown ? "item" : "container"}`,
         component: Flexbox,
         componentProps: {
           ...this.props,
-          flexContainer: this.props.flexbox.flexContainer,
+          flexContainer,
           scrollToTop: this.scrollToTop,
         },
-        header: this.getFlexboxHeader(this.props.flexbox.flexContainer),
-        opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
+        header: this.getFlexboxHeader(flexContainer),
+        opened,
         onToggled: () => {
-          const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
-          Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
+          Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF,
+            !Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF));
         },
       });
 
       // If the current selected node is both a flex container and flex item. Render
-      // an accordion with another Flexbox component where the flexbox to show is the
-      // parent flex container of the current selected node.
-      if (this.props.flexbox.flexItemContainer &&
-          this.props.flexbox.flexItemContainer.actorID) {
-        // Insert the parent flex container to the first index of the accordion item
-        // list.
-        items.splice(0, 0, {
+      // an extra accordion with another Flexbox component where the node is shown as an
+      // item of its parent flex container.
+      // If the node was selected from the markup-view, then show this accordion after the
+      // container accordion. Otherwise show it first.
+      // The reason is that if the user selects an item-container in the markup view, it
+      // is assumed that they want to primarily see that element as a container, so the
+      // container info should be at the top.
+      if (flexItemContainer && flexItemContainer.actorID) {
+        items.splice(this.props.flexbox.initiatedByMarkupViewSelection ? 1 : 0, 0, {
+          className: "flex-accordion item",
           component: Flexbox,
           componentProps: {
             ...this.props,
-            flexContainer: this.props.flexbox.flexItemContainer,
+            flexContainer: flexItemContainer,
             scrollToTop: this.scrollToTop,
           },
-          header: this.getFlexboxHeader(this.props.flexbox.flexItemContainer),
-          opened: Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF),
+          header: this.getFlexboxHeader(flexItemContainer),
+          opened,
           onToggled: () => {
-            const opened =  Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF);
-            Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF, !opened);
+            Services.prefs.setBoolPref(FLEXBOX_OPENED_PREF,
+              !Services.prefs.getBoolPref(FLEXBOX_OPENED_PREF));
           },
         });
       }
     }
 
     return (
       dom.div({ className: "layout-container", ref: this.containerRef },
         Accordion({ items })
--- a/dom/media/systemservices/moz.build
+++ b/dom/media/systemservices/moz.build
@@ -24,16 +24,17 @@ if CONFIG['MOZ_WEBRTC']:
         '/media/webrtc/signaling',
         '/media/webrtc/trunk',
         '/media/webrtc/trunk/webrtc',
     ]
 
     if CONFIG['OS_TARGET'] != 'Android':
         UNIFIED_SOURCES += [
             'video_engine/desktop_capture_impl.cc',
+            'video_engine/platform_uithread.cc',
         ]
 
 if CONFIG['OS_TARGET'] == 'Android':
     DEFINES['WEBRTC_ANDROID'] = True
 
 
 if CONFIG['OS_TARGET'] == 'Android':
     EXPORTS += [
--- a/dom/media/systemservices/video_engine/desktop_capture_impl.cc
+++ b/dom/media/systemservices/video_engine/desktop_capture_impl.cc
@@ -25,16 +25,22 @@
 #include "rtc_base/trace_event.h"
 #include "video_engine/desktop_capture_impl.h"
 #include "modules/desktop_capture/desktop_frame.h"
 #include "modules/desktop_capture/desktop_device_info.h"
 #include "modules/desktop_capture/app_capturer.h"
 #include "modules/desktop_capture/desktop_capture_options.h"
 #include "modules/video_capture/video_capture.h"
 
+#if defined(_WIN32)
+#include "platform_uithread.h"
+#else
+#include "rtc_base/platform_thread.h"
+#endif
+
 namespace webrtc {
 
 ScreenDeviceInfoImpl::ScreenDeviceInfoImpl(const int32_t id) : _id(id) {}
 
 ScreenDeviceInfoImpl::~ScreenDeviceInfoImpl(void) {}
 
 int32_t ScreenDeviceInfoImpl::Init() {
   desktop_device_info_ =
--- a/dom/media/systemservices/video_engine/desktop_capture_impl.h
+++ b/dom/media/systemservices/video_engine/desktop_capture_impl.h
@@ -19,24 +19,29 @@
 #include <memory>
 
 #include "api/video/video_frame.h"
 #include "common_video/libyuv/include/webrtc_libyuv.h"
 #include "modules/video_capture/video_capture_config.h"
 #include "modules/desktop_capture/shared_memory.h"
 #include "modules/desktop_capture/desktop_device_info.h"
 #include "modules/desktop_capture/desktop_and_cursor_composer.h"
-#include "rtc_base/criticalsection.h"
-#include "rtc_base/platform_thread.h"
-#include "rtc_base/scoped_ref_ptr.h"
 #include "system_wrappers/include/event_wrapper.h"
 #include <set>
 
 using namespace webrtc::videocapturemodule;
 
+namespace rtc {
+#if defined(_WIN32)
+class PlatformUIThread;
+#else
+class PlatformThread;
+#endif
+}  // namespace rtc
+
 namespace webrtc {
 
 class VideoCaptureEncodeInterface;
 
 // simulate deviceInfo interface for video engine, bridge screen/application and
 // real screen/application device info
 
 class ScreenDeviceInfoImpl : public VideoCaptureModule::DeviceInfo {
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/video_engine/platform_uithread.cc
@@ -0,0 +1,149 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "platform_uithread.h"
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+// For use in ThreadWindowsUI callbacks
+static UINT static_reg_windows_msg = RegisterWindowMessageW(L"WebrtcWindowsUIThreadEvent");
+// timer id used in delayed callbacks
+static const UINT_PTR kTimerId = 1;
+static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
+static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
+
+bool PlatformUIThread::InternalInit() {
+  // Create an event window for use in generating callbacks to capture
+  // objects.
+  CritScope scoped_lock(&cs_);
+  if (hwnd_ == NULL) {
+    WNDCLASSW wc;
+    HMODULE hModule = GetModuleHandle(NULL);
+    if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
+      ZeroMemory(&wc, sizeof(WNDCLASSW));
+      wc.hInstance = hModule;
+      wc.lpfnWndProc = EventWindowProc;
+      wc.lpszClassName = kThreadWindow;
+      RegisterClassW(&wc);
+    }
+    hwnd_ = CreateWindowW(kThreadWindow, L"",
+                          0, 0, 0, 0, 0,
+                          NULL, NULL, hModule, NULL);
+    RTC_DCHECK(hwnd_);
+    SetPropW(hwnd_, kThisProperty, this);
+
+    if (timeout_) {
+      // if someone set the timer before we started
+      RequestCallbackTimer(timeout_);
+    }
+  }
+  return !!hwnd_;
+}
+
+void PlatformUIThread::RequestCallback() {
+  RTC_DCHECK(hwnd_);
+  RTC_DCHECK(static_reg_windows_msg);
+  PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
+}
+
+bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
+  CritScope scoped_lock(&cs_);
+  if (!hwnd_) {
+    // There is a condition that thread_ (PlatformUIThread) has been
+    // created but PlatformUIThread::Run() hasn't been run yet (hwnd_ is
+    // null while thread_ is not). If we do RTC_DCHECK(!thread_) here,
+    // it would lead to crash in this condition.
+
+    // set timer once thread starts
+  } else {
+    if (timerid_) {
+      KillTimer(hwnd_, timerid_);
+    }
+    timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
+  }
+  timeout_ = milliseconds;
+  return !!timerid_;
+}
+
+void PlatformUIThread::Stop() {
+  RTC_DCHECK(thread_checker_.CalledOnValidThread());
+  // Shut down the dispatch loop and let the background thread exit.
+  if (timerid_) {
+    KillTimer(hwnd_, timerid_);
+    timerid_ = 0;
+  }
+
+  PostMessage(hwnd_, WM_CLOSE, 0, 0);
+
+  hwnd_ = NULL;
+
+  PlatformThread::Stop();
+}
+
+void PlatformUIThread::Run() {
+  RTC_CHECK(InternalInit()); // always evaluates
+  do {
+    // The interface contract of Start/Stop is that for a successful call to
+    // Start, there should be at least one call to the run function.  So we
+    // call the function before checking |stop_|.
+    run_function_deprecated_(obj_);
+
+    // Alertable sleep to permit RaiseFlag to run and update |stop_|.
+    if (MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT,
+                                    MWMO_ALERTABLE | MWMO_INPUTAVAILABLE) ==
+        WAIT_OBJECT_0) {
+      MSG msg;
+      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+        if (msg.message == WM_QUIT) {
+          stop_ = true;
+          break;
+        }
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+      }
+    }
+
+  } while (!stop_);
+}
+
+void PlatformUIThread::NativeEventCallback() {
+  if (!run_function_deprecated_) {
+    stop_ = true;
+    return;
+  }
+  run_function_deprecated_(obj_);
+}
+
+/* static */
+LRESULT CALLBACK
+PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+  if (uMsg == WM_DESTROY) {
+    RemovePropW(hwnd, kThisProperty);
+    PostQuitMessage(0);
+    return 0;
+  }
+
+  PlatformUIThread *twui = static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty));
+  if (!twui) {
+    return DefWindowProc(hwnd, uMsg, wParam, lParam);
+  }
+
+  if ((uMsg == static_reg_windows_msg && uMsg != WM_NULL) ||
+      (uMsg == WM_TIMER && wParam == kTimerId)) {
+    twui->NativeEventCallback();
+    return 0;
+  }
+
+  return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+#endif
+
+}  // namespace rtc
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/video_engine/platform_uithread.h
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_PLATFORM_UITHREAD_H_
+#define RTC_BASE_PLATFORM_UITHREAD_H_
+
+#include "rtc_base/platform_thread.h"
+
+namespace rtc {
+
+#if defined(WEBRTC_WIN)
+class PlatformUIThread : public PlatformThread {
+ public:
+  PlatformUIThread(ThreadRunFunctionDeprecated func, void* obj,
+                   const char* thread_name)
+      : PlatformThread(func, obj, thread_name),
+        hwnd_(nullptr),
+        timerid_(0),
+        timeout_(0) {}
+  virtual ~PlatformUIThread() {}
+
+  void Stop() override;
+
+  /**
+   * Request an async callback soon.
+   */
+  void RequestCallback();
+
+  /**
+   * Request a recurring callback.
+   */
+  bool RequestCallbackTimer(unsigned int milliseconds);
+
+ protected:
+  void Run() override;
+
+ private:
+  static LRESULT CALLBACK EventWindowProc(HWND, UINT, WPARAM, LPARAM);
+  void NativeEventCallback();
+  bool InternalInit();
+
+  HWND hwnd_;
+  UINT_PTR timerid_;
+  unsigned int timeout_;
+};
+#endif
+
+}  // namespace rtc
+
+#endif  // RTC_BASE_PLATFORM_UITHREAD_H_
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -40,16 +40,20 @@ struct ScrollbarData;
  * the sampler thread.
  */
 class APZSampler {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZSampler)
 
  public:
   APZSampler(const RefPtr<APZCTreeManager>& aApz, bool aIsUsingWebRender);
 
+  // Whoever creates this sampler is responsible for calling Destroy() on it
+  // before releasing the owning refptr.
+  void Destroy();
+
   void SetWebRenderWindowId(const wr::WindowId& aWindowId);
 
   /**
    * This function is invoked from rust on the render backend thread when it
    * is created. It effectively tells the APZSampler "the current thread is
    * the sampler thread for this window id" and allows APZSampler to remember
    * which thread it is.
    */
@@ -118,17 +122,18 @@ class APZSampler {
 
   // Used to manage the mapping from a WR window id to APZSampler. These are
   // only used if WebRender is enabled. Both sWindowIdMap and mWindowId should
   // only be used while holding the sWindowIdLock. Note that we use a
   // StaticAutoPtr wrapper on sWindowIdMap to avoid a static initializer for the
   // unordered_map. This also avoids the initializer/memory allocation in cases
   // where we're not using WebRender.
   static StaticMutex sWindowIdLock;
-  static StaticAutoPtr<std::unordered_map<uint64_t, APZSampler*>> sWindowIdMap;
+  static StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<APZSampler>>>
+      sWindowIdMap;
   Maybe<wr::WrWindowId> mWindowId;
 
   // Lock used to protected mSamplerThreadId
   mutable Mutex mThreadIdLock;
   // If WebRender is enabled, this holds the thread id of the render backend
   // thread (which is the sampler thread) for the compositor associated with
   // this APZSampler instance.
   Maybe<PlatformThreadId> mSamplerThreadId;
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -16,45 +16,45 @@
 #include "mozilla/layers/SynchronousTask.h"
 #include "TreeTraversal.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 namespace layers {
 
 StaticMutex APZSampler::sWindowIdLock;
-StaticAutoPtr<std::unordered_map<uint64_t, APZSampler*>>
+StaticAutoPtr<std::unordered_map<uint64_t, RefPtr<APZSampler>>>
     APZSampler::sWindowIdMap;
 
 APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz,
                        bool aIsUsingWebRender)
     : mApz(aApz),
       mIsUsingWebRender(aIsUsingWebRender),
       mThreadIdLock("APZSampler::mThreadIdLock"),
       mSampleTimeLock("APZSampler::mSampleTimeLock") {
   MOZ_ASSERT(aApz);
   mApz->SetSampler(this);
 }
 
-APZSampler::~APZSampler() {
-  mApz->SetSampler(nullptr);
+APZSampler::~APZSampler() { mApz->SetSampler(nullptr); }
 
+void APZSampler::Destroy() {
   StaticMutexAutoLock lock(sWindowIdLock);
   if (mWindowId) {
     MOZ_ASSERT(sWindowIdMap);
     sWindowIdMap->erase(wr::AsUint64(*mWindowId));
   }
 }
 
 void APZSampler::SetWebRenderWindowId(const wr::WindowId& aWindowId) {
   StaticMutexAutoLock lock(sWindowIdLock);
   MOZ_ASSERT(!mWindowId);
   mWindowId = Some(aWindowId);
   if (!sWindowIdMap) {
-    sWindowIdMap = new std::unordered_map<uint64_t, APZSampler*>();
+    sWindowIdMap = new std::unordered_map<uint64_t, RefPtr<APZSampler>>();
     NS_DispatchToMainThread(NS_NewRunnableFunction(
         "APZUpdater::ClearOnShutdown", [] { ClearOnShutdown(&sWindowIdMap); }));
   }
   (*sWindowIdMap)[wr::AsUint64(aWindowId)] = this;
 }
 
 /*static*/ void APZSampler::SetSamplerThread(const wr::WrWindowId& aWindowId) {
   if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -426,16 +426,17 @@ void CompositorBridgeParent::StopAndClea
   mPaused = true;
 
   // We need to clear the APZ tree before we destroy the WebRender API below,
   // because in the case of async scene building that will shut down the updater
   // thread and we need to run the task before that happens.
   MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
   MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
   if (mApzUpdater) {
+    mApzSampler->Destroy();
     mApzSampler = nullptr;
     mApzUpdater->ClearTree(mRootLayerTreeID);
     mApzUpdater = nullptr;
     mApzcTreeManager = nullptr;
   }
 
   // Ensure that the layer manager is destroyed before CompositorBridgeChild.
   if (mLayerManager) {
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-d771bae9f824769c73419fdc3ccffa2bdc47c3e4
+5b26863178f8533eeba2de28c6bdc019ba9ed3e8
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -405,16 +405,19 @@ impl Document {
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         resource_profile: &mut ResourceProfileCounters,
     ) -> RenderedDocument {
         let accumulated_scale_factor = self.view.accumulated_scale_factor();
         let pan = self.view.pan.to_f32() / accumulated_scale_factor;
 
+        // Advance to the next frame.
+        self.stamp.advance();
+
         assert!(self.stamp.frame_id() != FrameId::INVALID,
                 "First frame increment must happen before build_frame()");
 
         let frame = {
             let frame_builder = self.frame_builder.as_mut().unwrap();
             let frame = frame_builder.build(
                 resource_cache,
                 gpu_cache,
@@ -527,19 +530,16 @@ impl Document {
 
         self.frame_builder = Some(built_scene.frame_builder);
 
         self.scratch.recycle();
 
         let old_scrolling_states = self.clip_scroll_tree.drain();
         self.clip_scroll_tree = built_scene.clip_scroll_tree;
         self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
-
-        // Advance to the next frame.
-        self.stamp.advance();
     }
 }
 
 struct DocumentOps {
     scroll: bool,
 }
 
 impl DocumentOps {
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -4766,16 +4766,17 @@ pub struct RendererOptions {
     pub debug_flags: DebugFlags,
     pub renderer_id: Option<u64>,
     pub disable_dual_source_blending: bool,
     pub scene_builder_hooks: Option<Box<SceneBuilderHooks + Send>>,
     pub sampler: Option<Box<AsyncPropertySampler + Send>>,
     pub chase_primitive: ChasePrimitive,
     pub support_low_priority_transactions: bool,
     pub namespace_alloc_by_client: bool,
+    pub enable_picture_caching: bool,
 }
 
 impl Default for RendererOptions {
     fn default() -> Self {
         RendererOptions {
             device_pixel_ratio: 1.0,
             resource_override_path: None,
             enable_aa: true,
@@ -4801,16 +4802,17 @@ impl Default for RendererOptions {
             renderer_id: None,
             cached_programs: None,
             disable_dual_source_blending: false,
             scene_builder_hooks: None,
             sampler: None,
             chase_primitive: ChasePrimitive::Nothing,
             support_low_priority_transactions: false,
             namespace_alloc_by_client: false,
+            enable_picture_caching: false,
         }
     }
 }
 
 #[cfg(not(feature = "debugger"))]
 pub struct DebugServer;
 
 #[cfg(not(feature = "debugger"))]
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -817,29 +817,29 @@ impl TextureCache {
             entry.eviction = Eviction::Auto;
         }
     }
 
     /// Returns the default eviction policy.
     ///
     /// These parameters come from very rough instrumentation of hits in the
     /// shared cache, with simple browsing on a few pages. In rough terms, more
-    /// than 95% of cache hits occur for entries that were used in the previous
-    /// frame, and 99% occur within two frames. If we exclude immediately-reused
-    /// (first frame) entries, 90% of the remaining hits happen within the first
-    /// 30 frames. So we can be relatively agressive about eviction without
-    /// sacrificing much in terms of cache performance.
-    ///
+    /// than 99.5% of cache hits occur for entries that were used in the previous
+    /// frame. This is obviously the dominant case, but we still want good behavior
+    /// in long-tail cases (i.e. a large image is scrolled off-screen and on again).
+    /// If we exclude immediately-reused (first frame) entries, 70% of the remaining
+    /// hits happen within the first 200 frames. So we can be relatively agressive
+    /// about eviction without sacrificing much in terms of cache performance.
     /// The one wrinkle is that animation-heavy pages do tend to extend the
     /// distribution, presumably because they churn through FrameIds faster than
     /// their more-static counterparts. As such, we _also_ provide a time floor
     /// (which was not measured with the same degree of rigour).
     fn default_eviction(&self) -> EvictionThreshold {
         EvictionThresholdBuilder::new(self.now)
-            .max_frames(30)
+            .max_frames(200)
             .max_time_s(3)
             .scale_by_pressure()
             .build()
     }
 
     /// Shared eviction code for standalone and shared entries.
     ///
     /// See `EvictionThreshold` for more details on policy.
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -386,17 +386,18 @@ void jit::CheckLogging() {
         "  bl-scripts    Baseline script-compilation\n"
         "  bl-op         Baseline compiler detailed op-specific messages\n"
         "  bl-ic         Baseline inline-cache messages\n"
         "  bl-ic-fb      Baseline IC fallback stub messages\n"
         "  bl-osr        Baseline IC OSR messages\n"
         "  bl-bails      Baseline bailouts\n"
         "  bl-dbg-osr    Baseline debug mode on stack recompile messages\n"
         "  bl-all        All baseline spew\n"
-        "  bl-ic-stats   Baseline IC Statistics\n"
+        "\n"
+        "See also SPEW=help for information on the Structured Spewer."
         "\n");
     exit(0);
     /*NOTREACHED*/
   }
   if (ContainsFlag(env, "aborts")) {
     EnableChannel(JitSpew_IonAbort);
   }
   if (ContainsFlag(env, "prune")) {
@@ -516,19 +517,16 @@ void jit::CheckLogging() {
     EnableChannel(JitSpew_BaselineOSR);
   }
   if (ContainsFlag(env, "bl-bails")) {
     EnableChannel(JitSpew_BaselineBailouts);
   }
   if (ContainsFlag(env, "bl-dbg-osr")) {
     EnableChannel(JitSpew_BaselineDebugModeOSR);
   }
-  if (ContainsFlag(env, "bl-ic-stats")) {
-    EnableChannel(JitSpew_BaselineIC_Statistics);
-  }
   if (ContainsFlag(env, "bl-all")) {
     EnableChannel(JitSpew_BaselineAbort);
     EnableChannel(JitSpew_BaselineScripts);
     EnableChannel(JitSpew_BaselineOp);
     EnableChannel(JitSpew_BaselineIC);
     EnableChannel(JitSpew_BaselineICFallback);
     EnableChannel(JitSpew_BaselineOSR);
     EnableChannel(JitSpew_BaselineBailouts);
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -100,19 +100,17 @@ namespace jit {
   _(IonMIR)                                \
   /* Information during bailouts */        \
   _(IonBailouts)                           \
   /* Information during OSI */             \
   _(IonInvalidate)                         \
   /* Debug info about snapshots */         \
   _(IonSnapshots)                          \
   /* Generated inline cache stubs */       \
-  _(IonIC)                                 \
-  /* Baseline IC Statistic information */  \
-  _(BaselineIC_Statistics)
+  _(IonIC)
 
 enum JitSpewChannel {
 #define JITSPEW_CHANNEL(name) JitSpew_##name,
   JITSPEW_CHANNEL_LIST(JITSPEW_CHANNEL)
 #undef JITSPEW_CHANNEL
       JitSpew_Terminator
 };
 
--- a/js/src/util/StructuredSpewer.cpp
+++ b/js/src/util/StructuredSpewer.cpp
@@ -169,16 +169,53 @@ void StructuredSpewer::parseSpewFlags(co
 #define CHECK_CHANNEL(name)                             \
   if (ContainsFlag(flags, #name) || star) {             \
     selectedChannels_.enableChannel(SpewChannel::name); \
   }
 
   STRUCTURED_CHANNEL_LIST(CHECK_CHANNEL)
 
 #undef CHECK_CHANNEL
+
+  if (ContainsFlag(flags, "help")) {
+     printf(
+        "\n"
+        "usage: SPEW=option,option,option,... where options can be:\n"
+        "\n"
+        "  help               Dump this help message\n"
+        "  all|*              Enable all the below channels\n"
+        "  channel[,channel]  Enable the selected channels from below\n"
+        "\n"
+        " Channels: \n"
+        "\n"
+        // List Channels
+        "  BaselineICStats    Dump the IC Entry counters during Ion analysis\n"
+        // End Channel list
+        "\n\n"
+        "By default output goes to a file called spew_output.$PID.$THREAD\n"
+        "\n"
+        "Further control of the sepewer can be accomplished with the below\n"
+        "environment variables:\n"
+        "\n"
+        "   SPEW_FILE: Selects the file to write to. An absolute path.\n"
+        "\n"
+        "   SPEW_FILTER: A string which is matched against 'signature'\n"
+        "        constructed from a JSScript, currently connsisting of \n"
+        "        filename:line:col.\n"
+        "\n"
+        "        A JSScript matches the filter string is found in the\n"
+        "        signature\n"
+        "\n"
+        "   SPEW_UPLOAD: If this variable is set as well as MOZ_UPLOAD_DIR,\n"
+        "        output goes to $MOZ_UPLOAD_DIR/spew_output* to ease usage\n"
+        "        with Treeherder.\n"
+
+        );
+    exit(0);
+  }
 }
 
 AutoStructuredSpewer::AutoStructuredSpewer(JSContext* cx, SpewChannel channel,
                                            JSScript* script)
     : printer_(mozilla::Nothing()) {
   if (!cx->spewer().enabled(cx, script, channel)) {
     return;
   }
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -324,25 +324,29 @@ void MobileViewportManager::UpdateResolu
       // not _smaller_ than the intrinsic scale, otherwise we might be
       // trying to show regions where there is no content to show.
       if (zoom < intrinsicScale) {
         newZoom = Some(intrinsicScale);
       }
     }
   }
 
-  // If the zoom has changed, update the pres shell resolution and the
-  // visual viewport size accordingly.
+  // If the zoom has changed, update the pres shell resolution accordingly.
   if (newZoom) {
     LayoutDeviceToLayerScale resolution = ZoomToResolution(*newZoom, cssToDev);
     MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
     mPresShell->SetResolutionAndScaleTo(resolution.scale);
 
     MVM_LOG("%p: New zoom is %f\n", this, newZoom->scale);
-    UpdateVisualViewportSize(aDisplaySize, *newZoom);
+  }
+
+  // The visual viewport size depends on both the zoom and the display size,
+  // and needs to be updated if either might have changed.
+  if (newZoom || aType == UpdateType::ViewportSize) {
+    UpdateVisualViewportSize(aDisplaySize, newZoom ? *newZoom : zoom);
   }
 }
 
 ScreenIntSize MobileViewportManager::GetCompositionSize(
     const ScreenIntSize& aDisplaySize) const {
   ScreenIntSize compositionSize(aDisplaySize);
   ScreenMargin scrollbars =
       LayoutDeviceMargin::FromAppUnits(
--- a/media/webrtc/trunk/webrtc/rtc_base/platform_thread.cc
+++ b/media/webrtc/trunk/webrtc/rtc_base/platform_thread.cc
@@ -23,25 +23,16 @@
 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) // WEBRTC_BSD
 #include <pthread_np.h>
 #elif defined(__NetBSD__) // WEBRTC_BSD
 #include <lwp.h>
 #endif
 
 namespace rtc {
 
-#if defined(WEBRTC_WIN)
-// For use in ThreadWindowsUI callbacks
-static UINT static_reg_windows_msg = RegisterWindowMessageW(L"WebrtcWindowsUIThreadEvent");
-// timer id used in delayed callbacks
-static const UINT_PTR kTimerId = 1;
-static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
-static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
-#endif
-
 PlatformThreadId CurrentThreadId() {
   PlatformThreadId ret;
 #if defined(WEBRTC_WIN)
   ret = GetCurrentThreadId();
 #elif defined(WEBRTC_POSIX)
 #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
   ret = pthread_mach_thread_np(pthread_self());
 #elif defined(WEBRTC_ANDROID)
@@ -148,69 +139,16 @@ PlatformThread::~PlatformThread() {
   RTC_DCHECK(thread_checker_.CalledOnValidThread());
 #if defined(WEBRTC_WIN)
   RTC_DCHECK(!thread_);
   RTC_DCHECK(!thread_id_);
 #endif  // defined(WEBRTC_WIN)
 }
 
 #if defined(WEBRTC_WIN)
-bool PlatformUIThread::InternalInit() {
-  // Create an event window for use in generating callbacks to capture
-  // objects.
-  CritScope scoped_lock(&cs_);
-  if (hwnd_ == NULL) {
-    WNDCLASSW wc;
-    HMODULE hModule = GetModuleHandle(NULL);
-    if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
-      ZeroMemory(&wc, sizeof(WNDCLASSW));
-      wc.hInstance = hModule;
-      wc.lpfnWndProc = EventWindowProc;
-      wc.lpszClassName = kThreadWindow;
-      RegisterClassW(&wc);
-    }
-    hwnd_ = CreateWindowW(kThreadWindow, L"",
-                          0, 0, 0, 0, 0,
-                          NULL, NULL, hModule, NULL);
-    RTC_DCHECK(hwnd_);
-    SetPropW(hwnd_, kThisProperty, this);
-
-    if (timeout_) {
-      // if someone set the timer before we started
-      RequestCallbackTimer(timeout_);
-    }
-  }
-  return !!hwnd_;
-}
-
-void PlatformUIThread::RequestCallback() {
-  RTC_DCHECK(hwnd_);
-  RTC_DCHECK(static_reg_windows_msg);
-  PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
-}
-
-bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
-  CritScope scoped_lock(&cs_);
-  if (!hwnd_) {
-    // There is a condition that thread_ (PlatformUIThread) has been
-    // created but PlatformUIThread::Run() hasn't been run yet (hwnd_ is
-    // null while thread_ is not). If we do RTC_DCHECK(!thread_) here,
-    // it would lead to crash in this condition.
-
-    // set timer once thread starts
-  } else {
-    if (timerid_) {
-      KillTimer(hwnd_, timerid_);
-    }
-    timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
-  }
-  timeout_ = milliseconds;
-  return !!timerid_;
-}
-
 DWORD WINAPI PlatformThread::StartThread(void* param) {
   // The GetLastError() function only returns valid results when it is called
   // after a Win32 API function that returns a "failed" result. A crash dump
   // contains the result from GetLastError() and to make sure it does not
   // falsely report a Windows error we call SetLastError here.
   ::SetLastError(ERROR_SUCCESS);
   static_cast<PlatformThread*>(param)->Run();
   return 0;
@@ -282,33 +220,16 @@ void PlatformThread::Stop() {
   RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
   if (!run_function_)
     AtomicOps::ReleaseStore(&stop_flag_, 0);
   thread_ = 0;
 #endif  // defined(WEBRTC_WIN)
   spawned_thread_checker_.DetachFromThread();
 }
 
-#ifdef WEBRTC_WIN
-void PlatformUIThread::Stop() {
-  RTC_DCHECK(thread_checker_.CalledOnValidThread());
-  // Shut down the dispatch loop and let the background thread exit.
-  if (timerid_) {
-    KillTimer(hwnd_, timerid_);
-    timerid_ = 0;
-  }
-
-  PostMessage(hwnd_, WM_CLOSE, 0, 0);
-
-  hwnd_ = NULL;
-
-  PlatformThread::Stop();
-}
-#endif
-
 // TODO(tommi): Deprecate the loop behavior in PlatformThread.
 // * Introduce a new callback type that returns void.
 // * Remove potential for a busy loop in PlatformThread.
 // * Delegate the responsibility for how to stop the thread, to the
 //   implementation that actually uses the thread.
 // All implementations will need to be aware of how the thread should be stopped
 // and encouraging a busy polling loop, can be costly in terms of power and cpu.
 void PlatformThread::Run() {
@@ -368,75 +289,16 @@ void PlatformThread::Run() {
 #else
     static const struct timespec ts_null = {0};
     nanosleep(&ts_null, nullptr);
 #endif
   } while (!AtomicOps::AcquireLoad(&stop_flag_));
 #endif  // defined(WEBRTC_WIN)
 }
 
-#if defined(WEBRTC_WIN)
-void PlatformUIThread::Run() {
-  RTC_CHECK(InternalInit()); // always evaluates
-  do {
-    // The interface contract of Start/Stop is that for a successful call to
-    // Start, there should be at least one call to the run function.  So we
-    // call the function before checking |stop_|.
-    run_function_deprecated_(obj_);
-
-    // Alertable sleep to permit RaiseFlag to run and update |stop_|.
-    if (MsgWaitForMultipleObjectsEx(0, nullptr, INFINITE, QS_ALLINPUT,
-                                    MWMO_ALERTABLE | MWMO_INPUTAVAILABLE) ==
-        WAIT_OBJECT_0) {
-      MSG msg;
-      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
-        if (msg.message == WM_QUIT) {
-          stop_ = true;
-          break;
-        }
-        TranslateMessage(&msg);
-        DispatchMessage(&msg);
-      }
-    }
-
-  } while (!stop_);
-}
-
-void PlatformUIThread::NativeEventCallback() {
-  if (!run_function_deprecated_) {
-    stop_ = true;
-    return;
-  }
-  run_function_deprecated_(obj_);
-}
-
-/* static */
-LRESULT CALLBACK
-PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-  if (uMsg == WM_DESTROY) {
-    RemovePropW(hwnd, kThisProperty);
-    PostQuitMessage(0);
-    return 0;
-  }
-
-  PlatformUIThread *twui = static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty));
-  if (!twui) {
-    return DefWindowProc(hwnd, uMsg, wParam, lParam);
-  }
-
-  if ((uMsg == static_reg_windows_msg && uMsg != WM_NULL) ||
-      (uMsg == WM_TIMER && wParam == kTimerId)) {
-    twui->NativeEventCallback();
-    return 0;
-  }
-
-  return DefWindowProc(hwnd, uMsg, wParam, lParam);
-}
-#endif
-
 bool PlatformThread::SetPriority(ThreadPriority priority) {
 #if RTC_DCHECK_IS_ON
   if (run_function_) {
     // The non-deprecated way of how this function gets called, is that it must
     // be called on the worker thread itself.
     RTC_DCHECK(!thread_checker_.CalledOnValidThread());
     RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread());
   } else {
--- a/media/webrtc/trunk/webrtc/rtc_base/platform_thread.h
+++ b/media/webrtc/trunk/webrtc/rtc_base/platform_thread.h
@@ -114,49 +114,11 @@ class PlatformThread {
   // An atomic flag that we use to stop the thread. Only modified on the
   // controlling thread and checked on the worker thread.
   volatile int stop_flag_ = 0;
   pthread_t thread_ = 0;
 #endif  // defined(WEBRTC_WIN)
   RTC_DISALLOW_COPY_AND_ASSIGN(PlatformThread);
 };
 
-#if defined(WEBRTC_WIN)
-class PlatformUIThread : public PlatformThread {
- public:
-  PlatformUIThread(ThreadRunFunctionDeprecated func, void* obj,
-		  const char* thread_name) :
-  PlatformThread(func, obj, thread_name),
-  hwnd_(nullptr),
-  timerid_(0),
-  timeout_(0) {
- }
- virtual ~PlatformUIThread() {}
-
- void Stop() override;
-
- /**
-  * Request an async callback soon.
-  */
- void RequestCallback();
-
- /**
-  * Request a recurring callback.
-  */
- bool RequestCallbackTimer(unsigned int milliseconds);
-
- protected:
-  void Run() override;
-
- private:
-  static LRESULT CALLBACK EventWindowProc(HWND, UINT, WPARAM, LPARAM);
-  void NativeEventCallback();
-  bool InternalInit();
-
-  HWND hwnd_;
-  UINT_PTR timerid_;
-  unsigned int timeout_;
-};
-#endif
-
 }  // namespace rtc
 
 #endif  // RTC_BASE_PLATFORM_THREAD_H_
--- a/testing/web-platform/meta/content-security-policy/navigate-to/child-navigates-parent-blocked.sub.html.ini
+++ b/testing/web-platform/meta/content-security-policy/navigate-to/child-navigates-parent-blocked.sub.html.ini
@@ -1,11 +1,11 @@
 [child-navigates-parent-blocked.sub.html]
   disabled:
-    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
+    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
   [Violation report status OK.]
     expected: FAIL
 
   [Test that the child can't navigate the parent because the relevant policy belongs to the navigation initiator (in this case the child)]
     expected: FAIL
 
   [Test that the child can't navigate the parent because the relevant policy belongs to the navigation initiator (in this case the child which has the policy `navigate-to 'none'`)]
     expected: FAIL
--- a/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/allowed-end-of-chain-because-of-same-origin.sub.html.ini
+++ b/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/allowed-end-of-chain-because-of-same-origin.sub.html.ini
@@ -1,4 +1,4 @@
 [allowed-end-of-chain-because-of-same-origin.sub.html]
   disabled:
-    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
+    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
 
--- a/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/allowed-end-of-chain.sub.html.ini
+++ b/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/allowed-end-of-chain.sub.html.ini
@@ -1,4 +1,4 @@
 [allowed-end-of-chain.sub.html]
   disabled:
-    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
+    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
 
--- a/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/blocked-end-of-chain.sub.html.ini
+++ b/testing/web-platform/meta/content-security-policy/navigate-to/unsafe-allow-redirects/blocked-end-of-chain.sub.html.ini
@@ -1,6 +1,6 @@
 [blocked-end-of-chain.sub.html]
   disabled:
-    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
+    if os == "android": https://bugzilla.mozilla.org/show_bug.cgi?id=1511193
   [Test that the child iframe navigation is blocked]
     expected: FAIL
 
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -34,25 +34,28 @@
   --in-content-category-background-active: rgba(12,12,13,0.15);
   --in-content-category-background-selected-hover: rgba(12,12,13,0.15);
   --in-content-category-background-selected-active: rgba(12,12,13,0.2);
   --in-content-tab-color: #424f5a;
   --in-content-link-color: #0a8dff;
   --in-content-link-color-hover: #0060df;
   --in-content-link-color-active: #003eaa;
   --in-content-link-color-visited: #0a8dff;
-  --in-content-primary-button-background: #0a84ff;
-  --in-content-primary-button-background-hover: #0060df;
-  --in-content-primary-button-background-active: #003EAA;
+  --in-content-primary-button-background: var(--blue-60);
+  --in-content-primary-button-background-hover: var(--blue-70);
+  --in-content-primary-button-background-active: var(--blue-80);
   --in-content-table-background: #ebebeb;
   --in-content-table-border-dark-color: #d1d1d1;
   --in-content-table-header-background: #0a84ff;
 
   --blue-50: #0a84ff;
   --blue-50-a30: rgba(10, 132, 255, 0.3);
+  --blue-60: #0060df;
+  --blue-70: #003eaa;
+  --blue-80: #002275;
   --grey-20: #ededf0;
   --grey-30: #d7d7db;
   --grey-60: #4a4a4f;
   --grey-90: #0c0c0d;
   --grey-90-a10: rgba(12, 12, 13, 0.1);
   --grey-90-a20: rgba(12, 12, 13, 0.2);
   --grey-90-a30: rgba(12, 12, 13, 0.3);
   --grey-90-a50: rgba(12, 12, 13, 0.5);