Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 23 Feb 2017 09:51:44 -0500
changeset 344482 2fcef8b1c4374728d95c3d72cf6e4fba76d69169
parent 344481 02a1b5cd91dbccf7ad95c2090d620501ff273f28 (current diff)
parent 344472 c02dd6a7e9c193b488271eb53e3ea039042c9ed6 (diff)
child 344483 5069348353f8fc1121e632e3208da33900627214
child 345773 3e6f3f29feeea936f222df376b0d7f706cd33a1f
push id31410
push userkwierso@gmail.com
push dateThu, 23 Feb 2017 19:05:01 +0000
treeherdermozilla-central@5069348353f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.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 m-c to graphics MozReview-Commit-ID: BMjcETZ38gw
browser/components/customizableui/test/browser_967000_button_sync.js
browser/extensions/disableSHA1rollout/README.md
browser/extensions/disableSHA1rollout/bootstrap.js
browser/extensions/disableSHA1rollout/install.rdf.in
browser/extensions/disableSHA1rollout/moz.build
dom/system/tests/marionette/manifest.ini
dom/system/tests/marionette/test_proximity_change.js
dom/system/tests/marionette/test_proximity_init.js
gfx/tests/gtest/TestTiledLayerBuffer.cpp
gfx/tests/gtest/gfxFontSelectionTest.cpp
gfx/tests/gtest/gfxFontSelectionTests.h
gfx/tests/gtest/gfxTextRunPerfTest.cpp
gfx/tests/gtest/per-word-runs.h
gfx/thebes/gfxFontTest.cpp
gfx/thebes/gfxFontTest.h
layout/base/PresShell.cpp
layout/painting/nsDisplayList.cpp
servo/components/script/origin.rs
testing/web-platform/meta/html/semantics/tabular-data/the-caption-element/caption_001.html.ini
testing/web-platform/meta/html/semantics/tabular-data/the-table-element/caption-methods.html.ini
testing/web-platform/meta/html/semantics/tabular-data/the-table-element/tHead.html.ini
testing/web-platform/meta/html/semantics/tabular-data/the-table-element/table-insertRow.html.ini
testing/web-platform/meta/referrer-policy/generic/subresource-test/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer-when-downgrade/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/no-referrer/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-only/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/strict-origin-when-cross-origin/__dir__.ini
testing/web-platform/meta/referrer-policy/strict-origin/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unsafe-url/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/http-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/http-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/http-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/http-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-csp/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-csp/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-csp/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-csp/same-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-referrer/cross-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-referrer/cross-origin/http-https/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-http/img-tag/__dir__.ini
testing/web-platform/meta/referrer-policy/unset-referrer-policy/meta-referrer/same-origin/http-https/img-tag/__dir__.ini
toolkit/components/extensions/test/mochitest/test_clipboard.html
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
tools/profiler/public/ProfileGatherer.h
widget/gtk/mozcontainer.c
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2158,22 +2158,23 @@ Accessible::RemoveChild(Accessible* aChi
   }
 
   return true;
 }
 
 void
 Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
 {
-  MOZ_ASSERT(aChild, "No child was given");
-  MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
-  MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
-  MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
+  MOZ_DIAGNOSTIC_ASSERT(aChild, "No child was given");
+  MOZ_DIAGNOSTIC_ASSERT(aChild->mParent == this, "A child from different subtree was given");
+  MOZ_DIAGNOSTIC_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
+  MOZ_DIAGNOSTIC_ASSERT(aChild->mParent->GetChildAt(aChild->mIndexInParent) == aChild, "Wrong index in parent");
+  MOZ_DIAGNOSTIC_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
              "No move, same index");
-  MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
+  MOZ_DIAGNOSTIC_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
 
   RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false);
   if (mDoc->Controller()->QueueMutationEvent(hideEvent)) {
     aChild->SetHideEventTarget(true);
   }
 
   mEmbeddedObjCollector = nullptr;
   mChildren.RemoveElementAt(aChild->mIndexInParent);
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -212,23 +212,26 @@ static int do_main(int argc, char* argv[
     // no -app flag so we use the compiled-in app data
     config.appData = &sAppData;
     config.appDataPath = kDesktopFolder;
   }
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   sandbox::BrokerServices* brokerServices =
     sandboxing::GetInitializedBrokerServices();
+  sandboxing::PermissionsService* permissionsService =
+    sandboxing::GetPermissionsService();
 #if defined(MOZ_CONTENT_SANDBOX)
   if (!brokerServices) {
     Output("Couldn't initialize the broker services.\n");
     return 255;
   }
 #endif
   config.sandboxBrokerServices = brokerServices;
+  config.sandboxPermissionsService = permissionsService;
 #endif
 
 #ifdef LIBFUZZER
   if (getenv("LIBFUZZER"))
     gBootstrap->XRE_LibFuzzerSetDriver(fuzzer::FuzzerDriver);
 #endif
 
   return gBootstrap->XRE_main(argc, argv, config);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -455,48 +455,49 @@ const gStoragePressureObserver = {
 
     const BYTES_IN_GIGABYTE = 1073741824;
     const USAGE_THRESHOLD_BYTES = BYTES_IN_GIGABYTE *
       Services.prefs.getIntPref("browser.storageManager.pressureNotification.usageThresholdGB");
     let msg = "";
     let buttons = [];
     let usage = parseInt(data);
     let prefStrBundle = document.getElementById("bundle_preferences");
+    let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
     let notificationBox = document.getElementById("high-priority-global-notificationbox");
     buttons.push({
       label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
       accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"),
       callback(notificationBar, button) {
         let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
         gBrowser.selectedTab = gBrowser.addTab(learnMoreURL);
       }
     });
     if (usage < USAGE_THRESHOLD_BYTES) {
       // The firefox-used space < 5GB, then warn user to free some disk space.
       // This is because this usage is small and not the main cause for space issue.
       // In order to avoid the bad and wrong impression among users that
       // firefox eats disk space a lot, indicate users to clean up other disk space.
-      msg = prefStrBundle.getString("spaceAlert.under5GB.description");
+      msg = prefStrBundle.getFormattedString("spaceAlert.under5GB.message", [brandShortName]);
       buttons.push({
         label: prefStrBundle.getString("spaceAlert.under5GB.okButton.label"),
         accessKey: prefStrBundle.getString("spaceAlert.under5GB.okButton.accesskey"),
         callback() {}
       });
     } else {
       // The firefox-used space >= 5GB, then guide users to about:preferences
       // to clear some data stored on firefox by websites.
-      let descriptionStringID = "spaceAlert.over5GB.description";
+      let descriptionStringID = "spaceAlert.over5GB.message";
       let prefButtonLabelStringID = "spaceAlert.over5GB.prefButton.label";
       let prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButton.accesskey";
       if (AppConstants.platform == "win") {
-        descriptionStringID = "spaceAlert.over5GB.descriptionWin";
+        descriptionStringID = "spaceAlert.over5GB.messageWin";
         prefButtonLabelStringID = "spaceAlert.over5GB.prefButtonWin.label";
         prefButtonAccesskeyStringID = "spaceAlert.over5GB.prefButtonWin.accesskey";
       }
-      msg = prefStrBundle.getString(descriptionStringID);
+      msg = prefStrBundle.getFormattedString(descriptionStringID, [brandShortName]);
       buttons.push({
         label: prefStrBundle.getString(prefButtonLabelStringID),
         accessKey: prefStrBundle.getString(prefButtonAccesskeyStringID),
         callback(notificationBar, button) {
           gBrowser.ownerGlobal.openPreferences("advanced", { advancedTab: "networkTab" });
         }
       });
     }
@@ -3430,28 +3431,28 @@ var PrintPreviewListener = {
   _tabBeforePrintPreview: null,
   _simplifyPageTab: null,
 
   getPrintPreviewBrowser() {
     if (!this._printPreviewTab) {
       let browser = gBrowser.selectedBrowser;
       let preferredRemoteType = browser.remoteType;
       this._tabBeforePrintPreview = gBrowser.selectedTab;
-      this._printPreviewTab = gBrowser.loadOneTab("about:blank", {
+      this._printPreviewTab = gBrowser.loadOneTab("about:printpreview", {
         inBackground: false,
         preferredRemoteType,
         sameProcessAsFrameLoader: browser.frameLoader
       });
       gBrowser.selectedTab = this._printPreviewTab;
     }
     return gBrowser.getBrowserForTab(this._printPreviewTab);
   },
   createSimplifiedBrowser() {
     let browser = this._tabBeforePrintPreview.linkedBrowser;
-    this._simplifyPageTab = gBrowser.loadOneTab("about:blank", {
+    this._simplifyPageTab = gBrowser.loadOneTab("about:printpreview", {
       inBackground: true,
       sameProcessAsFrameLoader: browser.frameLoader
      });
     return this.getSimplifiedSourceBrowser();
   },
   getSourceBrowser() {
     return this._tabBeforePrintPreview ?
       this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
--- a/browser/base/content/test/general/browser_audioTabIcon.js
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -12,29 +12,42 @@ function* wait_for_tab_playing_event(tab
       is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
       is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
       return true;
     }
     return false;
   });
 }
 
+function* is_audio_playing(tab) {
+  let browser = tab.linkedBrowser;
+  let isPlaying = yield ContentTask.spawn(browser, {}, function* () {
+    let audio = content.document.querySelector("audio");
+    return !audio.paused;
+  });
+  return isPlaying;
+}
+
 function* play(tab) {
   let browser = tab.linkedBrowser;
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.play();
   });
 
+  // If the tab has already be muted, it means the tab won't get soundplaying,
+  // so we don't need to check this attribute.
+  if (browser.audioMuted) {
+      return;
+  }
+
   yield wait_for_tab_playing_event(tab, true);
 }
 
 function* pause(tab, options) {
-  ok(tab.hasAttribute("soundplaying"), "The tab should have the soundplaying attribute when pause() is called");
-
   let extendedDelay = options && options.extendedDelay;
   if (extendedDelay) {
     // Use 10s to remove possibility of race condition with attr removal.
     Services.prefs.setIntPref(TABATTR_REMOVAL_PREFNAME, 10000);
   }
 
   try {
     let browser = tab.linkedBrowser;
@@ -42,16 +55,22 @@ function* pause(tab, options) {
       BrowserTestUtils.waitForEvent(browser, "DOMAudioPlaybackStopped", "DOMAudioPlaybackStopped event should get fired after pause");
     let awaitTabPausedAttrModified =
       wait_for_tab_playing_event(tab, false);
     yield ContentTask.spawn(browser, {}, function* () {
       let audio = content.document.querySelector("audio");
       audio.pause();
     });
 
+    // If the tab has already be muted, it means the tab won't have soundplaying,
+    // so we don't need to check this attribute.
+    if (browser.audioMuted) {
+      return;
+    }
+
     if (extendedDelay) {
       ok(tab.hasAttribute("soundplaying"), "The tab should still have the soundplaying attribute immediately after pausing");
 
       yield awaitDOMAudioPlaybackStopped;
       ok(tab.hasAttribute("soundplaying"), "The tab should still have the soundplaying attribute immediately after DOMAudioPlaybackStopped");
     }
 
     yield awaitTabPausedAttrModified;
@@ -136,16 +155,23 @@ function* test_mute_tab(tab, icon, expec
   let tooltip = document.getElementById("tabbrowser-tab-tooltip");
 
   yield hover_icon(icon, tooltip);
   EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
   leave_icon(icon);
 
   is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
 
+  // If the audio is playing, we should check whether clicking on icon affects
+  // the media element's playing state.
+  let isAudioPlaying = yield is_audio_playing(tab);
+  if (isAudioPlaying) {
+    yield wait_for_tab_playing_event(tab, !expectMuted);
+  }
+
   return mutedPromise;
 }
 
 function get_tab_state(tab) {
   const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
   return JSON.parse(ss.getTabState(tab));
 }
 
@@ -164,17 +190,17 @@ function* test_muting_using_menu(tab, ex
   is(toggleMute.accessKey, expectedAccessKey, "Correct accessKey expected");
 
   is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute");
   ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute");
 
   yield play(tab);
 
   is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute");
-  ok(toggleMute.hasAttribute("soundplaying"), "Should have the soundplaying attribute");
+  is(!toggleMute.hasAttribute("soundplaying"), expectMuted, "The value of soundplaying attribute is incorrect");
 
   yield pause(tab);
 
   is(toggleMute.hasAttribute("muted"), expectMuted, "Should have the correct state for the muted attribute");
   ok(!toggleMute.hasAttribute("soundplaying"), "Should not have the soundplaying attribute");
 
   // Click on the menu and wait for the tab to be muted.
   let mutedPromise = get_wait_for_mute_promise(tab, !expectMuted);
@@ -229,32 +255,32 @@ function* test_playing_icon_on_tab(tab, 
   // Make sure it's possible to mute using the context menu.
   yield test_muting_using_menu(tab, false);
 
   // Make sure it's possible to unmute using the context menu.
   yield test_muting_using_menu(tab, true);
 }
 
 function* test_swapped_browser_while_playing(oldTab, newBrowser) {
+  // The tab was muted so it won't have soundplaying attribute even it's playing.
   ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab");
   is(oldTab.muteReason, null, "Expected the correct muteReason attribute on the old tab");
-  ok(oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab");
+  ok(!oldTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the old tab");
 
   let newTab = gBrowser.getTabForBrowser(newBrowser);
   let AttrChangePromise = BrowserTestUtils.waitForEvent(newTab, "TabAttrModified", false, event => {
-    return event.detail.changed.includes("soundplaying") &&
-           event.detail.changed.includes("muted");
+    return event.detail.changed.includes("muted");
   });
 
   gBrowser.swapBrowsersAndCloseOther(newTab, oldTab);
   yield AttrChangePromise;
 
   ok(newTab.hasAttribute("muted"), "Expected the correct muted attribute on the new tab");
   is(newTab.muteReason, null, "Expected the correct muteReason property on the new tab");
-  ok(newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab");
+  ok(!newTab.hasAttribute("soundplaying"), "Expected the correct soundplaying attribute on the new tab");
 
   let icon = document.getAnonymousElementByAttribute(newTab, "anonid",
                                                      "soundplaying-icon");
   yield test_tooltip(icon, "Unmute tab", true);
 }
 
 function* test_swapped_browser_while_not_playing(oldTab, newBrowser) {
   ok(oldTab.hasAttribute("muted"), "Expected the correct muted attribute on the old tab");
--- a/browser/base/content/test/referrer/head.js
+++ b/browser/base/content/test/referrer/head.js
@@ -137,17 +137,17 @@ function delayedStartupFinished(aWindow)
  * Waits for some (any) tab to load. The caller triggers the load.
  * @param aWindow The window where to wait for a tab to load.
  * @return {Promise}
  * @resolves With the tab once it's loaded.
  */
 function someTabLoaded(aWindow) {
   return new Promise(function(resolve) {
     aWindow.gBrowser.addEventListener("load", function onLoad(aEvent) {
-      if (aWindow.location === "about:blank") {
+      if (aWindow.location.href === "about:blank") {
         return;
       }
       let tab = aWindow.gBrowser._getTabForContentWindow(
           aEvent.target.defaultView.top);
       if (tab) {
         aWindow.gBrowser.removeEventListener("load", onLoad, true);
         resolve(tab);
       }
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -304,16 +304,18 @@ const CustomizableWidgets = [
     viewId: "PanelUI-remotetabs",
     defaultArea: CustomizableUI.AREA_PANEL,
     deckIndices: {
       DECKINDEX_TABS: 0,
       DECKINDEX_TABSDISABLED: 1,
       DECKINDEX_FETCHING: 2,
       DECKINDEX_NOCLIENTS: 3,
     },
+    TABS_PER_PAGE: 25,
+    NEXT_PAGE_MIN_TABS: 5, // Minimum number of tabs displayed when we click "Show All"
     onCreated(aNode) {
       // Add an observer to the button so we get the animation during sync.
       // (Note the observer sets many attributes, including label and
       // tooltiptext, but we only want the 'syncstatus' attribute for the
       // animation)
       let doc = aNode.ownerDocument;
       let obnode = doc.createElementNS(kNSXUL, "observes");
       obnode.setAttribute("element", "sync-status");
@@ -396,23 +398,23 @@ const CustomizableWidgets = [
       // We call setAttribute instead of relying on the XBL property setter due
       // to things going wrong when we try and set the index before the XBL
       // binding has been created - see bug 1241851 for the gory details.
       deck.setAttribute("selectedIndex", index);
     },
 
     _showTabsPromise: Promise.resolve(),
     // Update the tab list after any existing in-flight updates are complete.
-    _showTabs() {
+    _showTabs(paginationInfo) {
       this._showTabsPromise = this._showTabsPromise.then(() => {
-        return this.__showTabs();
+        return this.__showTabs(paginationInfo);
       });
     },
     // Return a new promise to update the tab list.
-    __showTabs() {
+    __showTabs(paginationInfo) {
       let doc = this._tabsList.ownerDocument;
       return SyncedTabs.getTabClients().then(clients => {
         // The view may have been hidden while the promise was resolving.
         if (!this._tabsList) {
           return;
         }
         if (clients.length === 0 && !SyncedTabs.hasSyncedThisSession) {
           // the "fetching tabs" deck is being shown - let's leave it there.
@@ -422,26 +424,30 @@ const CustomizableWidgets = [
 
         if (clients.length === 0) {
           this.setDeckIndex(this.deckIndices.DECKINDEX_NOCLIENTS);
           return;
         }
 
         this.setDeckIndex(this.deckIndices.DECKINDEX_TABS);
         this._clearTabList();
-        SyncedTabs.sortTabClientsByLastUsed(clients, 50 /* maxTabs */);
+        SyncedTabs.sortTabClientsByLastUsed(clients);
         let fragment = doc.createDocumentFragment();
 
         for (let client of clients) {
           // add a menu separator for all clients other than the first.
           if (fragment.lastChild) {
             let separator = doc.createElementNS(kNSXUL, "menuseparator");
             fragment.appendChild(separator);
           }
-          this._appendClient(client, fragment);
+          if (paginationInfo && paginationInfo.clientId == client.id) {
+            this._appendClient(client, fragment, paginationInfo.maxTabs);
+          } else {
+            this._appendClient(client, fragment);
+          }
         }
         this._tabsList.appendChild(fragment);
       }).catch(err => {
         Cu.reportError(err);
       }).then(() => {
         // an observer for tests.
         Services.obs.notifyObservers(null, "synced-tabs-menu:test:tabs-updated", null);
       });
@@ -461,36 +467,56 @@ const CustomizableWidgets = [
       }
       let message = this._tabsList.getAttribute(messageAttr);
       let doc = this._tabsList.ownerDocument;
       let messageLabel = doc.createElementNS(kNSXUL, "label");
       messageLabel.textContent = message;
       appendTo.appendChild(messageLabel);
       return messageLabel;
     },
-    _appendClient(client, attachFragment) {
+    _appendClient(client, attachFragment, maxTabs = this.TABS_PER_PAGE) {
       let doc = attachFragment.ownerDocument;
       // Create the element for the remote client.
       let clientItem = doc.createElementNS(kNSXUL, "label");
       clientItem.setAttribute("itemtype", "client");
       let window = doc.defaultView;
       clientItem.setAttribute("tooltiptext",
         window.gSyncUI.formatLastSyncDate(new Date(client.lastModified)));
       clientItem.textContent = client.name;
 
       attachFragment.appendChild(clientItem);
 
       if (client.tabs.length == 0) {
         let label = this._appendMessageLabel("notabsforclientlabel", attachFragment);
         label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
       } else {
+        // If this page will display all tabs, show no additional buttons.
+        // If the next page will display all the remaining tabs, show a "Show All" button
+        // Otherwise, show a "Shore More" button
+        let hasNextPage = client.tabs.length > maxTabs;
+        let nextPageIsLastPage = hasNextPage && maxTabs + this.TABS_PER_PAGE >= client.tabs.length;
+        if (nextPageIsLastPage) {
+          // When the user clicks "Show All", try to have at least NEXT_PAGE_MIN_TABS more tabs
+          // to display in order to avoid user frustration
+          maxTabs = Math.min(client.tabs.length - this.NEXT_PAGE_MIN_TABS, maxTabs);
+        }
+        if (hasNextPage) {
+          client.tabs = client.tabs.slice(0, maxTabs);
+        }
         for (let tab of client.tabs) {
           let tabEnt = this._createTabElement(doc, tab);
           attachFragment.appendChild(tabEnt);
         }
+        if (hasNextPage) {
+          let showAllEnt = this._createShowMoreElement(doc, client.id,
+                                                       nextPageIsLastPage ?
+                                                       Infinity :
+                                                       maxTabs + this.TABS_PER_PAGE);
+          attachFragment.appendChild(showAllEnt);
+        }
       }
     },
     _createTabElement(doc, tabInfo) {
       let item = doc.createElementNS(kNSXUL, "toolbarbutton");
       let tooltipText = (tabInfo.title ? tabInfo.title + "\n" : "") + tabInfo.url;
       item.setAttribute("itemtype", "tab");
       item.setAttribute("class", "subviewbutton");
       item.setAttribute("targetURI", tabInfo.url);
@@ -501,16 +527,39 @@ const CustomizableWidgets = [
       // respects different buttons (eg, to open in a new tab).
       item.addEventListener("click", e => {
         doc.defaultView.openUILink(tabInfo.url, e);
         CustomizableUI.hidePanelForNode(item);
         BrowserUITelemetry.countSyncedTabEvent("open", "toolbarbutton-subview");
       });
       return item;
     },
+    _createShowMoreElement(doc, clientId, showCount) {
+      let labelAttr, tooltipAttr;
+      if (showCount === Infinity) {
+        labelAttr = "showAllLabel";
+        tooltipAttr = "showAllTooltipText";
+      } else {
+        labelAttr = "showMoreLabel";
+        tooltipAttr = "showMoreTooltipText";
+      }
+      let showAllItem = doc.createElementNS(kNSXUL, "toolbarbutton");
+      showAllItem.setAttribute("itemtype", "showmorebutton");
+      showAllItem.setAttribute("class", "subviewbutton");
+      let label = this._tabsList.getAttribute(labelAttr);
+      showAllItem.setAttribute("label", label);
+      let tooltipText = this._tabsList.getAttribute(tooltipAttr);
+      showAllItem.setAttribute("tooltiptext", tooltipText);
+      showAllItem.addEventListener("click", e => {
+        e.preventDefault();
+        e.stopPropagation();
+        this._showTabs({ clientId, maxTabs: showCount });
+      });
+      return showAllItem;
+    }
   }, {
     id: "privatebrowsing-button",
     shortcutId: "key_privatebrowsing",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand(e) {
       let win = e.target.ownerGlobal;
       win.OpenBrowserWindow({private: true});
     }
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -120,16 +120,20 @@
                            oncommand="gSyncUI.doSync();"
                            closemenu="none"/>
             <menuseparator id="PanelUI-remotetabs-separator"/>
           </vbox>
           <deck id="PanelUI-remotetabs-deck">
             <!-- Sync is ready to Sync and the "tabs" engine is enabled -->
             <vbox id="PanelUI-remotetabs-tabspane">
               <vbox id="PanelUI-remotetabs-tabslist"
+                    showAllLabel="&appMenuRemoteTabs.showAll.label;"
+                    showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;"
+                    showMoreLabel="&appMenuRemoteTabs.showMore.label;"
+                    showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
                     notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
                     />
             </vbox>
             <!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
             <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
               <vbox class="PanelUI-remotetabs-instruction-box">
                 <hbox pack="center">
                   <html:img class="fxaSyncIllustration" src="chrome://browser/skin/fxa/sync-illustration.svg"/>
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -95,17 +95,16 @@ skip-if = os == "linux" # Intermittent f
 [browser_948985_non_removable_defaultArea.js]
 [browser_952963_areaType_getter_no_area.js]
 [browser_956602_remove_special_widget.js]
 [browser_962069_drag_to_overflow_chevron.js]
 [browser_962884_opt_in_disable_hyphens.js]
 [browser_963639_customizing_attribute_non_customizable_toolbar.js]
 [browser_967000_button_charEncoding.js]
 [browser_967000_button_feeds.js]
-[browser_967000_button_sync.js]
 [browser_968447_bookmarks_toolbar_items_in_panel.js]
 skip-if = os == "linux" # Intemittent failures - bug 979207
 [browser_968565_insert_before_hidden_items.js]
 [browser_969427_recreate_destroyed_widget_after_reset.js]
 [browser_969661_character_encoding_navbar_disabled.js]
 [browser_970511_undo_restore_default.js]
 [browser_972267_customizationchange_events.js]
 [browser_973641_button_addon.js]
@@ -146,9 +145,10 @@ skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
 [browser_panel_toggle.js]
 [browser_switch_to_customize_mode.js]
+[browser_synced_tabs_menu.js]
 [browser_check_tooltips_in_navbar.js]
deleted file mode 100644
--- a/browser/components/customizableui/test/browser_967000_button_sync.js
+++ /dev/null
@@ -1,337 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-requestLongerTimeout(2);
-
-let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
-
-XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
-
-// These are available on the widget implementation, but it seems impossible
-// to grab that impl at runtime.
-const DECKINDEX_TABS = 0;
-const DECKINDEX_TABSDISABLED = 1;
-const DECKINDEX_FETCHING = 2;
-const DECKINDEX_NOCLIENTS = 3;
-
-var initialLocation = gBrowser.currentURI.spec;
-var newTab = null;
-
-// A helper to notify there are new tabs. Returns a promise that is resolved
-// once the UI has been updated.
-function updateTabsPanel() {
-  let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
-  Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, null);
-  return promiseTabsUpdated;
-}
-
-// This is the mock we use for SyncedTabs.jsm - tests may override various
-// functions.
-let mockedInternal = {
-  get isConfiguredToSyncTabs() { return true; },
-  getTabClients() { return []; },
-  syncTabs() {},
-  hasSyncedThisSession: false,
-};
-
-
-add_task(function* setup() {
-  let oldInternal = SyncedTabs._internal;
-  SyncedTabs._internal = mockedInternal;
-
-  // This test hacks some observer states to simulate a user being signed
-  // in to Sync - restore them when the test completes.
-  let initialObserverStates = {};
-  for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) {
-    initialObserverStates[id] = document.getElementById(id).hidden;
-  }
-
-  registerCleanupFunction(() => {
-    SyncedTabs._internal = oldInternal;
-    for (let [id, initial] of Object.entries(initialObserverStates)) {
-      document.getElementById(id).hidden = initial;
-    }
-  });
-});
-
-// The test expects the about:preferences#sync page to open in the current tab
-function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
-  info("Check Sync button functionality");
-  Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
-
-  // check the button's functionality
-  yield PanelUI.show();
-
-  if (entryPoint == "uitour") {
-    UITour.tourBrowsersByWindow.set(window, new Set());
-    UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser);
-  }
-
-  let syncButton = document.getElementById("sync-button");
-  ok(syncButton, "The Sync button was added to the Panel Menu");
-
-  syncButton.click();
-  let syncPanel = document.getElementById("PanelUI-remotetabs");
-  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
-
-  // Sync is not configured - verify that state is reflected.
-  let subpanel = document.getElementById(expectedPanelId)
-  ok(!subpanel.hidden, "sync setup element is visible");
-
-  // Find and click the "setup" button.
-  let setupButton = subpanel.querySelector(".PanelUI-remotetabs-prefs-button");
-  setupButton.click();
-
-  let deferred = Promise.defer();
-  let handler = (e) => {
-    if (e.originalTarget != gBrowser.selectedBrowser.contentDocument ||
-        e.target.location.href == "about:blank") {
-      info("Skipping spurious 'load' event for " + e.target.location.href);
-      return;
-    }
-    gBrowser.selectedBrowser.removeEventListener("load", handler, true);
-    deferred.resolve();
-  }
-  gBrowser.selectedBrowser.addEventListener("load", handler, true);
-
-  yield deferred.promise;
-  newTab = gBrowser.selectedTab;
-
-  is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync",
-    "Firefox Sync preference page opened with `menupanel` entrypoint");
-  ok(!isPanelUIOpen(), "The panel closed");
-
-  if (isPanelUIOpen()) {
-    let panelHidePromise = promisePanelHidden(window);
-    PanelUI.hide();
-    yield panelHidePromise;
-  }
-}
-
-function* asyncCleanup() {
-  Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
-  // reset the panel UI to the default state
-  yield resetCustomization();
-  ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
-
-  // restore the tabs
-  gBrowser.addTab(initialLocation);
-  gBrowser.removeTab(newTab);
-  UITour.tourBrowsersByWindow.delete(window);
-}
-
-// When Sync is not setup.
-add_task(() => openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs"));
-add_task(asyncCleanup);
-
-// When Sync is configured in a "needs reauthentication" state.
-add_task(function* () {
-  // configure our broadcasters so we are in the right state.
-  document.getElementById("sync-reauth-state").hidden = false;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = true;
-  yield openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs")
-});
-
-// Test the mobile promo links
-add_task(function* () {
-  // change the preferences for the mobile links.
-  Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail=");
-  Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail=");
-
-  mockedInternal.getTabClients = () => [];
-  mockedInternal.syncTabs = () => Promise.resolve();
-
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = false;
-
-  let syncPanel = document.getElementById("PanelUI-remotetabs");
-  let links = syncPanel.querySelectorAll(".remotetabs-promo-link");
-
-  is(links.length, 2, "found 2 links as expected");
-
-  // test each link and left and middle mouse buttons
-  for (let link of links) {
-    for (let button = 0; button < 2; button++) {
-      yield PanelUI.show();
-      EventUtils.sendMouseEvent({ type: "click", button }, link, window);
-      // the panel should have been closed.
-      ok(!isPanelUIOpen(), "click closed the panel");
-      // should be a new tab - wait for the load.
-      is(gBrowser.tabs.length, 2, "there's a new tab");
-      yield new Promise(resolve => {
-        if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") {
-          gBrowser.selectedBrowser.addEventListener("load", function(e) {
-            resolve();
-          }, {capture: true, once: true});
-          return;
-        }
-        // the new tab has already transitioned away from about:blank so we
-        // are good to go.
-        resolve();
-      });
-
-      let os = link.getAttribute("mobile-promo-os");
-      let expectedUrl = `http://example.com/?os=${os}&tail=synced-tabs`;
-      is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL");
-      gBrowser.removeTab(gBrowser.selectedTab);
-    }
-  }
-
-  // test each link and right mouse button - should be a noop.
-  yield PanelUI.show();
-  for (let link of links) {
-    EventUtils.sendMouseEvent({ type: "click", button: 2 }, link, window);
-    // the panel should still be open
-    ok(isPanelUIOpen(), "panel remains open after right-click");
-    is(gBrowser.tabs.length, 1, "no new tab was opened");
-  }
-  PanelUI.hide();
-
-  Services.prefs.clearUserPref("identity.mobilepromo.android");
-  Services.prefs.clearUserPref("identity.mobilepromo.ios");
-});
-
-// Test the "Sync Now" button
-add_task(function* () {
-  mockedInternal.getTabClients = () => [];
-  mockedInternal.syncTabs = () => {
-    return Promise.resolve();
-  }
-
-  // configure our broadcasters so we are in the right state.
-  document.getElementById("sync-reauth-state").hidden = true;
-  document.getElementById("sync-setup-state").hidden = true;
-  document.getElementById("sync-syncnow-state").hidden = false;
-
-  yield PanelUI.show();
-  document.getElementById("sync-button").click();
-  let syncPanel = document.getElementById("PanelUI-remotetabs");
-  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
-
-  let subpanel = document.getElementById("PanelUI-remotetabs-main")
-  ok(!subpanel.hidden, "main pane is visible");
-  let deck = document.getElementById("PanelUI-remotetabs-deck");
-
-  // The widget is still fetching tabs, as we've neutered everything that
-  // provides them
-  is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible");
-
-  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
-
-  let didSync = false;
-  let oldDoSync = gSyncUI.doSync;
-  gSyncUI.doSync = function() {
-    didSync = true;
-    mockedInternal.hasSyncedThisSession = true;
-    gSyncUI.doSync = oldDoSync;
-  }
-  syncNowButton.click();
-  ok(didSync, "clicking the button called the correct function");
-
-  // Tell the widget there are tabs available, but with zero clients.
-  mockedInternal.getTabClients = () => {
-    return Promise.resolve([]);
-  }
-  yield updateTabsPanel();
-  // The UI should be showing the "no clients" pane.
-  is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible");
-
-  // Tell the widget there are tabs available - we have 3 clients, one with no
-  // tabs.
-  mockedInternal.getTabClients = () => {
-    return Promise.resolve([
-      {
-        id: "guid_mobile",
-        type: "client",
-        name: "My Phone",
-        tabs: [],
-      },
-      {
-        id: "guid_desktop",
-        type: "client",
-        name: "My Desktop",
-        tabs: [
-          {
-            title: "http://example.com/10",
-            lastUsed: 10, // the most recent
-          },
-          {
-            title: "http://example.com/1",
-            lastUsed: 1, // the least recent.
-          },
-          {
-            title: "http://example.com/5",
-            lastUsed: 5,
-          },
-        ],
-      },
-      {
-        id: "guid_second_desktop",
-        name: "My Other Desktop",
-        tabs: [
-          {
-            title: "http://example.com/6",
-            lastUsed: 6,
-          }
-        ],
-      },
-    ]);
-  };
-  yield updateTabsPanel();
-
-  // The UI should be showing tabs!
-  is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible");
-  let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
-  let node = tabList.firstChild;
-  // First entry should be the client with the most-recent tab.
-  is(node.getAttribute("itemtype"), "client", "node is a client entry");
-  is(node.textContent, "My Desktop", "correct client");
-  // Next entry is the most-recent tab
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "tab", "node is a tab");
-  is(node.getAttribute("label"), "http://example.com/10");
-
-  // Next entry is the next-most-recent tab
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "tab", "node is a tab");
-  is(node.getAttribute("label"), "http://example.com/5");
-
-  // Next entry is the least-recent tab from the first client.
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "tab", "node is a tab");
-  is(node.getAttribute("label"), "http://example.com/1");
-
-  // Next is a menuseparator between the clients.
-  node = node.nextSibling;
-  is(node.nodeName, "menuseparator");
-
-  // Next is the client with 1 tab.
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "client", "node is a client entry");
-  is(node.textContent, "My Other Desktop", "correct client");
-  // Its single tab
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "tab", "node is a tab");
-  is(node.getAttribute("label"), "http://example.com/6");
-
-  // Next is a menuseparator between the clients.
-  node = node.nextSibling;
-  is(node.nodeName, "menuseparator");
-
-  // Next is the client with no tab.
-  node = node.nextSibling;
-  is(node.getAttribute("itemtype"), "client", "node is a client entry");
-  is(node.textContent, "My Phone", "correct client");
-  // There is a single node saying there's no tabs for the client.
-  node = node.nextSibling;
-  is(node.nodeName, "label", "node is a label");
-  is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client");
-
-  node = node.nextSibling;
-  is(node, null, "no more entries");
-});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_synced_tabs_menu.js
@@ -0,0 +1,427 @@
+/* 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";
+
+requestLongerTimeout(2);
+
+let {SyncedTabs} = Cu.import("resource://services-sync/SyncedTabs.jsm", {});
+
+XPCOMUtils.defineLazyModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
+
+// These are available on the widget implementation, but it seems impossible
+// to grab that impl at runtime.
+const DECKINDEX_TABS = 0;
+const DECKINDEX_TABSDISABLED = 1;
+const DECKINDEX_FETCHING = 2;
+const DECKINDEX_NOCLIENTS = 3;
+
+var initialLocation = gBrowser.currentURI.spec;
+var newTab = null;
+
+// A helper to notify there are new tabs. Returns a promise that is resolved
+// once the UI has been updated.
+function updateTabsPanel() {
+  let promiseTabsUpdated = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+  Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, null);
+  return promiseTabsUpdated;
+}
+
+// This is the mock we use for SyncedTabs.jsm - tests may override various
+// functions.
+let mockedInternal = {
+  get isConfiguredToSyncTabs() { return true; },
+  getTabClients() { return Promise.resolve([]); },
+  syncTabs() { return Promise.resolve(); },
+  hasSyncedThisSession: false,
+};
+
+
+add_task(function* setup() {
+  let oldInternal = SyncedTabs._internal;
+  SyncedTabs._internal = mockedInternal;
+
+  // This test hacks some observer states to simulate a user being signed
+  // in to Sync - restore them when the test completes.
+  let initialObserverStates = {};
+  for (let id of ["sync-reauth-state", "sync-setup-state", "sync-syncnow-state"]) {
+    initialObserverStates[id] = document.getElementById(id).hidden;
+  }
+
+  registerCleanupFunction(() => {
+    SyncedTabs._internal = oldInternal;
+    for (let [id, initial] of Object.entries(initialObserverStates)) {
+      document.getElementById(id).hidden = initial;
+    }
+  });
+});
+
+// The test expects the about:preferences#sync page to open in the current tab
+function* openPrefsFromMenuPanel(expectedPanelId, entryPoint) {
+  info("Check Sync button functionality");
+  Services.prefs.setCharPref("identity.fxaccounts.remote.signup.uri", "http://example.com/");
+
+  // check the button's functionality
+  yield PanelUI.show();
+
+  if (entryPoint == "uitour") {
+    UITour.tourBrowsersByWindow.set(window, new Set());
+    UITour.tourBrowsersByWindow.get(window).add(gBrowser.selectedBrowser);
+  }
+
+  let syncButton = document.getElementById("sync-button");
+  ok(syncButton, "The Sync button was added to the Panel Menu");
+
+  let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+  syncButton.click();
+  yield tabsUpdatedPromise;
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+
+  // Sync is not configured - verify that state is reflected.
+  let subpanel = document.getElementById(expectedPanelId)
+  ok(!subpanel.hidden, "sync setup element is visible");
+
+  // Find and click the "setup" button.
+  let setupButton = subpanel.querySelector(".PanelUI-remotetabs-prefs-button");
+  setupButton.click();
+
+  let deferred = Promise.defer();
+  let handler = (e) => {
+    if (e.originalTarget != gBrowser.selectedBrowser.contentDocument ||
+        e.target.location.href == "about:blank") {
+      info("Skipping spurious 'load' event for " + e.target.location.href);
+      return;
+    }
+    gBrowser.selectedBrowser.removeEventListener("load", handler, true);
+    deferred.resolve();
+  }
+  gBrowser.selectedBrowser.addEventListener("load", handler, true);
+
+  yield deferred.promise;
+  newTab = gBrowser.selectedTab;
+
+  is(gBrowser.currentURI.spec, "about:preferences?entrypoint=" + entryPoint + "#sync",
+    "Firefox Sync preference page opened with `menupanel` entrypoint");
+  ok(!isPanelUIOpen(), "The panel closed");
+
+  if (isPanelUIOpen()) {
+    yield panelUIHide();
+  }
+}
+
+function panelUIHide() {
+  let panelHidePromise = promisePanelHidden(window);
+  PanelUI.hide();
+  return panelHidePromise;
+}
+
+function* asyncCleanup() {
+  Services.prefs.clearUserPref("identity.fxaccounts.remote.signup.uri");
+  // reset the panel UI to the default state
+  yield resetCustomization();
+  ok(CustomizableUI.inDefaultState, "The panel UI is in default state again.");
+
+  // restore the tabs
+  gBrowser.addTab(initialLocation);
+  gBrowser.removeTab(newTab);
+  UITour.tourBrowsersByWindow.delete(window);
+}
+
+// When Sync is not setup.
+add_task(function* () {
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = false;
+  document.getElementById("sync-syncnow-state").hidden = true;
+  yield openPrefsFromMenuPanel("PanelUI-remotetabs-setupsync", "synced-tabs")
+});
+add_task(asyncCleanup);
+
+// When Sync is configured in a "needs reauthentication" state.
+add_task(function* () {
+  // configure our broadcasters so we are in the right state.
+  document.getElementById("sync-reauth-state").hidden = false;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = true;
+  yield openPrefsFromMenuPanel("PanelUI-remotetabs-reauthsync", "synced-tabs")
+});
+
+// Test the mobile promo links
+add_task(function* () {
+  // change the preferences for the mobile links.
+  Services.prefs.setCharPref("identity.mobilepromo.android", "http://example.com/?os=android&tail=");
+  Services.prefs.setCharPref("identity.mobilepromo.ios", "http://example.com/?os=ios&tail=");
+
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = false;
+
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  let links = syncPanel.querySelectorAll(".remotetabs-promo-link");
+
+  is(links.length, 2, "found 2 links as expected");
+
+  // test each link and left and middle mouse buttons
+  for (let link of links) {
+    for (let button = 0; button < 2; button++) {
+      yield PanelUI.show();
+      EventUtils.sendMouseEvent({ type: "click", button }, link, window);
+      // the panel should have been closed.
+      ok(!isPanelUIOpen(), "click closed the panel");
+      // should be a new tab - wait for the load.
+      is(gBrowser.tabs.length, 2, "there's a new tab");
+      yield new Promise(resolve => {
+        if (gBrowser.selectedBrowser.currentURI.spec == "about:blank") {
+          gBrowser.selectedBrowser.addEventListener("load", function(e) {
+            resolve();
+          }, {capture: true, once: true});
+          return;
+        }
+        // the new tab has already transitioned away from about:blank so we
+        // are good to go.
+        resolve();
+      });
+
+      let os = link.getAttribute("mobile-promo-os");
+      let expectedUrl = `http://example.com/?os=${os}&tail=synced-tabs`;
+      is(gBrowser.selectedBrowser.currentURI.spec, expectedUrl, "correct URL");
+      gBrowser.removeTab(gBrowser.selectedTab);
+    }
+  }
+
+  // test each link and right mouse button - should be a noop.
+  yield PanelUI.show();
+  for (let link of links) {
+    EventUtils.sendMouseEvent({ type: "click", button: 2 }, link, window);
+    // the panel should still be open
+    ok(isPanelUIOpen(), "panel remains open after right-click");
+    is(gBrowser.tabs.length, 1, "no new tab was opened");
+  }
+  yield panelUIHide();
+
+  Services.prefs.clearUserPref("identity.mobilepromo.android");
+  Services.prefs.clearUserPref("identity.mobilepromo.ios");
+});
+
+// Test the "Sync Now" button
+add_task(function* () {
+  // configure our broadcasters so we are in the right state.
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = false;
+
+  yield PanelUI.show();
+  let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+  document.getElementById("sync-button").click();
+  yield tabsUpdatedPromise;
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+
+  let subpanel = document.getElementById("PanelUI-remotetabs-main")
+  ok(!subpanel.hidden, "main pane is visible");
+  let deck = document.getElementById("PanelUI-remotetabs-deck");
+
+  // The widget is still fetching tabs, as we've neutered everything that
+  // provides them
+  is(deck.selectedIndex, DECKINDEX_FETCHING, "first deck entry is visible");
+
+  let syncNowButton = document.getElementById("PanelUI-remotetabs-syncnow");
+
+  let didSync = false;
+  let oldDoSync = gSyncUI.doSync;
+  gSyncUI.doSync = function() {
+    didSync = true;
+    mockedInternal.hasSyncedThisSession = true;
+    gSyncUI.doSync = oldDoSync;
+  }
+  syncNowButton.click();
+  ok(didSync, "clicking the button called the correct function");
+
+  // Tell the widget there are tabs available, but with zero clients.
+  mockedInternal.getTabClients = () => {
+    return Promise.resolve([]);
+  }
+  yield updateTabsPanel();
+  // The UI should be showing the "no clients" pane.
+  is(deck.selectedIndex, DECKINDEX_NOCLIENTS, "no-clients deck entry is visible");
+
+  // Tell the widget there are tabs available - we have 3 clients, one with no
+  // tabs.
+  mockedInternal.getTabClients = () => {
+    return Promise.resolve([
+      {
+        id: "guid_mobile",
+        type: "client",
+        name: "My Phone",
+        tabs: [],
+      },
+      {
+        id: "guid_desktop",
+        type: "client",
+        name: "My Desktop",
+        tabs: [
+          {
+            title: "http://example.com/10",
+            lastUsed: 10, // the most recent
+          },
+          {
+            title: "http://example.com/1",
+            lastUsed: 1, // the least recent.
+          },
+          {
+            title: "http://example.com/5",
+            lastUsed: 5,
+          },
+        ],
+      },
+      {
+        id: "guid_second_desktop",
+        name: "My Other Desktop",
+        tabs: [
+          {
+            title: "http://example.com/6",
+            lastUsed: 6,
+          }
+        ],
+      },
+    ]);
+  };
+  yield updateTabsPanel();
+
+  // The UI should be showing tabs!
+  is(deck.selectedIndex, DECKINDEX_TABS, "no-clients deck entry is visible");
+  let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
+  let node = tabList.firstChild;
+  // First entry should be the client with the most-recent tab.
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Desktop", "correct client");
+  // Next entry is the most-recent tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/10");
+
+  // Next entry is the next-most-recent tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/5");
+
+  // Next entry is the least-recent tab from the first client.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/1");
+
+  // Next is a menuseparator between the clients.
+  node = node.nextSibling;
+  is(node.nodeName, "menuseparator");
+
+  // Next is the client with 1 tab.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Other Desktop", "correct client");
+  // Its single tab
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "tab", "node is a tab");
+  is(node.getAttribute("label"), "http://example.com/6");
+
+  // Next is a menuseparator between the clients.
+  node = node.nextSibling;
+  is(node.nodeName, "menuseparator");
+
+  // Next is the client with no tab.
+  node = node.nextSibling;
+  is(node.getAttribute("itemtype"), "client", "node is a client entry");
+  is(node.textContent, "My Phone", "correct client");
+  // There is a single node saying there's no tabs for the client.
+  node = node.nextSibling;
+  is(node.nodeName, "label", "node is a label");
+  is(node.getAttribute("itemtype"), "", "node is neither a tab nor a client");
+
+  node = node.nextSibling;
+  is(node, null, "no more entries");
+
+  yield panelUIHide();
+});
+
+// Test the pagination capabilities (Show More/All tabs)
+add_task(function* () {
+  mockedInternal.getTabClients = () => {
+    return Promise.resolve([
+      {
+        id: "guid_desktop",
+        type: "client",
+        name: "My Desktop",
+        tabs: function() {
+          let allTabsDesktop = [];
+          // We choose 77 tabs, because TABS_PER_PAGE is 25, which means
+          // on the second to last page we should have 22 items shown
+          // (because we have to show at least NEXT_PAGE_MIN_TABS=5 tabs on the last page)
+          for (let i = 1; i <= 77; i++) {
+            allTabsDesktop.push({ title: "Tab #" + i });
+          }
+          return allTabsDesktop;
+        }(),
+      }
+    ]);
+  };
+
+  // configure our broadcasters so we are in the right state.
+  document.getElementById("sync-reauth-state").hidden = true;
+  document.getElementById("sync-setup-state").hidden = true;
+  document.getElementById("sync-syncnow-state").hidden = false;
+
+  yield PanelUI.show();
+  let tabsUpdatedPromise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+  document.getElementById("sync-button").click();
+  yield tabsUpdatedPromise;
+
+  // Check pre-conditions
+  let syncPanel = document.getElementById("PanelUI-remotetabs");
+  ok(syncPanel.getAttribute("current"), "Sync Panel is in view");
+  let subpanel = document.getElementById("PanelUI-remotetabs-main")
+  ok(!subpanel.hidden, "main pane is visible");
+  let deck = document.getElementById("PanelUI-remotetabs-deck");
+  is(deck.selectedIndex, DECKINDEX_TABS, "we should be showing tabs");
+
+  function checkTabsPage(tabsShownCount, showMoreLabel) {
+    let tabList = document.getElementById("PanelUI-remotetabs-tabslist");
+    let node = tabList.firstChild;
+    is(node.getAttribute("itemtype"), "client", "node is a client entry");
+    is(node.textContent, "My Desktop", "correct client");
+    for (let i = 0; i < tabsShownCount; i++) {
+      node = node.nextSibling;
+      is(node.getAttribute("itemtype"), "tab", "node is a tab");
+      is(node.getAttribute("label"), "Tab #" + (i + 1), "the tab is the correct one");
+    }
+    let showMoreButton;
+    if (showMoreLabel) {
+      node = showMoreButton = node.nextSibling;
+      is(node.getAttribute("itemtype"), "showmorebutton", "node is a show more button");
+      is(node.getAttribute("label"), showMoreLabel);
+    }
+    node = node.nextSibling;
+    is(node, null, "no more entries");
+
+    return showMoreButton;
+  }
+
+  let showMoreButton;
+  function clickShowMoreButton() {
+    let promise = promiseObserverNotified("synced-tabs-menu:test:tabs-updated");
+    showMoreButton.click();
+    return promise;
+  }
+
+  showMoreButton = checkTabsPage(25, "Show More");
+  yield clickShowMoreButton();
+
+  showMoreButton = checkTabsPage(50, "Show More");
+  yield clickShowMoreButton();
+
+  showMoreButton = checkTabsPage(72, "Show All");
+  yield clickShowMoreButton();
+
+  checkTabsPage(77, null);
+
+  yield panelUIHide();
+});
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -33,17 +33,24 @@ function isAncestorOrSelf(target, node) 
     if (node === target) {
       return true;
     }
   }
   return false;
 }
 
 // WeakMap[Extension -> BrowserAction]
-var browserActionMap = new WeakMap();
+const browserActionMap = new WeakMap();
+
+const browserAreas = {
+  "navbar": CustomizableUI.AREA_NAVBAR,
+  "menupanel": CustomizableUI.AREA_PANEL,
+  "tabstrip": CustomizableUI.AREA_TABSTRIP,
+  "personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
+};
 
 // Responsible for the browser_action section of the manifest as well
 // as the associated popup.
 function BrowserAction(options, extension) {
   this.extension = extension;
 
   let widgetId = makeWidgetId(extension.id);
   this.id = `${widgetId}-browser-action`;
@@ -57,16 +64,17 @@ function BrowserAction(options, extensio
 
   this.defaults = {
     enabled: true,
     title: options.default_title || extension.name,
     badgeText: "",
     badgeBackgroundColor: null,
     icon: IconDetails.normalize({path: options.default_icon}, extension),
     popup: options.default_popup || "",
+    area: browserAreas[options.default_area || "navbar"],
   };
 
   this.browserStyle = options.browser_style || false;
   if (options.browser_style === null) {
     this.extension.logger.warn("Please specify whether you want browser_style " +
                                "or not in your browser_action options.");
   }
 
@@ -80,17 +88,17 @@ BrowserAction.prototype = {
   build() {
     let widget = CustomizableUI.createWidget({
       id: this.id,
       viewId: this.viewId,
       type: "view",
       removable: true,
       label: this.defaults.title || this.extension.name,
       tooltiptext: this.defaults.title || "",
-      defaultArea: CustomizableUI.AREA_NAVBAR,
+      defaultArea: this.defaults.area,
 
       onBeforeCreated: document => {
         let view = document.createElementNS(XUL_NS, "panelview");
         view.id = this.viewId;
         view.setAttribute("flex", "1");
 
         document.getElementById("PanelUI-multiView").appendChild(view);
         document.addEventListener("popupshowing", this);
--- a/browser/components/extensions/schemas/browser_action.json
+++ b/browser/components/extensions/schemas/browser_action.json
@@ -26,16 +26,22 @@
                 "type": "string",
                 "format": "relativeUrl",
                 "optional": true,
                 "preprocess": "localize"
               },
               "browser_style": {
                 "type": "boolean",
                 "optional": true
+              },
+              "default_area": {
+                "description": "Defines the location the browserAction will appear by default.  The default location is navbar.",
+                "type": "string",
+                "enum": ["navbar", "menupanel", "tabstrip", "personaltoolbar"],
+                "optional": true
               }
             },
             "optional": true
           }
         }
       }
     ]
   },
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -19,16 +19,17 @@ support-files =
   file_dummy.html
   file_inspectedwindow_reload_target.sjs
   file_serviceWorker.html
   serviceWorker.js
   searchSuggestionEngine.xml
   searchSuggestionEngine.sjs
   ../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
 
+[browser_ext_browserAction_area.js]
 [browser_ext_browserAction_context.js]
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
 [browser_ext_browserAction_popup.js]
 [browser_ext_browserAction_popup_preload.js]
 [browser_ext_browserAction_popup_resize.js]
 [browser_ext_browserAction_simple.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_area.js
@@ -0,0 +1,49 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+var browserAreas = {
+  "navbar": CustomizableUI.AREA_NAVBAR,
+  "menupanel": CustomizableUI.AREA_PANEL,
+  "tabstrip": CustomizableUI.AREA_TABSTRIP,
+  "personaltoolbar": CustomizableUI.AREA_BOOKMARKS,
+};
+
+function* testInArea(area) {
+  let manifest = {
+    "browser_action": {
+      "browser_style": true,
+    },
+  };
+  if (area) {
+    manifest.browser_action.default_area = area;
+  }
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest,
+  });
+  yield extension.startup();
+  let widget = getBrowserActionWidget(extension);
+  let placement = CustomizableUI.getPlacementOfWidget(widget.id);
+  is(placement && placement.area, browserAreas[area || "navbar"], `widget located in correct area`);
+  yield extension.unload();
+}
+
+add_task(function* testBrowserActionDefaultArea() {
+  yield testInArea();
+});
+
+add_task(function* testBrowserActionInToolbar() {
+  yield testInArea("navbar");
+});
+
+add_task(function* testBrowserActionInMenuPanel() {
+  yield testInArea("menupanel");
+});
+
+add_task(function* testBrowserActionInTabStrip() {
+  yield testInArea("tabstrip");
+});
+
+add_task(function* testBrowserActionInPersonalToolbar() {
+  yield testInArea("personaltoolbar");
+});
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -383,17 +383,17 @@ this.SessionStore = {
    *        The browser state for which we remove worth-saving tabs.
    *        The given object will be modified.
    */
   keepOnlyWorthSavingTabs: function (aState) {
     for (let i = aState.windows.length - 1; i >= 0; i--) {
       let win = aState.windows[i];
       for (let j = win.tabs.length - 1; j >= 0; j--) {
         let tab = win.tabs[j];
-        if (!SessionStoreInternal._shouldSaveTabState(tab)) {
+        if (!SessionStoreInternal._shouldSaveTab(tab)) {
           win.tabs.splice(j, 1);
           if (win.selected > j) {
             win.selected--;
           }
         }
       }
       if (!win.tabs.length) {
         aState.windows.splice(i, 1);
@@ -4193,21 +4193,40 @@ var SessionStoreInternal = {
   _shouldSaveTabState: function ssi_shouldSaveTabState(aTabState) {
     // If the tab has only a transient about: history entry, no other
     // session history, and no userTypedValue, then we don't actually want to
     // store this tab's data.
     return aTabState.entries.length &&
            !(aTabState.entries.length == 1 &&
                 (aTabState.entries[0].url == "about:blank" ||
                  aTabState.entries[0].url == "about:newtab" ||
+                 aTabState.entries[0].url == "about:printpreview" ||
                  aTabState.entries[0].url == "about:privatebrowsing") &&
                  !aTabState.userTypedValue);
   },
 
   /**
+   * Determine if the tab state we're passed is something we should keep to be
+   * reopened at session restore. This is used when we are saving the current
+   * session state to disk. This method is very similar to _shouldSaveTabState,
+   * however, "about:blank" and "about:newtab" tabs will still be saved to disk.
+   *
+   * @param aTabState
+   *        The current tab state
+   * @returns boolean
+   */
+  _shouldSaveTab: function ssi_shouldSaveTab(aTabState) {
+    // If the tab has one of the following transient about: history entry,
+    // then we don't actually want to write this tab's data to disk.
+    return aTabState.entries.length &&
+           !(aTabState.entries[0].url == "about:printpreview" ||
+             aTabState.entries[0].url == "about:privatebrowsing");
+  },
+
+  /**
    * This is going to take a state as provided at startup (via
    * nsISessionStartup.state) and split it into 2 parts. The first part
    * (defaultState) will be a state that should still be restored at startup,
    * while the second part (state) is a state that should be saved for later.
    * defaultState will be comprised of windows with only pinned tabs, extracted
    * from state. It will contain the cookies that go along with the history
    * entries in those tabs. It will also contain window position information.
    *
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -50,17 +50,16 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=browser/
 MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 # This should usually be the same as the value MAR_CHANNEL_ID.
 # If more than one ID is needed, then you should use a comma separated list
 # of values.
 ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-central
 # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
 MAR_CHANNEL_ID=firefox-mozilla-central
 MOZ_PROFILE_MIGRATOR=1
-MOZ_WEBGL_CONFORMANT=1
 MOZ_JSDOWNLOADS=1
 MOZ_RUST_MP4PARSE=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/README.md
+++ /dev/null
@@ -1,99 +0,0 @@
-This system add-on is a follow-up to the MITM prevalence experiment. The purpose
-is to facilitate rolling out the disabling of SHA-1 in signatures on
-certificates issued by publicly-trusted roots. When installed, this add-on will
-perform a number of checks to determine if it should change the preference that
-controls the SHA-1 policy. First, this should only apply to users on the beta
-update channel. It should also only apply to users who have not otherwise
-changed the policy to always allow or always forbid SHA-1. Additionally, it
-must double-check that the user is not affected by a TLS intercepting proxy
-using a publicly-trusted root. If these checks pass, the add-on will divide the
-population into a test group and a control group (starting on a 10%/90% split).
-The test group will have the policy changed. After doing this, a telemetry
-payload is reported with the following values:
-
-* cohortName -- the name of the group this user is in:
-  1. "notSafeToDisableSHA1" if the user is behind a MITM proxy using a
-     publicly-trusted root
-  2. "optedOut" if the user already set the SHA-1 policy to always allow or
-     always forbid
-  3. "optedIn" if the user already set the SHA-1 policy to only allow for
-     non-built-in roots
-  4. "test" if the user is in the test cohort (and SHA-1 will be disabled)
-  5. "control" if the user is not in the test cohort
-* errorCode -- 0 for successful connections, some PR error code otherwise
-* error -- a short description of one of four error conditions encountered, if
-  applicable, and an empty string otherwise:
-  1. "timeout" if the connection to telemetry.mozilla.org timed out
-  2. "user override" if the user has stored a permanent certificate exception
-     override for telemetry.mozilla.org (due to technical limitations, we can't
-     gather much information in this situation)
-  3. "certificate reverification" if re-building the certificate chain after
-     connecting failed for some reason (unfortunately this step is necessary
-     due to technical limitations)
-  4. "connection error" if the connection to telemetry.mozilla.org failed for
-     another reason
-* chain -- a list of dictionaries each corresponding to a certificate in the
-  verified certificate chain, if it was successfully constructed. The first
-  entry is the end-entity certificate. The last entry is the root certificate.
-  This will be empty if the connection failed or if reverification failed. Each
-  element in the list contains the following values:
-  * sha256Fingerprint -- a hex string representing the SHA-256 hash of the
-    certificate
-  * isBuiltInRoot -- true if the certificate is a trust anchor in the web PKI,
-    false otherwise
-  * signatureAlgorithm -- a description of the algorithm used to sign the
-    certificate. Will be one of "md2WithRSAEncryption", "md5WithRSAEncryption",
-    "sha1WithRSAEncryption", "sha256WithRSAEncryption",
-    "sha384WithRSAEncryption", "sha512WithRSAEncryption", "ecdsaWithSHA1",
-    "ecdsaWithSHA224", "ecdsaWithSHA256", "ecdsaWithSHA384", "ecdsaWithSHA512",
-    or "unknown".
-* disabledSHA1 -- true if SHA-1 was disabled, false otherwise
-* didNotDisableSHA1Because -- a short string describing why SHA-1 could not be
-    disabled, if applicable. Reasons are limited to:
-    1. "MITM" if the user is behind a TLS intercepting proxy using a
-       publicly-trusted root
-    2. "connection error" if there was an error connecting to
-       telemetry.mozilla.org
-    3. "code error" if some inconsistent state was detected, and it was
-       determined that the experiment should not attempt to change the
-       preference
-    4. "preference:userReset" if the user reset the SHA-1 policy after it had
-       been changed by this add-on
-    5. "preference:allow" if the user had already configured Firefox to always
-       accept SHA-1 signatures
-    6. "preference:forbid" if the user had already configured Firefox to always
-       forbid SHA-1 signatures
-
-For a connection not intercepted by a TLS proxy and where the user is in the
-test cohort, the expected result will be:
-
-    { "cohortName": "test",
-      "errorCode": 0,
-      "error": "",
-      "chain": [
-        { "sha256Fingerprint": "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c",
-          "isBuiltInRoot": false,
-          "signatureAlgorithm": "sha256WithRSAEncryption"
-        },
-        { "sha256Fingerprint": "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f",
-          "isBuiltInRoot": false,
-          "signatureAlgorithm": "sha256WithRSAEncryption"
-        },
-        { "sha256Fingerprint": "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161",
-          "isBuiltInRoot": true,
-          "signatureAlgorithm": "sha1WithRSAEncryption"
-        }
-      ],
-      "disabledSHA1": true,
-      "didNotDisableSHA1Because": ""
-    }
-
-When this result is encountered, the user's preferences are updated to disable
-SHA-1 in signatures on certificates issued by publicly-trusted roots.
-Similarly, if the user is behind a TLS intercepting proxy but the root
-certificate is not part of the public web PKI, we can also disable SHA-1 in
-signatures on certificates issued by publicly-trusted roots.
-
-If the user has already indicated in their preferences that they will always
-accept SHA-1 in signatures or that they will never accept SHA-1 in signatures,
-then the preference is not changed.
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/bootstrap.js
+++ /dev/null
@@ -1,306 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/UpdateUtils.jsm");
-Cu.import("resource://gre/modules/TelemetryController.jsm");
-
- // Percentage of the population to attempt to disable SHA-1 for, by channel.
-const TEST_THRESHOLD = {
-  beta: 0.1, // 10%
-};
-
-const PREF_COHORT_SAMPLE = "disableSHA1.rollout.cohortSample";
-const PREF_COHORT_NAME = "disableSHA1.rollout.cohort";
-const PREF_SHA1_POLICY = "security.pki.sha1_enforcement_level";
-const PREF_SHA1_POLICY_SET_BY_ADDON = "disableSHA1.rollout.policySetByAddOn";
-const PREF_SHA1_POLICY_RESET_BY_USER = "disableSHA1.rollout.userResetPref";
-
-const SHA1_MODE_ALLOW = 0;
-const SHA1_MODE_FORBID = 1;
-const SHA1_MODE_IMPORTED_ROOTS_ONLY = 3;
-const SHA1_MODE_CURRENT_DEFAULT = 4;
-
-function startup() {
-  Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
-}
-
-function install() {
-  let updateChannel = UpdateUtils.getUpdateChannel(false);
-  if (updateChannel in TEST_THRESHOLD) {
-    makeRequest().then(defineCohort).catch((e) => console.error(e));
-  }
-}
-
-function policyPreferenceChanged() {
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  Preferences.reset(PREF_SHA1_POLICY_RESET_BY_USER);
-  if (currentPrefValue == SHA1_MODE_CURRENT_DEFAULT) {
-    Preferences.set(PREF_SHA1_POLICY_RESET_BY_USER, true);
-  }
-}
-
-function defineCohort(result) {
-  let userOptedOut = optedOut();
-  let userOptedIn = optedIn();
-  let shouldNotDisableSHA1Because = reasonToNotDisableSHA1(result);
-  let safeToDisableSHA1 = shouldNotDisableSHA1Because.length == 0;
-  let updateChannel = UpdateUtils.getUpdateChannel(false);
-  let testGroup = getUserSample() < TEST_THRESHOLD[updateChannel];
-
-  let cohortName;
-  if (!safeToDisableSHA1) {
-    cohortName = "notSafeToDisableSHA1";
-  } else if (userOptedOut) {
-    cohortName = "optedOut";
-  } else if (userOptedIn) {
-    cohortName = "optedIn";
-  } else if (testGroup) {
-    cohortName = "test";
-    Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
-    Preferences.set(PREF_SHA1_POLICY, SHA1_MODE_IMPORTED_ROOTS_ONLY);
-    Preferences.observe(PREF_SHA1_POLICY, policyPreferenceChanged);
-    Preferences.set(PREF_SHA1_POLICY_SET_BY_ADDON, true);
-  } else {
-    cohortName = "control";
-  }
-  Preferences.set(PREF_COHORT_NAME, cohortName);
-  reportTelemetry(result, cohortName, shouldNotDisableSHA1Because,
-                  cohortName == "test");
-}
-
-function shutdown(data, reason) {
-  Preferences.ignore(PREF_SHA1_POLICY, policyPreferenceChanged);
-}
-
-function uninstall() {
-}
-
-function getUserSample() {
-  let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
-  let value = 0.0;
-
-  if (typeof(prefValue) == "string") {
-    value = parseFloat(prefValue, 10);
-    return value;
-  }
-
-  value = Math.random();
-
-  Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
-  return value;
-}
-
-function reportTelemetry(result, cohortName, didNotDisableSHA1Because,
-                         disabledSHA1) {
-  result.cohortName = cohortName;
-  result.disabledSHA1 = disabledSHA1;
-  if (cohortName == "optedOut") {
-    let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
-    let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                           SHA1_MODE_CURRENT_DEFAULT);
-    if (userResetPref) {
-      didNotDisableSHA1Because = "preference:userReset";
-    } else if (currentPrefValue == SHA1_MODE_ALLOW) {
-      didNotDisableSHA1Because = "preference:allow";
-    } else {
-      didNotDisableSHA1Because = "preference:forbid";
-    }
-  }
-  result.didNotDisableSHA1Because = didNotDisableSHA1Because;
-  return TelemetryController.submitExternalPing("disableSHA1rollout", result,
-                                                {});
-}
-
-function optedIn() {
-  let policySetByAddOn = Preferences.get(PREF_SHA1_POLICY_SET_BY_ADDON, false);
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  return currentPrefValue == SHA1_MODE_IMPORTED_ROOTS_ONLY && !policySetByAddOn;
-}
-
-function optedOut() {
-  // Users can also opt-out by setting the policy to always allow or always
-  // forbid SHA-1, or by resetting the preference after this add-on has changed
-  // it (in that case, this will be reported the next time this add-on is
-  // updated).
-  let currentPrefValue = Preferences.get(PREF_SHA1_POLICY,
-                                         SHA1_MODE_CURRENT_DEFAULT);
-  let userResetPref = Preferences.get(PREF_SHA1_POLICY_RESET_BY_USER, false);
-  return currentPrefValue == SHA1_MODE_ALLOW ||
-         currentPrefValue == SHA1_MODE_FORBID ||
-         userResetPref;
-}
-
-function delocalizeAlgorithm(localizedString) {
-  let bundle = Services.strings.createBundle(
-    "chrome://pipnss/locale/pipnss.properties");
-  let algorithmStringIdsToOIDDescriptionMap = {
-    "CertDumpMD2WithRSA":                       "md2WithRSAEncryption",
-    "CertDumpMD5WithRSA":                       "md5WithRSAEncryption",
-    "CertDumpSHA1WithRSA":                      "sha1WithRSAEncryption",
-    "CertDumpSHA256WithRSA":                    "sha256WithRSAEncryption",
-    "CertDumpSHA384WithRSA":                    "sha384WithRSAEncryption",
-    "CertDumpSHA512WithRSA":                    "sha512WithRSAEncryption",
-    "CertDumpAnsiX962ECDsaSignatureWithSha1":   "ecdsaWithSHA1",
-    "CertDumpAnsiX962ECDsaSignatureWithSha224": "ecdsaWithSHA224",
-    "CertDumpAnsiX962ECDsaSignatureWithSha256": "ecdsaWithSHA256",
-    "CertDumpAnsiX962ECDsaSignatureWithSha384": "ecdsaWithSHA384",
-    "CertDumpAnsiX962ECDsaSignatureWithSha512": "ecdsaWithSHA512",
-  };
-
-  let description;
-  Object.keys(algorithmStringIdsToOIDDescriptionMap).forEach((l10nID) => {
-    let candidateLocalizedString = bundle.GetStringFromName(l10nID);
-    if (localizedString == candidateLocalizedString) {
-      description = algorithmStringIdsToOIDDescriptionMap[l10nID];
-    }
-  });
-  if (!description) {
-    return "unknown";
-  }
-  return description;
-}
-
-function getSignatureAlgorithm(cert) {
-  // Certificate  ::=  SEQUENCE  {
-  //      tbsCertificate       TBSCertificate,
-  //      signatureAlgorithm   AlgorithmIdentifier,
-  //      signatureValue       BIT STRING  }
-  let certificate = cert.ASN1Structure.QueryInterface(Ci.nsIASN1Sequence);
-  let signatureAlgorithm = certificate.ASN1Objects
-                                      .queryElementAt(1, Ci.nsIASN1Sequence);
-  // AlgorithmIdentifier  ::=  SEQUENCE  {
-  //      algorithm               OBJECT IDENTIFIER,
-  //      parameters              ANY DEFINED BY algorithm OPTIONAL  }
-
-  // If parameters is NULL (or empty), signatureAlgorithm won't be a container
-  // under this implementation. Just get its displayValue.
-  if (!signatureAlgorithm.isValidContainer) {
-    return signatureAlgorithm.displayValue;
-  }
-  let oid = signatureAlgorithm.ASN1Objects.queryElementAt(0, Ci.nsIASN1Object);
-  return oid.displayValue;
-}
-
-function processCertChain(chain) {
-  let output = [];
-  let enumerator = chain.getEnumerator();
-  while (enumerator.hasMoreElements()) {
-    let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
-    output.push({
-      sha256Fingerprint: cert.sha256Fingerprint.replace(/:/g, "").toLowerCase(),
-      isBuiltInRoot: cert.isBuiltInRoot,
-      signatureAlgorithm: delocalizeAlgorithm(getSignatureAlgorithm(cert)),
-    });
-  }
-  return output;
-}
-
-class CertificateVerificationResult {
-  constructor(resolve) {
-    this.resolve = resolve;
-  }
-
-  verifyCertFinished(aPRErrorCode, aVerifiedChain, aEVStatus) {
-    let result = { errorCode: aPRErrorCode, error: "", chain: [] };
-    if (aPRErrorCode == 0) {
-      result.chain = processCertChain(aVerifiedChain);
-    } else {
-      result.error = "certificate reverification";
-    }
-    this.resolve(result);
-  }
-}
-
-function makeRequest() {
-  return new Promise((resolve) => {
-    let hostname = "telemetry.mozilla.org";
-    let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
-                .createInstance(Ci.nsIXMLHttpRequest);
-    req.open("GET", "https://" + hostname);
-    req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
-    req.timeout = 30000;
-    req.addEventListener("error", (evt) => {
-      // If we can't connect to telemetry.mozilla.org, then how did we even
-      // download the experiment? In any case, we may still be able to get some
-      // information.
-      let result = { error: "connection error" };
-      if (evt.target.channel && evt.target.channel.securityInfo) {
-        let securityInfo = evt.target.channel.securityInfo
-                             .QueryInterface(Ci.nsITransportSecurityInfo);
-        if (securityInfo) {
-          result.errorCode = securityInfo.errorCode;
-        }
-        if (securityInfo && securityInfo.failedCertChain) {
-          result.chain = processCertChain(securityInfo.failedCertChain);
-        }
-      }
-      resolve(result);
-    });
-    req.addEventListener("timeout", (evt) => {
-      resolve({ error: "timeout" });
-    });
-    req.addEventListener("load", (evt) => {
-      let securityInfo = evt.target.channel.securityInfo
-                           .QueryInterface(Ci.nsITransportSecurityInfo);
-      if (securityInfo.securityState &
-          Ci.nsIWebProgressListener.STATE_CERT_USER_OVERRIDDEN) {
-        resolve({ error: "user override" });
-        return;
-      }
-      let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
-                        .SSLStatus;
-      let certdb = Cc["@mozilla.org/security/x509certdb;1"]
-                     .getService(Ci.nsIX509CertDB);
-      let result = new CertificateVerificationResult(resolve);
-      // Unfortunately, we don't have direct access to the verified certificate
-      // chain as built by the AuthCertificate hook, so we have to re-build it
-      // here. In theory we are likely to get the same result.
-      certdb.asyncVerifyCertAtTime(sslStatus.serverCert,
-                                   2, // certificateUsageSSLServer
-                                   0, // flags
-                                   hostname,
-                                   Date.now() / 1000,
-                                   result);
-    });
-    req.send();
-  });
-}
-
-// As best we know, it is safe to disable SHA1 if the connection was successful
-// and either the connection was MITM'd by a root not in the public PKI or the
-// chain is part of the public PKI and is the one served by the real
-// telemetry.mozilla.org.
-// This will return a short string description of why it might not be safe to
-// disable SHA1 or an empty string if it is safe to disable SHA1.
-function reasonToNotDisableSHA1(result) {
-  if (!("errorCode" in result) || result.errorCode != 0) {
-    return "connection error";
-  }
-  if (!("chain" in result)) {
-    return "code error";
-  }
-  if (!result.chain[result.chain.length - 1].isBuiltInRoot) {
-    return "";
-  }
-  if (result.chain.length != 3) {
-    return "MITM";
-  }
-  const kEndEntityFingerprint = "197feaf3faa0f0ad637a89c97cb91336bfc114b6b3018203cbd9c3d10c7fa86c";
-  const kIntermediateFingerprint = "154c433c491929c5ef686e838e323664a00e6a0d822ccc958fb4dab03e49a08f";
-  const kRootFingerprint = "4348a0e9444c78cb265e058d5e8944b4d84f9662bd26db257f8934a443c70161";
-  if (result.chain[0].sha256Fingerprint != kEndEntityFingerprint ||
-      result.chain[1].sha256Fingerprint != kIntermediateFingerprint ||
-      result.chain[2].sha256Fingerprint != kRootFingerprint) {
-    return "MITM";
-  }
-  return "";
-}
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/install.rdf.in
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-#filter substitution
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>disableSHA1rollout@mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:multiprocessCompatible>true</em:multiprocessCompatible>
-
-    <!-- Target Application this theme can install into,
-        with minimum and maximum supported versions. -->
-    <em:targetApplication>
-      <Description>
-        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
-        <em:minVersion>@MOZ_APP_VERSION@</em:minVersion>
-        <em:maxVersion>@MOZ_APP_MAXVERSION@</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-
-    <!-- Front End MetaData -->
-    <em:name>SHA-1 deprecation staged rollout</em:name>
-    <em:description>Staged rollout deprecating SHA-1 in certificate signatures.</em:description>
-  </Description>
-</RDF>
deleted file mode 100644
--- a/browser/extensions/disableSHA1rollout/moz.build
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
-DEFINES['MOZ_APP_MAXVERSION'] = CONFIG['MOZ_APP_MAXVERSION']
-
-FINAL_TARGET_FILES.features['disableSHA1rollout@mozilla.org'] += [
-  'bootstrap.js'
-]
-
-FINAL_TARGET_PP_FILES.features['disableSHA1rollout@mozilla.org'] += [
-  'install.rdf.in'
-]
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -266,19 +266,20 @@ var FormAutofillContent = {
 
     Services.cpmm.addMessageListener("FormAutofill:enabledStatus", (result) => {
       if (result.data) {
         ProfileAutocomplete.ensureRegistered();
       } else {
         ProfileAutocomplete.ensureUnregistered();
       }
     });
-    Services.cpmm.sendAsyncMessage("FormAutofill:getEnabledStatus");
-    // TODO: use initialProcessData:
-    // Services.cpmm.initialProcessData.autofillEnabled
+
+    if (Services.cpmm.initialProcessData.autofillEnabled) {
+      ProfileAutocomplete.ensureRegistered();
+    }
   },
 
   /**
    * Get the input's information from cache which is created after page identified.
    *
    * @param {HTMLInputElement} element Focused input which triggered profile searching
    * @returns {Object|null}
    *          Return target input's information that cloned from content cache
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -75,21 +75,20 @@ FormAutofillParent.prototype = {
     this._profileStore = new ProfileStorage(storePath);
     this._profileStore.initialize();
 
     Services.obs.addObserver(this, "advanced-pane-loaded", false);
 
     // Observing the pref and storage changes
     Services.prefs.addObserver(ENABLED_PREF, this, false);
     Services.obs.addObserver(this, "formautofill-storage-changed", false);
-    this._enabled = this._getStatus();
+
     // Force to trigger the onStatusChanged function for setting listeners properly
     // while initizlization
-    this._onStatusChanged();
-    Services.ppmm.addMessageListener("FormAutofill:getEnabledStatus", this);
+    this._setStatus(this._getStatus());
   },
 
   observe(subject, topic, data) {
     log.debug("observe:", topic, "with data:", data);
     switch (topic) {
       case "advanced-pane-loaded": {
         let formAutofillPreferences = new FormAutofillPreferences();
         let document = subject.document;
@@ -99,32 +98,30 @@ FormAutofillParent.prototype = {
         parentNode.insertBefore(prefGroup, insertBeforeNode);
         break;
       }
 
       case "nsPref:changed": {
         // Observe pref changes and update _enabled cache if status is changed.
         let currentStatus = this._getStatus();
         if (currentStatus !== this._enabled) {
-          this._enabled = currentStatus;
-          this._onStatusChanged();
+          this._setStatus(currentStatus);
         }
         break;
       }
 
       case "formautofill-storage-changed": {
         // Early exit if the action is not "add" nor "remove"
         if (data != "add" && data != "remove") {
           break;
         }
 
         let currentStatus = this._getStatus();
         if (currentStatus !== this._enabled) {
-          this._enabled = currentStatus;
-          this._onStatusChanged();
+          this._setStatus(currentStatus);
         }
         break;
       }
 
       default: {
         throw new Error(`FormAutofillParent: Unexpected topic observed: ${topic}`);
       }
     }
@@ -138,16 +135,19 @@ FormAutofillParent.prototype = {
     log.debug("_onStatusChanged: Status changed to", this._enabled);
     if (this._enabled) {
       Services.ppmm.addMessageListener("FormAutofill:GetProfiles", this);
     } else {
       Services.ppmm.removeMessageListener("FormAutofill:GetProfiles", this);
     }
 
     Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus", this._enabled);
+    // Sync process data autofillEnabled to make sure the value up to date
+    // no matter when the new content process is initialized.
+    Services.ppmm.initialProcessData.autofillEnabled = this._enabled;
   },
 
   /**
    * Query pref and storage status to determine the overall status for
    * form autofill feature.
    *
    * @returns {boolean} status of form autofill feature
    */
@@ -155,31 +155,37 @@ FormAutofillParent.prototype = {
     if (!Services.prefs.getBoolPref(ENABLED_PREF)) {
       return false;
     }
 
     return this._profileStore.getAll().length > 0;
   },
 
   /**
+   * Set status and trigger _onStatusChanged.
+   *
+   * @param {boolean} newStatus The latest status we want to set for _enabled
+   */
+  _setStatus(newStatus) {
+    this._enabled = newStatus;
+    this._onStatusChanged();
+  },
+
+  /**
    * Handles the message coming from FormAutofillContent.
    *
    * @param   {string} message.name The name of the message.
    * @param   {object} message.data The data of the message.
    * @param   {nsIFrameMessageManager} message.target Caller's message manager.
    */
   receiveMessage({name, data, target}) {
     switch (name) {
       case "FormAutofill:GetProfiles":
         this._getProfiles(data, target);
         break;
-      case "FormAutofill:getEnabledStatus":
-        Services.ppmm.broadcastAsyncMessage("FormAutofill:enabledStatus",
-                                            this._enabled);
-        break;
     }
   },
 
   /**
    * Returns the instance of ProfileStorage. To avoid syncing issues, anyone
    * who needs to access the profile should request the instance by this instead
    * of creating a new one.
    *
--- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
+++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm
@@ -64,21 +64,21 @@ ProfileAutoCompleteResult.prototype = {
 
   // An array of primary and secondary labels for each profiles.
   _popupLabels: null,
 
   /**
    * @returns {number} The number of results
    */
   get matchCount() {
-    return this._matchingProfiles.length;
+    return this._popupLabels.length;
   },
 
   _checkIndexBounds(index) {
-    if (index < 0 || index >= this._matchingProfiles.length) {
+    if (index < 0 || index >= this._popupLabels.length) {
       throw Components.Exception("Index out of range.", Cr.NS_ERROR_ILLEGAL_VALUE);
     }
   },
 
   /**
    * Get the secondary label based on the focused field name and related field names
    * in the same form.
    * @param   {string} focusedFieldName The field name of the focused input
@@ -118,17 +118,20 @@ ProfileAutoCompleteResult.prototype = {
         return profile[currentFieldName];
       }
     }
 
     return ""; // Nothing matched.
   },
 
   _generateLabels(focusedFieldName, allFieldNames, profiles) {
-    return profiles.map(profile => {
+    // Skip results without a primary label.
+    return profiles.filter(profile => {
+      return !!profile[focusedFieldName];
+    }).map(profile => {
       return {
         primary: profile[focusedFieldName],
         secondary: this._getSecondaryLabel(focusedFieldName,
                                            allFieldNames,
                                            profile),
       };
     });
   },
--- a/browser/extensions/formautofill/test/unit/test_enabledStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_enabledStatus.js
@@ -3,60 +3,62 @@
  */
 
 "use strict";
 
 Cu.import("resource://formautofill/FormAutofillParent.jsm");
 
 add_task(function* test_enabledStatus_init() {
   let formAutofillParent = new FormAutofillParent();
-  sinon.spy(formAutofillParent, "_onStatusChanged");
+  sinon.spy(formAutofillParent, "_setStatus");
 
   // Default status is false before initialization
   do_check_eq(formAutofillParent._enabled, false);
+  do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, undefined);
 
   formAutofillParent.init();
-  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+  do_check_eq(formAutofillParent._setStatus.called, true);
+  do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false);
 
   formAutofillParent._uninit();
 });
 
 add_task(function* test_enabledStatus_observe() {
   let formAutofillParent = new FormAutofillParent();
   sinon.stub(formAutofillParent, "_getStatus");
-  sinon.spy(formAutofillParent, "_onStatusChanged");
+  sinon.spy(formAutofillParent, "_setStatus");
 
   // _enabled = _getStatus() => No need to trigger onStatusChanged
   formAutofillParent._enabled = true;
   formAutofillParent._getStatus.returns(true);
   formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
-  do_check_eq(formAutofillParent._onStatusChanged.called, false);
+  do_check_eq(formAutofillParent._setStatus.called, false);
 
   // _enabled != _getStatus() => Need to trigger onStatusChanged
   formAutofillParent._getStatus.returns(false);
   formAutofillParent.observe(null, "nsPref:changed", "browser.formautofill.enabled");
-  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+  do_check_eq(formAutofillParent._setStatus.called, true);
 
   // profile added => Need to trigger onStatusChanged
   formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
-  formAutofillParent._onStatusChanged.reset();
+  formAutofillParent._setStatus.reset();
   formAutofillParent.observe(null, "formautofill-storage-changed", "add");
-  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+  do_check_eq(formAutofillParent._setStatus.called, true);
 
   // profile removed => Need to trigger onStatusChanged
   formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
-  formAutofillParent._onStatusChanged.reset();
+  formAutofillParent._setStatus.reset();
   formAutofillParent.observe(null, "formautofill-storage-changed", "remove");
-  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+  do_check_eq(formAutofillParent._setStatus.called, true);
 
   // profile updated => no need to trigger onStatusChanged
   formAutofillParent._getStatus.returns(!formAutofillParent._enabled);
-  formAutofillParent._onStatusChanged.reset();
+  formAutofillParent._setStatus.reset();
   formAutofillParent.observe(null, "formautofill-storage-changed", "update");
-  do_check_eq(formAutofillParent._onStatusChanged.called, false);
+  do_check_eq(formAutofillParent._setStatus.called, false);
 });
 
 add_task(function* test_enabledStatus_getStatus() {
   let formAutofillParent = new FormAutofillParent();
   do_register_cleanup(function cleanup() {
     Services.prefs.clearUserPref("browser.formautofill.enabled");
   });
 
@@ -77,8 +79,24 @@ add_task(function* test_enabledStatus_ge
   // pref is enabled and profile is not empty.
   Services.prefs.setBoolPref("browser.formautofill.enabled", true);
   do_check_eq(formAutofillParent._getStatus(), true);
 
   // pref is disabled and profile is not empty.
   Services.prefs.setBoolPref("browser.formautofill.enabled", false);
   do_check_eq(formAutofillParent._getStatus(), false);
 });
+
+add_task(function* test_enabledStatus_setStatus() {
+  let formAutofillParent = new FormAutofillParent();
+  sinon.spy(formAutofillParent, "_onStatusChanged");
+
+  formAutofillParent._setStatus(true);
+  do_check_eq(formAutofillParent._enabled, true);
+  do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, true);
+  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+
+  formAutofillParent._onStatusChanged.reset();
+  formAutofillParent._setStatus(false);
+  do_check_eq(formAutofillParent._enabled, false);
+  do_check_eq(Services.ppmm.initialProcessData.autofillEnabled, false);
+  do_check_eq(formAutofillParent._onStatusChanged.called, true);
+});
--- a/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
+++ b/browser/extensions/formautofill/test/unit/test_profileAutocompleteResult.js
@@ -7,21 +7,27 @@ let matchingProfiles = [{
   organization: "Sesame Street",
   "street-address": "123 Sesame Street.",
   tel: "1-345-345-3456.",
 }, {
   guid: "test-guid-2",
   organization: "Mozilla",
   "street-address": "331 E. Evelyn Avenue",
   tel: "1-650-903-0800",
+}, {
+  guid: "test-guid-3",
+  organization: "",
+  "street-address": "321, No Name St.",
+  tel: "1-000-000-0000",
 }];
 
 let allFieldNames = ["street-address", "organization", "tel"];
 
 let testCases = [{
+  description: "Focus on an `organization` field",
   options: {},
   matchingProfiles,
   allFieldNames,
   searchString: "",
   fieldName: "organization",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
@@ -41,16 +47,17 @@ let testCases = [{
       label: JSON.stringify({
         primary: "Mozilla",
         secondary: "331 E. Evelyn Avenue",
       }),
       image: "",
     }],
   },
 }, {
+  description: "Focus on an `tel` field",
   options: {},
   matchingProfiles,
   allFieldNames,
   searchString: "",
   fieldName: "tel",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
@@ -67,19 +74,29 @@ let testCases = [{
       value: "1-650-903-0800",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[1]),
       label: JSON.stringify({
         primary: "1-650-903-0800",
         secondary: "331 E. Evelyn Avenue",
       }),
       image: "",
+    }, {
+      value: "1-000-000-0000",
+      style: "autofill-profile",
+      comment: JSON.stringify(matchingProfiles[2]),
+      label: JSON.stringify({
+        primary: "1-000-000-0000",
+        secondary: "321, No Name St.",
+      }),
+      image: "",
     }],
   },
 }, {
+  description: "Focus on an `street-address` field",
   options: {},
   matchingProfiles,
   allFieldNames,
   searchString: "",
   fieldName: "street-address",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_SUCCESS,
     defaultIndex: 0,
@@ -96,50 +113,62 @@ let testCases = [{
       value: "331 E. Evelyn Avenue",
       style: "autofill-profile",
       comment: JSON.stringify(matchingProfiles[1]),
       label: JSON.stringify({
         primary: "331 E. Evelyn Avenue",
         secondary: "Mozilla",
       }),
       image: "",
+    }, {
+      value: "321, No Name St.",
+      style: "autofill-profile",
+      comment: JSON.stringify(matchingProfiles[2]),
+      label: JSON.stringify({
+        primary: "321, No Name St.",
+        secondary: "1-000-000-0000",
+      }),
+      image: "",
     }],
   },
 }, {
+  description: "No matching profiles",
   options: {},
   matchingProfiles: [],
   allFieldNames,
   searchString: "",
   fieldName: "",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
     defaultIndex: 0,
     items: [],
   },
 }, {
+  description: "Search with failure",
   options: {resultCode: Ci.nsIAutoCompleteResult.RESULT_FAILURE},
   matchingProfiles: [],
   allFieldNames,
   searchString: "",
   fieldName: "",
   expected: {
     searchResult: Ci.nsIAutoCompleteResult.RESULT_FAILURE,
     defaultIndex: 0,
     items: [],
   },
 }];
 
 add_task(function* test_all_patterns() {
-  testCases.forEach(pattern => {
-    let actual = new ProfileAutoCompleteResult(pattern.searchString,
-                                               pattern.fieldName,
-                                               pattern.allFieldNames,
-                                               pattern.matchingProfiles,
-                                               pattern.options);
-    let expectedValue = pattern.expected;
+  testCases.forEach(testCase => {
+    do_print("Starting testcase: " + testCase.description);
+    let actual = new ProfileAutoCompleteResult(testCase.searchString,
+                                               testCase.fieldName,
+                                               testCase.allFieldNames,
+                                               testCase.matchingProfiles,
+                                               testCase.options);
+    let expectedValue = testCase.expected;
     equal(actual.searchResult, expectedValue.searchResult);
     equal(actual.defaultIndex, expectedValue.defaultIndex);
     equal(actual.matchCount, expectedValue.items.length);
     expectedValue.items.forEach((item, index) => {
       equal(actual.getValueAt(index), item.value);
       equal(actual.getCommentAt(index), item.comment);
       equal(actual.getLabelAt(index), item.label);
       equal(actual.getStyleAt(index), item.style);
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,17 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'aushelper',
-    'disableSHA1rollout',
     'e10srollout',
     'pdfjs',
     'pocket',
     'webcompat',
     'shield-recipe-client',
 ]
 
 # Only include the following system add-ons if building Aurora or Nightly
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -631,16 +631,17 @@
 
 ; [Default Preferences]
 ; All the pref files must be part of base to prevent migration bugs
 @RESPATH@/browser/@PREF_DIR@/firefox.js
 @RESPATH@/browser/@PREF_DIR@/firefox-branding.js
 @RESPATH@/greprefs.js
 @RESPATH@/defaults/autoconfig/prefcalls.js
 @RESPATH@/browser/defaults/permissions
+@RESPATH@/browser/defaults/blocklists
 
 ; Warning: changing the path to channel-prefs.js can cause bugs (Bug 756325)
 ; Technically this is an app pref file, but we are keeping it in the original
 ; gre location for now.
 @RESPATH@/defaults/pref/channel-prefs.js
 
 ; Services (gre) prefs
 @RESPATH@/defaults/pref/services-sync.js
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -353,16 +353,24 @@ These should match what Safari and other
 <!ENTITY appMenuHistory.restoreSession.label "Restore Previous Session">
 <!ENTITY appMenuHistory.viewSidebar.label "View History Sidebar">
 <!ENTITY appMenuHelp.tooltip "Open Help Menu">
 
 <!ENTITY appMenuRemoteTabs.label "Synced Tabs">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.notabs.label): This is shown beneath
      the name of a device when that device has no open tabs -->
 <!ENTITY appMenuRemoteTabs.notabs.label "No open tabs">
+<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showMore.label, appMenuRemoteTabs.showMore.tooltip):
+     This is shown after the tabs list if we can display more tabs by clicking on the button -->
+<!ENTITY appMenuRemoteTabs.showMore.label "Show More">
+<!ENTITY appMenuRemoteTabs.showMore.tooltip "Show more tabs from this device">
+<!-- LOCALIZATION NOTE (appMenuRemoteTabs.showAll.label, appMenuRemoteTabs.showAll.tooltip):
+     This is shown after the tabs list if we can all the remaining tabs by clicking on the button -->
+<!ENTITY appMenuRemoteTabs.showAll.label "Show All">
+<!ENTITY appMenuRemoteTabs.showAll.tooltip "Show all tabs from this device">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.tabsnotsyncing.label): This is shown
      when Sync is configured but syncing tabs is disabled. -->
 <!ENTITY appMenuRemoteTabs.tabsnotsyncing.label "Turn on tab syncing to view a list of tabs from your other devices.">
 <!-- LOCALIZATION NOTE (appMenuRemoteTabs.noclients.label): This is shown
      when Sync is configured but this appears to be the only device attached to
      the account. We also show links to download Firefox for android/ios. -->
 <!ENTITY appMenuRemoteTabs.noclients.title "No synced tabs… yet!">
 <!ENTITY appMenuRemoteTabs.noclients.subtitle "Want to see your tabs from other devices here?">
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -202,22 +202,26 @@ removeAllSiteDataShown.label=Remove All 
 removeAllSiteDataShown.accesskey=e
 spaceAlert.learnMoreButton.label=Learn More
 spaceAlert.learnMoreButton.accesskey=L
 spaceAlert.over5GB.prefButton.label=Open Preferences
 spaceAlert.over5GB.prefButton.accesskey=O
 # LOCALIZATION NOTE (spaceAlert.over5GB.prefButtonWin.label): On Windows Preferences is called Options
 spaceAlert.over5GB.prefButtonWin.label=Open Options
 spaceAlert.over5GB.prefButtonWin.accesskey=O
-spaceAlert.over5GB.description=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data.
-# LOCALIZATION NOTE (spaceAlert.over5GB.descriptionWin): On Windows Preferences is called Options
-spaceAlert.over5GB.descriptionWin=Firefox is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data.
+# LOCALIZATION NOTE (spaceAlert.over5GB.message): %S = brandShortName
+spaceAlert.over5GB.message=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Preferences > Advanced > Site Data.
+# LOCALIZATION NOTE (spaceAlert.over5GB.messageWin):
+# - On Windows Preferences is called Options
+# - %S = brandShortName
+spaceAlert.over5GB.messageWin=%S is running out of disk space. Website contents may not display properly. You can clear stored site data in Options > Advanced > Site Data.
 spaceAlert.under5GB.okButton.label=OK, Got it
 spaceAlert.under5GB.okButton.accesskey=K
-spaceAlert.under5GB.description=Firefox is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.
+# LOCALIZATION NOTE (spaceAlert.under5GB.message): %S = brandShortName
+spaceAlert.under5GB.message=%S is running out of disk space. Website contents may not display properly. Visit “Learn More” to optimize your disk usage for better browsing experience.
 
 # LOCALIZATION NOTE (featureEnableRequiresRestart, featureDisableRequiresRestart, restartTitle): %S = brandShortName
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
 shouldRestartTitle=Restart %S
 okToRestartButton=Restart %S now
 revertNoRestartButton=Revert
 
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1238,29 +1238,29 @@ menuitem.panel-subview-footer@menuStateA
 
 .subviewbutton > .menu-accel-container {
   -moz-box-pack: start;
   margin-inline-start: 10px;
   margin-inline-end: auto;
   color: GrayText;
 }
 
-#PanelUI-remotetabs-tabslist > toolbarbutton,
+#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
 #PanelUI-historyItems > toolbarbutton {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 @media (min-resolution: 1.1dppx) {
-  #PanelUI-remotetabs-tabslist > toolbarbutton,
+  #PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"],
   #PanelUI-historyItems > toolbarbutton {
     list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
   }
 }
 
-#PanelUI-remotetabs-tabslist > toolbarbutton > .toolbarbutton-icon,
+#PanelUI-remotetabs-tabslist > toolbarbutton[itemtype="tab"] > .toolbarbutton-icon,
 #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
 toolbarbutton[panel-multiview-anchor="true"],
--- a/build/moz.configure/warnings.configure
+++ b/build/moz.configure/warnings.configure
@@ -73,19 +73,16 @@ check_and_add_gcc_warning('-Wimplicit-fa
 # --enable-warnings-as-errors is specified so that no unexpected fatal
 # warnings are produced.
 check_and_add_gcc_warning('-Werror=non-literal-null-conversion',
                           when='--enable-warnings-as-errors')
 
 # catches string literals used in boolean expressions
 check_and_add_gcc_warning('-Wstring-conversion')
 
-# catches inconsistent use of mutexes
-check_and_add_gcc_warning('-Wthread-safety')
-
 # we inline 'new' and 'delete' in mozalloc
 check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler)
 
 # Prevent the following GCC warnings from being treated as errors:
 # too many false positives
 check_and_add_gcc_warning('-Wno-error=maybe-uninitialized')
 
 # we don't want our builds held hostage when a platform-specific API
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -36,16 +36,17 @@ EXPORTS.mozilla = [
 SOURCES += [
     # Compile this separately since nsExceptionHandler.h conflicts
     # with something from nsNullPrincipal.cpp.
     'BasePrincipal.cpp',
 ]
 
 UNIFIED_SOURCES += [
     'DomainPolicy.cpp',
+    'nsExpandedPrincipal.cpp',
     'nsJSPrincipals.cpp',
     'nsNullPrincipal.cpp',
     'nsNullPrincipalURI.cpp',
     'nsPrincipal.cpp',
     'nsScriptSecurityManager.cpp',
     'nsSystemPrincipal.cpp',
 ]
 
copy from caps/nsPrincipal.cpp
copy to caps/nsExpandedPrincipal.cpp
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsExpandedPrincipal.cpp
@@ -1,471 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsPrincipal.h"
-
-#include "mozIThirdPartyUtil.h"
-#include "nscore.h"
-#include "nsScriptSecurityManager.h"
-#include "nsString.h"
-#include "nsReadableUtils.h"
-#include "pratom.h"
-#include "nsIURI.h"
-#include "nsIURL.h"
-#include "nsIStandardURL.h"
-#include "nsIURIWithPrincipal.h"
-#include "nsJSPrincipals.h"
-#include "nsIEffectiveTLDService.h"
+#include "nsExpandedPrincipal.h"
 #include "nsIClassInfoImpl.h"
-#include "nsIProtocolHandler.h"
-#include "nsError.h"
-#include "nsIContentSecurityPolicy.h"
-#include "nsNetCID.h"
-#include "jswrapper.h"
-
-#include "mozilla/dom/nsCSPContext.h"
-#include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/HashFunctions.h"
 
 using namespace mozilla;
 
-static bool gCodeBasePrincipalSupport = false;
-
-static bool URIIsImmutable(nsIURI* aURI)
-{
-  nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
-  bool isMutable;
-  return
-    mutableObj &&
-    NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
-    !isMutable;
-}
-
-NS_IMPL_CLASSINFO(nsPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
-                  NS_PRINCIPAL_CID)
-NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal,
-                           nsIPrincipal,
-                           nsISerializable)
-NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
-                            nsIPrincipal,
-                            nsISerializable)
-
-// Called at startup:
-/* static */ void
-nsPrincipal::InitializeStatics()
-{
-  Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
-                               "signed.applets.codebase_principal_support",
-                               false);
-}
-
-nsPrincipal::nsPrincipal()
-  : mCodebaseImmutable(false)
-  , mDomainImmutable(false)
-  , mInitialized(false)
-{ }
-
-nsPrincipal::~nsPrincipal()
-{
-  // let's clear the principal within the csp to avoid a tangling pointer
-  if (mCSP) {
-    static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
-  }
-}
-
-nsresult
-nsPrincipal::Init(nsIURI *aCodebase, const OriginAttributes& aOriginAttributes)
-{
-  NS_ENSURE_STATE(!mInitialized);
-  NS_ENSURE_ARG(aCodebase);
-
-  mInitialized = true;
-
-  mCodebase = NS_TryToMakeImmutable(aCodebase);
-  mCodebaseImmutable = URIIsImmutable(mCodebase);
-  mOriginAttributes = aOriginAttributes;
-
-  return NS_OK;
-}
-
-nsresult
-nsPrincipal::GetScriptLocation(nsACString &aStr)
-{
-  return mCodebase->GetSpec(aStr);
-}
-
-nsresult
-nsPrincipal::GetOriginInternal(nsACString& aOrigin)
-{
-  if (!mCodebase) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(mCodebase);
-  if (!origin) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAutoCString hostPort;
-
-  // chrome: URLs don't have a meaningful origin, so make
-  // sure we just get the full spec for them.
-  // XXX this should be removed in favor of the solution in
-  // bug 160042.
-  bool isChrome;
-  nsresult rv = origin->SchemeIs("chrome", &isChrome);
-  if (NS_SUCCEEDED(rv) && !isChrome) {
-    rv = origin->GetAsciiHostPort(hostPort);
-    // Some implementations return an empty string, treat it as no support
-    // for asciiHost by that implementation.
-    if (hostPort.IsEmpty()) {
-      rv = NS_ERROR_FAILURE;
-    }
-  }
-
-  // We want the invariant that prinA.origin == prinB.origin i.f.f.
-  // prinA.equals(prinB). However, this requires that we impose certain constraints
-  // on the behavior and origin semantics of principals, and in particular, forbid
-  // creating origin strings for principals whose equality constraints are not
-  // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs
-  // containing the magic "^" we use as a separating character for origin
-  // attributes.
-  //
-  // These constraints can generally be achieved by restricting .origin to
-  // nsIStandardURL-based URIs, but there are a few other URI schemes that we need
-  // to handle.
-  bool isBehaved;
-  if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) ||
-      (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) ||
-      (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
-    rv = origin->GetAsciiSpec(aOrigin);
-    NS_ENSURE_SUCCESS(rv, rv);
-    // These URIs could technically contain a '^', but they never should.
-    if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) {
-      aOrigin.Truncate();
-      return NS_ERROR_FAILURE;
-    }
-    return NS_OK;
-  }
-
-  if (NS_SUCCEEDED(rv) && !isChrome) {
-    rv = origin->GetScheme(aOrigin);
-    NS_ENSURE_SUCCESS(rv, rv);
-    aOrigin.AppendLiteral("://");
-    aOrigin.Append(hostPort);
-    return NS_OK;
-  }
-
-  // This URL can be a blobURL. In this case, we should use the 'parent'
-  // principal instead.
-  nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(origin);
-  if (uriWithPrincipal) {
-    nsCOMPtr<nsIPrincipal> uriPrincipal;
-    rv = uriWithPrincipal->GetPrincipal(getter_AddRefs(uriPrincipal));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (uriPrincipal) {
-      return uriPrincipal->GetOriginNoSuffix(aOrigin);
-    }
-  }
-
-  // If we reached this branch, we can only create an origin if we have a
-  // nsIStandardURL.  So, we query to a nsIStandardURL, and fail if we aren't
-  // an instance of an nsIStandardURL nsIStandardURLs have the good property
-  // of escaping the '^' character in their specs, which means that we can be
-  // sure that the caret character (which is reserved for delimiting the end
-  // of the spec, and the beginning of the origin attributes) is not present
-  // in the origin string
-  nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
-  NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE);
-
-  rv = origin->GetAsciiSpec(aOrigin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // The origin, when taken from the spec, should not contain the ref part of
-  // the URL.
-
-  int32_t pos = aOrigin.FindChar('?');
-  int32_t hashPos = aOrigin.FindChar('#');
-
-  if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
-    pos = hashPos;
-  }
-
-  if (pos != kNotFound) {
-    aOrigin.Truncate(pos);
-  }
-
-  return NS_OK;
-}
-
-bool
-nsPrincipal::SubsumesInternal(nsIPrincipal* aOther,
-                              BasePrincipal::DocumentDomainConsideration aConsideration)
-{
-  MOZ_ASSERT(aOther);
-
-  // For nsPrincipal, Subsumes is equivalent to Equals.
-  if (aOther == this) {
-    return true;
-  }
-
-  // If either the subject or the object has changed its principal by
-  // explicitly setting document.domain then the other must also have
-  // done so in order to be considered the same origin. This prevents
-  // DNS spoofing based on document.domain (154930)
-  nsresult rv;
-  if (aConsideration == ConsiderDocumentDomain) {
-    // Get .domain on each principal.
-    nsCOMPtr<nsIURI> thisDomain, otherDomain;
-    GetDomain(getter_AddRefs(thisDomain));
-    aOther->GetDomain(getter_AddRefs(otherDomain));
-
-    // If either has .domain set, we have equality i.f.f. the domains match.
-    // Otherwise, we fall through to the non-document-domain-considering case.
-    if (thisDomain || otherDomain) {
-      return nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
-    }
-  }
-
-  nsCOMPtr<nsIURI> otherURI;
-  rv = aOther->GetURI(getter_AddRefs(otherURI));
-  NS_ENSURE_SUCCESS(rv, false);
-
-  // Compare codebases.
-  return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI);
-}
-
-NS_IMETHODIMP
-nsPrincipal::GetURI(nsIURI** aURI)
-{
-  if (mCodebaseImmutable) {
-    NS_ADDREF(*aURI = mCodebase);
-    return NS_OK;
-  }
-
-  if (!mCodebase) {
-    *aURI = nullptr;
-    return NS_OK;
-  }
-
-  return NS_EnsureSafeToReturn(mCodebase, aURI);
-}
-
-bool
-nsPrincipal::MayLoadInternal(nsIURI* aURI)
-{
-  // See if aURI is something like a Blob URI that is actually associated with
-  // a principal.
-  nsCOMPtr<nsIURIWithPrincipal> uriWithPrin = do_QueryInterface(aURI);
-  nsCOMPtr<nsIPrincipal> uriPrin;
-  if (uriWithPrin) {
-    uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin));
-  }
-  if (uriPrin) {
-    return nsIPrincipal::Subsumes(uriPrin);
-  }
-
-  // If this principal is associated with an addon, check whether that addon
-  // has been given permission to load from this domain.
-  if (AddonAllowsLoad(aURI)) {
-    return true;
-  }
-
-  if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
-    return true;
-  }
-
-  // If strict file origin policy is in effect, local files will always fail
-  // SecurityCompareURIs unless they are identical. Explicitly check file origin
-  // policy, in that case.
-  if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
-      NS_URIIsLocalFile(aURI) &&
-      NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) {
-    return true;
-  }
-
-  return false;
-}
-
-void
-nsPrincipal::SetURI(nsIURI* aURI)
-{
-  mCodebase = NS_TryToMakeImmutable(aURI);
-  mCodebaseImmutable = URIIsImmutable(mCodebase);
-}
-
-NS_IMETHODIMP
-nsPrincipal::GetHashValue(uint32_t* aValue)
-{
-  NS_PRECONDITION(mCodebase, "Need a codebase");
-
-  *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPrincipal::GetDomain(nsIURI** aDomain)
-{
-  if (!mDomain) {
-    *aDomain = nullptr;
-    return NS_OK;
-  }
-
-  if (mDomainImmutable) {
-    NS_ADDREF(*aDomain = mDomain);
-    return NS_OK;
-  }
-
-  return NS_EnsureSafeToReturn(mDomain, aDomain);
-}
-
-NS_IMETHODIMP
-nsPrincipal::SetDomain(nsIURI* aDomain)
-{
-  mDomain = NS_TryToMakeImmutable(aDomain);
-  mDomainImmutable = URIIsImmutable(mDomain);
-
-  // Recompute all wrappers between compartments using this principal and other
-  // non-chrome compartments.
-  AutoSafeJSContext cx;
-  JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
-  bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
-                                       js::CompartmentsWithPrincipals(principals));
-  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
-  success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
-                                  js::ContentCompartmentsOnly());
-  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPrincipal::GetBaseDomain(nsACString& aBaseDomain)
-{
-  // For a file URI, we return the file path.
-  if (NS_URIIsLocalFile(mCodebase)) {
-    nsCOMPtr<nsIURL> url = do_QueryInterface(mCodebase);
-
-    if (url) {
-      return url->GetFilePath(aBaseDomain);
-    }
-  }
-
-  bool hasNoRelativeFlag;
-  nsresult rv = NS_URIChainHasFlags(mCodebase,
-                                    nsIProtocolHandler::URI_NORELATIVE,
-                                    &hasNoRelativeFlag);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (hasNoRelativeFlag) {
-    return mCodebase->GetSpec(aBaseDomain);
-  }
-
-  // For everything else, we ask the TLD service via
-  // the ThirdPartyUtil.
-  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-    do_GetService(THIRDPARTYUTIL_CONTRACTID);
-  if (thirdPartyUtil) {
-    return thirdPartyUtil->GetBaseDomain(mCodebase, aBaseDomain);
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPrincipal::Read(nsIObjectInputStream* aStream)
-{
-  nsCOMPtr<nsISupports> supports;
-  nsCOMPtr<nsIURI> codebase;
-  nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  codebase = do_QueryInterface(supports);
-
-  nsCOMPtr<nsIURI> domain;
-  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  domain = do_QueryInterface(supports);
-
-  nsAutoCString suffix;
-  rv = aStream->ReadCString(suffix);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  OriginAttributes attrs;
-  bool ok = attrs.PopulateFromSuffix(suffix);
-  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
-
-  rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = Init(codebase, attrs);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mCSP = do_QueryInterface(supports, &rv);
-  // make sure setRequestContext is called after Init(),
-  // to make sure  the principals URI been initalized.
-  if (mCSP) {
-    mCSP->SetRequestContext(nullptr, this);
-  }
-
-  SetDomain(domain);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsPrincipal::Write(nsIObjectOutputStream* aStream)
-{
-  NS_ENSURE_STATE(mCodebase);
-  nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
-                                               true);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
-                                      true);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsAutoCString suffix;
-  OriginAttributesRef().CreateSuffix(suffix);
-
-  rv = aStream->WriteStringZ(suffix.get());
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = NS_WriteOptionalCompoundObject(aStream, mCSP,
-                                      NS_GET_IID(nsIContentSecurityPolicy),
-                                      true);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // mCodebaseImmutable and mDomainImmutable will be recomputed based
-  // on the deserialized URIs in Read().
-
-  return NS_OK;
-}
-
-/************************************************************************************************************************/
-
 NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_EXPANDEDPRINCIPAL_CID)
 NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
                            nsIPrincipal,
                            nsIExpandedPrincipal)
 NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
                              nsIPrincipal,
                              nsIExpandedPrincipal)
copy from caps/nsPrincipal.h
copy to caps/nsExpandedPrincipal.h
--- a/caps/nsPrincipal.h
+++ b/caps/nsExpandedPrincipal.h
@@ -1,69 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef nsPrincipal_h__
-#define nsPrincipal_h__
+#ifndef nsExpandedPrincipal_h
+#define nsExpandedPrincipal_h
 
 #include "nsCOMPtr.h"
 #include "nsJSPrincipals.h"
 #include "nsTArray.h"
-#include "nsIContentSecurityPolicy.h"
-#include "nsIProtocolHandler.h"
 #include "nsNetUtil.h"
-#include "nsScriptSecurityManager.h"
 #include "mozilla/BasePrincipal.h"
 
-class nsPrincipal final : public mozilla::BasePrincipal
-{
-public:
-  NS_DECL_NSISERIALIZABLE
-  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
-  NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
-  NS_IMETHOD GetURI(nsIURI** aURI) override;
-  NS_IMETHOD GetDomain(nsIURI** aDomain) override;
-  NS_IMETHOD SetDomain(nsIURI* aDomain) override;
-  NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
-  bool IsCodebasePrincipal() const override { return true; }
-  nsresult GetOriginInternal(nsACString& aOrigin) override;
-
-  nsPrincipal();
-
-  // Init() must be called before the principal is in a usable state.
-  nsresult Init(nsIURI* aCodebase,
-                const mozilla::OriginAttributes& aOriginAttributes);
-
-  virtual nsresult GetScriptLocation(nsACString& aStr) override;
-  void SetURI(nsIURI* aURI);
-
-  /**
-   * Called at startup to setup static data, e.g. about:config pref-observers.
-   */
-  static void InitializeStatics();
-
-  PrincipalKind Kind() override { return eCodebasePrincipal; }
-
-  nsCOMPtr<nsIURI> mDomain;
-  nsCOMPtr<nsIURI> mCodebase;
-  // If mCodebaseImmutable is true, mCodebase is non-null and immutable
-  bool mCodebaseImmutable;
-  bool mDomainImmutable;
-  bool mInitialized;
-
-protected:
-  virtual ~nsPrincipal();
-
-  bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
-  bool MayLoadInternal(nsIURI* aURI) override;
-};
-
-class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal
+class nsExpandedPrincipal : public nsIExpandedPrincipal
+                          , public mozilla::BasePrincipal
 {
 public:
   nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
                       const mozilla::OriginAttributes& aAttrs);
 
   NS_DECL_NSIEXPANDEDPRINCIPAL
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
@@ -85,19 +40,14 @@ protected:
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
   bool MayLoadInternal(nsIURI* aURI) override;
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
 };
 
-#define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
-#define NS_PRINCIPAL_CID \
-{ 0x653e0e4d, 0x3ee4, 0x45fa, \
-  { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } }
-
 #define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
 #define NS_EXPANDEDPRINCIPAL_CID \
 { 0xe8ee88b0, 0x5571, 0x4086, \
   { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
 
-#endif // nsPrincipal_h__
+#endif // nsExpandedPrincipal_h
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -6,19 +6,39 @@
 /* Defines the abstract interface for a principal. */
 
 #include "nsISerializable.idl"
 
 %{C++
 struct JSPrincipals;
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
+#include "mozilla/DebugOnly.h"
 namespace mozilla {
 class OriginAttributes;
 }
+
+/**
+ * Some methods have a fast path for the case when we're comparing a principal
+ * to itself. The situation may happen for example with about:blank documents.
+ */
+
+#define DECL_FAST_INLINE_HELPER(method_)                       \
+  inline bool method_(nsIPrincipal* aOther)                    \
+  {                                                            \
+    mozilla::DebugOnly<bool> val = false;                      \
+    MOZ_ASSERT_IF(this == aOther,                              \
+                  NS_SUCCEEDED(method_(aOther, &val)) && val); \
+                                                               \
+    bool retVal = false;                                       \
+    return                                                     \
+      this == aOther ||                                        \
+      (NS_SUCCEEDED(method_(aOther, &retVal)) && retVal);      \
+  }
+
 %}
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 interface nsIDOMDocument;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
@@ -36,25 +56,18 @@ interface nsIPrincipal : nsISerializable
     boolean equals(in nsIPrincipal other);
 
     /**
      * Like equals, but takes document.domain changes into account.
      */
     boolean equalsConsideringDomain(in nsIPrincipal other);
 
     %{C++
-    inline bool Equals(nsIPrincipal* aOther) {
-      bool equal = false;
-      return NS_SUCCEEDED(Equals(aOther, &equal)) && equal;
-    }
-
-    inline bool EqualsConsideringDomain(nsIPrincipal* aOther) {
-      bool equal = false;
-      return NS_SUCCEEDED(EqualsConsideringDomain(aOther, &equal)) && equal;
-    }
+      DECL_FAST_INLINE_HELPER(Equals)
+      DECL_FAST_INLINE_HELPER(EqualsConsideringDomain)
     %}
 
     /**
      * Returns a hash value for the principal.
      */
     [noscript] readonly attribute unsigned long hashValue;
 
     /**
@@ -96,30 +109,20 @@ interface nsIPrincipal : nsISerializable
 
     /**
      * Same as the subsumesConsideringDomain(), but ignores the first party
      * domain in its originAttributes.
      */
     boolean subsumesConsideringDomainIgnoringFPD(in nsIPrincipal other);
 
     %{C++
-    inline bool Subsumes(nsIPrincipal* aOther) {
-      bool subsumes = false;
-      return NS_SUCCEEDED(Subsumes(aOther, &subsumes)) && subsumes;
-    }
-
-    inline bool SubsumesConsideringDomain(nsIPrincipal* aOther) {
-      bool subsumes = false;
-      return NS_SUCCEEDED(SubsumesConsideringDomain(aOther, &subsumes)) && subsumes;
-    }
-
-    inline bool SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther) {
-      bool subsumes = false;
-      return NS_SUCCEEDED(SubsumesConsideringDomainIgnoringFPD(aOther, &subsumes)) && subsumes;
-    }
+      DECL_FAST_INLINE_HELPER(Subsumes)
+      DECL_FAST_INLINE_HELPER(SubsumesConsideringDomain)
+      DECL_FAST_INLINE_HELPER(SubsumesConsideringDomainIgnoringFPD)
+#undef DECL_FAST_INLINE_HELPER
     %}
 
     /**
      * Checks whether this principal is allowed to load the network resource
      * located at the given URI under the same-origin policy. This means that
      * codebase principals are only allowed to load resources from the same
      * domain, the system principal is allowed to load anything, and null
      * principals can only load URIs where they are the principal. This is
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -453,208 +453,8 @@ nsPrincipal::Write(nsIObjectOutputStream
     return rv;
   }
 
   // mCodebaseImmutable and mDomainImmutable will be recomputed based
   // on the deserialized URIs in Read().
 
   return NS_OK;
 }
-
-/************************************************************************************************************************/
-
-NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
-                  NS_EXPANDEDPRINCIPAL_CID)
-NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
-                           nsIPrincipal,
-                           nsIExpandedPrincipal)
-NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
-                             nsIPrincipal,
-                             nsIExpandedPrincipal)
-
-struct OriginComparator
-{
-  bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const
-  {
-    nsAutoCString originA;
-    nsresult rv = a->GetOrigin(originA);
-    NS_ENSURE_SUCCESS(rv, false);
-    nsAutoCString originB;
-    rv = b->GetOrigin(originB);
-    NS_ENSURE_SUCCESS(rv, false);
-    return originA < originB;
-  }
-
-  bool Equals(nsIPrincipal* a, nsIPrincipal* b) const
-  {
-    nsAutoCString originA;
-    nsresult rv = a->GetOrigin(originA);
-    NS_ENSURE_SUCCESS(rv, false);
-    nsAutoCString originB;
-    rv = b->GetOrigin(originB);
-    NS_ENSURE_SUCCESS(rv, false);
-    return a == b;
-  }
-};
-
-nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
-                                         const OriginAttributes& aAttrs)
-{
-  // We force the principals to be sorted by origin so that nsExpandedPrincipal
-  // origins can have a canonical form.
-  OriginComparator c;
-  for (size_t i = 0; i < aWhiteList.Length(); ++i) {
-    mPrincipals.InsertElementSorted(aWhiteList[i], c);
-  }
-  mOriginAttributes = aAttrs;
-}
-
-nsExpandedPrincipal::~nsExpandedPrincipal()
-{ }
-
-NS_IMETHODIMP
-nsExpandedPrincipal::GetDomain(nsIURI** aDomain)
-{
-  *aDomain = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::SetDomain(nsIURI* aDomain)
-{
-  return NS_OK;
-}
-
-nsresult
-nsExpandedPrincipal::GetOriginInternal(nsACString& aOrigin)
-{
-  aOrigin.AssignLiteral("[Expanded Principal [");
-  for (size_t i = 0; i < mPrincipals.Length(); ++i) {
-    if (i != 0) {
-      aOrigin.AppendLiteral(", ");
-    }
-
-    nsAutoCString subOrigin;
-    nsresult rv = mPrincipals.ElementAt(i)->GetOrigin(subOrigin);
-    NS_ENSURE_SUCCESS(rv, rv);
-    aOrigin.Append(subOrigin);
-  }
-
-  aOrigin.Append("]]");
-  return NS_OK;
-}
-
-bool
-nsExpandedPrincipal::SubsumesInternal(nsIPrincipal* aOther,
-                                      BasePrincipal::DocumentDomainConsideration aConsideration)
-{
-  // If aOther is an ExpandedPrincipal too, we break it down into its component
-  // nsIPrincipals, and check subsumes on each one.
-  nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aOther);
-  if (expanded) {
-    nsTArray< nsCOMPtr<nsIPrincipal> >* otherList;
-    expanded->GetWhiteList(&otherList);
-    for (uint32_t i = 0; i < otherList->Length(); ++i){
-      // Use SubsumesInternal rather than Subsumes here, since OriginAttribute
-      // checks are only done between non-expanded sub-principals, and we don't
-      // need to incur the extra virtual call overhead.
-      if (!SubsumesInternal((*otherList)[i], aConsideration)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  // We're dealing with a regular principal. One of our principals must subsume
-  // it.
-  for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
-    if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-bool
-nsExpandedPrincipal::MayLoadInternal(nsIURI* uri)
-{
-  for (uint32_t i = 0; i < mPrincipals.Length(); ++i){
-    if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::GetHashValue(uint32_t* result)
-{
-  MOZ_CRASH("extended principal should never be used as key in a hash map");
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::GetURI(nsIURI** aURI)
-{
-  *aURI = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
-{
-  *aWhiteList = &mPrincipals;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
-{
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-bool
-nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
-{
-  for (size_t i = 0; i < mPrincipals.Length(); ++i) {
-    if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-nsresult
-nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
-{
-  aStr.Assign("[Expanded Principal [");
-  for (size_t i = 0; i < mPrincipals.Length(); ++i) {
-    if (i != 0) {
-      aStr.AppendLiteral(", ");
-    }
-
-    nsAutoCString spec;
-    nsresult rv =
-      nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    aStr.Append(spec);
-  }
-  aStr.Append("]]");
-  return NS_OK;
-}
-
-//////////////////////////////////////////
-// Methods implementing nsISerializable //
-//////////////////////////////////////////
-
-NS_IMETHODIMP
-nsExpandedPrincipal::Read(nsIObjectInputStream* aStream)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
-nsExpandedPrincipal::Write(nsIObjectOutputStream* aStream)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
--- a/caps/nsPrincipal.h
+++ b/caps/nsPrincipal.h
@@ -53,51 +53,14 @@ public:
 
 protected:
   virtual ~nsPrincipal();
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
   bool MayLoadInternal(nsIURI* aURI) override;
 };
 
-class nsExpandedPrincipal : public nsIExpandedPrincipal, public mozilla::BasePrincipal
-{
-public:
-  nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
-                      const mozilla::OriginAttributes& aAttrs);
-
-  NS_DECL_NSIEXPANDEDPRINCIPAL
-  NS_DECL_NSISERIALIZABLE
-  NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
-  NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
-  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
-  NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
-  NS_IMETHOD GetURI(nsIURI** aURI) override;
-  NS_IMETHOD GetDomain(nsIURI** aDomain) override;
-  NS_IMETHOD SetDomain(nsIURI* aDomain) override;
-  NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
-  virtual bool AddonHasPermission(const nsAString& aPerm) override;
-  virtual nsresult GetScriptLocation(nsACString &aStr) override;
-  nsresult GetOriginInternal(nsACString& aOrigin) override;
-
-  PrincipalKind Kind() override { return eExpandedPrincipal; }
-
-protected:
-  virtual ~nsExpandedPrincipal();
-
-  bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
-  bool MayLoadInternal(nsIURI* aURI) override;
-
-private:
-  nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
-};
-
 #define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
 #define NS_PRINCIPAL_CID \
 { 0x653e0e4d, 0x3ee4, 0x45fa, \
   { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } }
 
-#define NS_EXPANDEDPRINCIPAL_CONTRACTID "@mozilla.org/expandedprincipal;1"
-#define NS_EXPANDEDPRINCIPAL_CID \
-{ 0xe8ee88b0, 0x5571, 0x4086, \
-  { 0xa4, 0x5b, 0x39, 0xa7, 0x16, 0x90, 0x6b, 0xdb } }
-
 #endif // nsPrincipal_h__
--- a/devtools/client/framework/target-from-url.js
+++ b/devtools/client/framework/target-from-url.js
@@ -1,45 +1,48 @@
 /* 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 { Cu, Ci } = require("chrome");
-
 const { TargetFactory } = require("devtools/client/framework/target");
 const { DebuggerServer } = require("devtools/server/main");
 const { DebuggerClient } = require("devtools/shared/client/main");
 const { Task } = require("devtools/shared/task");
 
 /**
  * Construct a Target for a given URL object having various query parameters:
  *
  * host:
  *    {String} The hostname or IP address to connect to.
  * port:
  *    {Number} The TCP port to connect to, to use with `host` argument.
  * ws:
  *    {Boolean} If true, connect via websocket instread of regular TCP connection.
  *
- * type: tab, process
- *    {String} The type of target to connect to.  Currently tabs and processes are supported types.
+ * type: tab, process, window
+ *    {String} The type of target to connect to.
  *
- * If type="tab":
+ * If type == "tab":
  * id:
  *    {Number} the tab outerWindowID
  * chrome: Optional
- *    {Boolean} Force the creation of a chrome target. Gives more privileges to the tab
- *    actor. Allows chrome execution in the webconsole and see chrome files in
- *    the debugger. (handy when contributing to firefox)
+ *    {Boolean} Force the creation of a chrome target. Gives more privileges to
+ *    the tab actor. Allows chrome execution in the webconsole and see chrome
+ *    files in the debugger. (handy when contributing to firefox)
  *
- * If type="process":
+ * If type == "process":
  * id:
- *    {Number} the process id to debug. Default to 0, which is the parent process.
+ *    {Number} the process id to debug. Default to 0, which is the parent
+ *    process.
+ *
+ * If type == "window":
+ * id:
+ *    {Number} the window outerWindowID
  *
  * @param {URL} url
  *        The url to fetch query params from.
  *
  * @return A target object
  */
 exports.targetFromURL = Task.async(function* (url) {
   let params = url.searchParams;
@@ -88,16 +91,36 @@ exports.targetFromURL = Task.async(funct
         isTabActor = false;
       }
     } catch (ex) {
       if (ex.error == "noProcess") {
         throw new Error("targetFromURL, process with id:'" + id + "' doesn't exist");
       }
       throw ex;
     }
+  } else if (type == "window") {
+    // Fetch target for a remote window actor
+    DebuggerServer.allowChromeProcess = true;
+    try {
+      id = parseInt(id, 10);
+      if (isNaN(id)) {
+        throw new Error("targetFromURL, window requires id parameter");
+      }
+      let response = yield client.mainRoot.getWindow({
+        outerWindowID: id,
+      });
+      form = response.window;
+      chrome = true;
+    } catch (ex) {
+      if (ex.error == "notFound") {
+        throw new Error(`targetFromURL, window with id:'${id}' ` +
+                        "doesn't exist");
+      }
+      throw ex;
+    }
   } else {
     throw new Error("targetFromURL, unsupported type='" + type + "' parameter");
   }
 
   return TargetFactory.forRemoteTab({ client, form, chrome, isTabActor });
 });
 
 function* createClient(params) {
--- a/devtools/client/framework/test/browser_target_from_url.js
+++ b/devtools/client/framework/test/browser_target_from_url.js
@@ -31,18 +31,29 @@ add_task(function* () {
   info("Test invalid type");
   try {
     yield targetFromURL(new URL("http://foo?type=x"));
     ok(false, "Shouldn't pass");
   } catch (e) {
     is(e.message, "targetFromURL, unsupported type='x' parameter");
   }
 
+  info("Test browser window");
+  let windowId = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils)
+                       .outerWindowID;
+  target = yield targetFromURL(new URL("http://foo?type=window&id=" + windowId));
+  is(target.url, window.location.href);
+  is(target.isLocalTab, false);
+  is(target.chrome, true);
+  is(target.isTabActor, true);
+  is(target.isRemote, true);
+
   info("Test tab");
-  let windowId = browser.outerWindowID;
+  windowId = browser.outerWindowID;
   target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId));
   assertIsTabTarget(target, TEST_URI);
 
   info("Test tab with chrome privileges");
   target = yield targetFromURL(new URL("http://foo?type=tab&id=" + windowId + "&chrome"));
   assertIsTabTarget(target, TEST_URI, true);
 
   info("Test invalid tab id");
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -107,26 +107,28 @@ registerCleanupFunction(function* cleanu
 
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url The url to be loaded in the new tab
  * @param {Object} options Object with various optional fields:
  *   - {Boolean} background If true, open the tab in background
  *   - {ChromeWindow} window Firefox top level window we should use to open the tab
  *   - {Number} userContextId The userContextId of the tab.
+ *   - {String} preferredRemoteType
  * @return a promise that resolves to the tab object when the url is loaded
  */
 var addTab = Task.async(function* (url, options = { background: false, window: window }) {
   info("Adding a new tab with URL: " + url);
 
   let { background } = options;
   let { gBrowser } = options.window ? options.window : window;
   let { userContextId } = options;
 
-  let tab = gBrowser.addTab(url, {userContextId});
+  let tab = gBrowser.addTab(url,
+    {userContextId, preferredRemoteType: options.preferredRemoteType});
   if (!background) {
     gBrowser.selectedTab = tab;
   }
   yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("Tab added and finished loading");
 
   return tab;
--- a/devtools/client/framework/toolbox-init.js
+++ b/devtools/client/framework/toolbox-init.js
@@ -1,12 +1,14 @@
 /* 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/. */
 
+/* eslint-env browser */
+
 "use strict";
 
 // URL constructor doesn't support about: scheme
 let href = window.location.href.replace("about:", "http://");
 let url = new window.URL(href);
 
 // Only use this method to attach the toolbox if some query parameters are given
 if (url.search.length > 1) {
@@ -21,16 +23,33 @@ if (url.search.length > 1) {
   const { DebuggerClient } = require("devtools/shared/client/main");
   const { Task } = require("devtools/shared/task");
 
   // `host` is the frame element loading the toolbox.
   let host = window.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindowUtils)
                    .containerElement;
 
+  // If there's no containerElement (which happens when loading about:devtools-toolbox as
+  // a top level document), use the current window.
+  if (!host) {
+    host = {
+      contentWindow: window,
+      contentDocument: document,
+      // toolbox-host-manager.js wants to set attributes on the frame that contains it,
+      // but that is fine to skip and doesn't make sense when using the current window.
+      setAttribute() {},
+      ownerDocument: document,
+      // toolbox-host-manager.js wants to listen for unload events from outside the frame,
+      // but this is fine to skip since the toolbox code listens inside the frame as well,
+      // and there is no outer document in this case.
+      addEventListener() {},
+    };
+  }
+
   // Specify the default tool to open
   let tool = url.searchParams.get("tool");
 
   Task.spawn(function* () {
     let target;
     if (url.searchParams.has("target")) {
       // Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
       // mozbrowser>) whose reference is set on the host iframe.
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1285,33 +1285,33 @@ Inspector.prototype = {
       label: INSPECTOR_L10N.getStr("inspectorAddAttribute.label"),
       accesskey: INSPECTOR_L10N.getStr("inspectorAddAttribute.accesskey"),
       disabled: !isEditableElement,
       click: () => this.onAddAttribute(),
     }));
     attributesSubmenu.append(new MenuItem({
       id: "node-menu-copy-attribute",
       label: INSPECTOR_L10N.getFormatStr("inspectorCopyAttributeValue.label",
-                                        isAttributeClicked ? `"${nodeInfo.value}"` : ""),
+                                        isAttributeClicked ? `${nodeInfo.value}` : ""),
       accesskey: INSPECTOR_L10N.getStr("inspectorCopyAttributeValue.accesskey"),
       disabled: !isAttributeClicked,
       click: () => this.onCopyAttributeValue(),
     }));
     attributesSubmenu.append(new MenuItem({
       id: "node-menu-edit-attribute",
       label: INSPECTOR_L10N.getFormatStr("inspectorEditAttribute.label",
-                                        isAttributeClicked ? `"${nodeInfo.name}"` : ""),
+                                        isAttributeClicked ? `${nodeInfo.name}` : ""),
       accesskey: INSPECTOR_L10N.getStr("inspectorEditAttribute.accesskey"),
       disabled: !isAttributeClicked,
       click: () => this.onEditAttribute(),
     }));
     attributesSubmenu.append(new MenuItem({
       id: "node-menu-remove-attribute",
       label: INSPECTOR_L10N.getFormatStr("inspectorRemoveAttribute.label",
-                                        isAttributeClicked ? `"${nodeInfo.name}"` : ""),
+                                        isAttributeClicked ? `${nodeInfo.name}` : ""),
       accesskey: INSPECTOR_L10N.getStr("inspectorRemoveAttribute.accesskey"),
       disabled: !isAttributeClicked,
       click: () => this.onRemoveAttribute(),
     }));
 
     return attributesSubmenu;
   },
 
--- a/devtools/client/inspector/layout/actions/grids.js
+++ b/devtools/client/inspector/layout/actions/grids.js
@@ -1,34 +1,51 @@
 /* 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 {
+  UPDATE_GRID_COLOR,
   UPDATE_GRID_HIGHLIGHTED,
   UPDATE_GRIDS,
 } = require("./index");
 
 module.exports = {
 
   /**
+   * Update the color used for the grid's highlighter.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront of the DOM node to toggle the grid highlighter.
+   * @param  {String} color
+   *         The color to use for thie nodeFront's grid highlighter.
+   */
+  updateGridColor(nodeFront, color) {
+    return {
+      type: UPDATE_GRID_COLOR,
+      color,
+      nodeFront,
+    };
+  },
+
+  /**
    * Update the grid highlighted state.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront of the DOM node to toggle the grid highlighter.
    * @param  {Boolean} highlighted
    *         Whether or not the grid highlighter is highlighting the grid.
    */
   updateGridHighlighted(nodeFront, highlighted) {
     return {
       type: UPDATE_GRID_HIGHLIGHTED,
+      highlighted,
       nodeFront,
-      highlighted,
     };
   },
 
   /**
    * Update the grid state with the new list of grids.
    */
   updateGrids(grids) {
     return {
--- a/devtools/client/inspector/layout/actions/index.js
+++ b/devtools/client/inspector/layout/actions/index.js
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createEnum } = require("devtools/client/shared/enum");
 
 createEnum([
 
+  // Update the color used for the overlay of a grid.
+  "UPDATE_GRID_COLOR",
+
   // Update the grid highlighted state.
   "UPDATE_GRID_HIGHLIGHTED",
 
   // Update the entire grids state with the new list of grids.
   "UPDATE_GRIDS",
 
   // Update the layout state with the latest layout properties.
   "UPDATE_LAYOUT",
--- a/devtools/client/inspector/layout/components/Accordion.css
+++ b/devtools/client/inspector/layout/components/Accordion.css
@@ -19,17 +19,17 @@
   font-size: 11px;
   padding: 5px;
   transition: all 0.25s ease;
   width: 100%;
   -moz-user-select: none;
 }
 
 .accordion ._header:hover {
-  background-color: var(--theme-selection-color);
+  background-color: var(--theme-toolbar-hover);
 }
 
 .accordion ._header:hover svg {
   fill: var(--theme-comment-alt);
 }
 
 .accordion ._content {
   border-bottom: 1px solid var(--theme-splitter-color);
--- a/devtools/client/inspector/layout/components/App.js
+++ b/devtools/client/inspector/layout/components/App.js
@@ -21,21 +21,23 @@ const BOXMODEL_STRINGS_URI = "devtools/c
 const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
 
 const App = createClass({
 
   displayName: "App",
 
   propTypes: {
     boxModel: PropTypes.shape(Types.boxModel).isRequired,
+    getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
     showBoxModelProperties: PropTypes.bool.isRequired,
+    onHideBoxModelHighlighter: PropTypes.func.isRequired,
+    onSetGridOverlayColor: PropTypes.func.isRequired,
     onShowBoxModelEditor: PropTypes.func.isRequired,
-    onHideBoxModelHighlighter: PropTypes.func.isRequired,
     onShowBoxModelHighlighter: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
--- a/devtools/client/inspector/layout/components/Grid.js
+++ b/devtools/client/inspector/layout/components/Grid.js
@@ -13,41 +13,47 @@ const GridList = createFactory(require("
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
 module.exports = createClass({
 
   displayName: "Grid",
 
   propTypes: {
+    getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     highlighterSettings: PropTypes.shape(Types.highlighterSettings).isRequired,
+    onSetGridOverlayColor: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
     onToggleShowGridLineNumbers: PropTypes.func.isRequired,
     onToggleShowInfiniteLines: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   render() {
     let {
+      getSwatchColorPickerTooltip,
       grids,
       highlighterSettings,
+      onSetGridOverlayColor,
       onToggleGridHighlighter,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
     } = this.props;
 
     return grids.length ?
       dom.div(
         {
           id: "layout-grid-container",
         },
         GridList({
+          getSwatchColorPickerTooltip,
           grids,
+          onSetGridOverlayColor,
           onToggleGridHighlighter,
         }),
         GridDisplaySettings({
           highlighterSettings,
           onToggleShowGridLineNumbers,
           onToggleShowInfiniteLines,
         })
       )
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/layout/components/GridItem.js
@@ -0,0 +1,118 @@
+/* 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 { addons, createClass, DOM: dom, PropTypes } = require("devtools/client/shared/vendor/react");
+const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+
+const Types = require("../types");
+
+module.exports = createClass({
+
+  displayName: "GridItem",
+
+  propTypes: {
+    getSwatchColorPickerTooltip: PropTypes.func.isRequired,
+    grid: PropTypes.shape(Types.grid).isRequired,
+    onSetGridOverlayColor: PropTypes.func.isRequired,
+    onToggleGridHighlighter: PropTypes.func.isRequired,
+  },
+
+  mixins: [ addons.PureRenderMixin ],
+
+  componentDidMount() {
+    let tooltip = this.props.getSwatchColorPickerTooltip();
+    let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
+
+    let previousColor;
+    tooltip.addSwatch(swatchEl, {
+      onCommit: this.setGridColor,
+      onPreview: this.setGridColor,
+      onRevert: () => {
+        this.props.onSetGridOverlayColor(this.props.grid.nodeFront, previousColor);
+      },
+      onShow: () => {
+        previousColor = this.props.grid.color;
+      },
+    });
+  },
+
+  componentWillUnmount() {
+    let tooltip = this.props.getSwatchColorPickerTooltip();
+    let swatchEl = findDOMNode(this).querySelector(".grid-color-swatch");
+    tooltip.removeSwatch(swatchEl);
+  },
+
+  setGridColor() {
+    let color = findDOMNode(this).querySelector(".grid-color-value").textContent;
+    this.props.onSetGridOverlayColor(this.props.grid.nodeFront, color);
+  },
+
+  onGridCheckboxClick() {
+    let {
+      grid,
+      onToggleGridHighlighter,
+    } = this.props;
+
+    onToggleGridHighlighter(grid.nodeFront);
+  },
+
+  render() {
+    let { grid } = this.props;
+    let { nodeFront } = grid;
+    let { displayName, attributes } = nodeFront;
+
+    let gridName = displayName;
+
+    let idIndex = attributes.findIndex(({ name }) => name === "id");
+    if (idIndex > -1 && attributes[idIndex].value) {
+      gridName += "#" + attributes[idIndex].value;
+    }
+
+    let classIndex = attributes.findIndex(({name}) => name === "class");
+    if (classIndex > -1 && attributes[classIndex].value) {
+      gridName += "." + attributes[classIndex].value.split(" ").join(".");
+    }
+
+    return dom.li(
+      {
+        key: grid.id,
+        className: "grid-item",
+      },
+      dom.label(
+        {},
+        dom.input(
+          {
+            type: "checkbox",
+            value: grid.id,
+            checked: grid.highlighted,
+            onChange: this.onGridCheckboxClick,
+          }
+        ),
+        gridName
+      ),
+      dom.div(
+        {
+          className: "grid-color-swatch",
+          style: {
+            backgroundColor: grid.color,
+          },
+          title: grid.color,
+        }
+      ),
+      // The SwatchColorPicker relies on the nextSibling of the swatch element to apply
+      // the selected color. This is why we use a span in display: none for now.
+      // Ideally we should modify the SwatchColorPickerTooltip to bypass this requirement.
+      // See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
+      dom.span(
+        {
+          className: "grid-color-value"
+        },
+        grid.color
+      )
+    );
+  },
+
+});
--- a/devtools/client/inspector/layout/components/GridList.js
+++ b/devtools/client/inspector/layout/components/GridList.js
@@ -1,87 +1,56 @@
 /* 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 { addons, createClass, DOM: dom, PropTypes } =
+const { addons, createClass, createFactory, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
+const GridItem = createFactory(require("./GridItem"));
+
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
 module.exports = createClass({
 
   displayName: "GridList",
 
   propTypes: {
+    getSwatchColorPickerTooltip: PropTypes.func.isRequired,
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
+    onSetGridOverlayColor: PropTypes.func.isRequired,
     onToggleGridHighlighter: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
-  onGridCheckboxClick({ target }) {
-    let {
-      grids,
-      onToggleGridHighlighter,
-    } = this.props;
-
-    onToggleGridHighlighter(grids[target.value].nodeFront);
-  },
-
   render() {
     let {
+      getSwatchColorPickerTooltip,
       grids,
+      onSetGridOverlayColor,
+      onToggleGridHighlighter,
     } = this.props;
 
     return dom.div(
       {
         className: "grid-container",
       },
       dom.span(
         {},
         getStr("layout.overlayGrid")
       ),
       dom.ul(
         {},
-        grids.map(grid => {
-          let { nodeFront } = grid;
-          let { displayName, attributes } = nodeFront;
-
-          let gridName = displayName;
-
-          let idIndex = attributes.findIndex(({ name }) => name === "id");
-          if (idIndex > -1 && attributes[idIndex].value) {
-            gridName += "#" + attributes[idIndex].value;
-          }
-
-          let classIndex = attributes.findIndex(({name}) => name === "class");
-          if (classIndex > -1 && attributes[classIndex].value) {
-            gridName += "." + attributes[classIndex].value.split(" ").join(".");
-          }
-
-          return dom.li(
-            {
-              key: grid.id,
-            },
-            dom.label(
-              {},
-              dom.input(
-                {
-                  type: "checkbox",
-                  value: grid.id,
-                  checked: grid.highlighted,
-                  onChange: this.onGridCheckboxClick,
-                }
-              ),
-              gridName
-            )
-          );
-        })
+        grids.map(grid => GridItem({
+          getSwatchColorPickerTooltip,
+          grid,
+          onSetGridOverlayColor,
+          onToggleGridHighlighter,
+        }))
       )
     );
   },
 
 });
-
--- a/devtools/client/inspector/layout/components/moz.build
+++ b/devtools/client/inspector/layout/components/moz.build
@@ -11,10 +11,11 @@ DevToolsModules(
     'BoxModel.js',
     'BoxModelEditable.js',
     'BoxModelInfo.js',
     'BoxModelMain.js',
     'BoxModelProperties.js',
     'ComputedProperty.js',
     'Grid.js',
     'GridDisplaySettings.js',
+    'GridItem.js',
     'GridList.js',
 )
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -8,20 +8,23 @@ const Services = require("Services");
 const { Task } = require("devtools/shared/task");
 const { getCssProperties } = require("devtools/shared/fronts/css-properties");
 const { ReflowFront } = require("devtools/shared/fronts/reflow");
 
 const { InplaceEditor } = require("devtools/client/shared/inplace-editor");
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
+const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
+
 const {
   updateLayout,
 } = require("./actions/box-model");
 const {
+  updateGridColor,
   updateGridHighlighted,
   updateGrids,
 } = require("./actions/grids");
 const {
   updateShowGridLineNumbers,
   updateShowInfiniteLines,
 } = require("./actions/highlighter-settings");
 
@@ -32,36 +35,43 @@ const EditingSession = require("./utils/
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 const NUMERIC = /^-?[\d\.]+$/;
 const SHOW_GRID_LINE_NUMBERS = "devtools.gridinspector.showGridLineNumbers";
 const SHOW_INFINITE_LINES_PREF = "devtools.gridinspector.showInfiniteLines";
 
+// Default grid colors.
+const GRID_COLORS = [
+  "#05E4EE",
+  "#BB9DFF",
+  "#FFB53B",
+  "#71F362",
+  "#FF90FF",
+  "#FF90FF",
+  "#1B80FF",
+  "#FF2647"
+];
+
 function LayoutView(inspector, window) {
   this.document = window.document;
   this.highlighters = inspector.highlighters;
   this.inspector = inspector;
   this.store = inspector.store;
   this.walker = this.inspector.walker;
 
   this.updateBoxModel = this.updateBoxModel.bind(this);
 
   this.onGridLayoutChange = this.onGridLayoutChange.bind(this);
   this.onHighlighterChange = this.onHighlighterChange.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
 
   this.init();
-
-  this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
-  this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
-  this.inspector.selection.on("new-node-front", this.onNewSelection);
-  this.inspector.sidebar.on("select", this.onSidebarSelect);
 }
 
 LayoutView.prototype = {
 
   /**
    * Initializes the layout view by fetching the LayoutFront from the walker, creating
    * the redux store and adding the view into the inspector sidebar.
    */
@@ -69,32 +79,76 @@ LayoutView.prototype = {
     if (!this.inspector) {
       return;
     }
 
     this.layoutInspector = yield this.inspector.walker.getLayoutInspector();
 
     this.loadHighlighterSettings();
 
+    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
+    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
+    this.inspector.selection.on("new-node-front", this.onNewSelection);
+    this.inspector.sidebar.on("select", this.onSidebarSelect);
+
+    // Create a shared SwatchColorPicker instance to be reused by all GridItem components.
+    this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
+      this.inspector.toolbox.doc,
+      this.inspector,
+      {
+        supportsCssColor4ColorFunction: () => false
+      }
+    );
+
     let app = App({
       /**
+       * Retrieve the shared SwatchColorPicker instance.
+       */
+      getSwatchColorPickerTooltip: () => {
+        return this.swatchColorPickerTooltip;
+      },
+
+      /**
        * Shows the box model properties under the box model if true, otherwise, hidden by
        * default.
        */
       showBoxModelProperties: true,
 
       /**
        * Hides the box-model highlighter on the currently selected element.
        */
       onHideBoxModelHighlighter: () => {
         let toolbox = this.inspector.toolbox;
         toolbox.highlighterUtils.unhighlight();
       },
 
       /**
+       * Handler for a change in the grid overlay color picker for a grid container.
+       *
+       * @param  {NodeFront} node
+       *         The NodeFront of the grid container element for which the grid color is
+       *         being updated.
+       * @param  {String} color
+       *         A hex string representing the color to use.
+       */
+      onSetGridOverlayColor: (node, color) => {
+        this.store.dispatch(updateGridColor(node, color));
+        let { grids } = this.store.getState();
+
+        // If the grid for which the color was updated currently has a highlighter, update
+        // the color.
+        for (let grid of grids) {
+          if (grid.nodeFront === node && grid.highlighted) {
+            let highlighterSettings = this.getGridHighlighterSettings(node);
+            this.highlighters.showGridHighlighter(node, highlighterSettings);
+          }
+        }
+      },
+
+      /**
        * Shows the inplace editor when a box model editable value is clicked on the
        * box model panel.
        *
        * @param  {DOMNode} element
        *         The element that was clicked.
        * @param  {Event} event
        *         The event object.
        * @param  {String} property
@@ -175,37 +229,38 @@ LayoutView.prototype = {
        * Handler for a change in the input checkboxes in the GridList component.
        * Toggles on/off the grid highlighter for the provided grid container element.
        *
        * @param  {NodeFront} node
        *         The NodeFront of the grid container element for which the grid
        *         highlighter is toggled on/off for.
        */
       onToggleGridHighlighter: node => {
-        let { highlighterSettings } = this.store.getState();
+        let highlighterSettings = this.getGridHighlighterSettings(node);
         this.highlighters.toggleGridHighlighter(node, highlighterSettings);
       },
 
       /**
        * Handler for a change in the show grid line numbers checkbox in the
-       * GridDisplaySettings component. TOggles on/off the option to show the grid line
+       * GridDisplaySettings component. Toggles on/off the option to show the grid line
        * numbers in the grid highlighter. Refreshes the shown grid highlighter for the
        * grids currently highlighted.
        *
        * @param  {Boolean} enabled
        *         Whether or not the grid highlighter should show the grid line numbers.
        */
       onToggleShowGridLineNumbers: enabled => {
         this.store.dispatch(updateShowGridLineNumbers(enabled));
         Services.prefs.setBoolPref(SHOW_GRID_LINE_NUMBERS, enabled);
 
-        let { grids, highlighterSettings } = this.store.getState();
+        let { grids } = this.store.getState();
 
         for (let grid of grids) {
           if (grid.highlighted) {
+            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
             this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
           }
         }
       },
 
       /**
        * Handler for a change in the extend grid lines infinitely checkbox in the
        * GridDisplaySettings component. Toggles on/off the option to extend the grid
@@ -214,24 +269,25 @@ LayoutView.prototype = {
        *
        * @param  {Boolean} enabled
        *         Whether or not the grid highlighter should extend grid lines infinitely.
        */
       onToggleShowInfiniteLines: enabled => {
         this.store.dispatch(updateShowInfiniteLines(enabled));
         Services.prefs.setBoolPref(SHOW_INFINITE_LINES_PREF, enabled);
 
-        let { grids, highlighterSettings } = this.store.getState();
+        let { grids } = this.store.getState();
 
         for (let grid of grids) {
           if (grid.highlighted) {
+            let highlighterSettings = this.getGridHighlighterSettings(grid.nodeFront);
             this.highlighters.showGridHighlighter(grid.nodeFront, highlighterSettings);
           }
         }
-      },
+      }
     });
 
     let provider = createElement(Provider, {
       store: this.store,
       id: "layoutview",
       title: INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2"),
       key: "layoutview",
     }, app);
@@ -266,16 +322,53 @@ LayoutView.prototype = {
     this.document = null;
     this.inspector = null;
     this.layoutInspector = null;
     this.store = null;
     this.walker = null;
   },
 
   /**
+   * Returns the color set for the grid highlighter associated with the provided
+   * nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need the color.
+   */
+  getGridColorForNodeFront(nodeFront) {
+    let { grids } = this.store.getState();
+
+    for (let grid of grids) {
+      if (grid.nodeFront === nodeFront) {
+        return grid.color;
+      }
+    }
+
+    return null;
+  },
+
+  /**
+   * Create a highlighter settings object for the provided nodeFront.
+   *
+   * @param  {NodeFront} nodeFront
+   *         The NodeFront for which we need highlighter settings.
+   */
+  getGridHighlighterSettings(nodeFront) {
+    let { highlighterSettings } = this.store.getState();
+
+    // Get the grid color for the provided nodeFront.
+    let color = this.getGridColorForNodeFront(nodeFront);
+
+    // Merge the grid color to the generic highlighter settings.
+    return Object.assign({}, highlighterSettings, {
+      color
+    });
+  },
+
+  /**
    * Returns true if the layout panel is visible, and false otherwise.
    */
   isPanelVisible() {
     return this.inspector.toolbox.currentToolId === "inspector" &&
            this.inspector.sidebar &&
            this.inspector.sidebar.getCurrentTabID() === "layoutview";
   },
 
@@ -386,18 +479,22 @@ LayoutView.prototype = {
       gridFronts = yield this.layoutInspector.getAllGrids(this.walker.rootNode);
     }
 
     let grids = [];
     for (let i = 0; i < gridFronts.length; i++) {
       let grid = gridFronts[i];
       let nodeFront = yield this.walker.getNodeFromActor(grid.actorID, ["containerEl"]);
 
+      let fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
+      let color = this.getGridColorForNodeFront(nodeFront) || fallbackColor;
+
       grids.push({
         id: i,
+        color,
         gridFragments: grid.gridFragments,
         highlighted: nodeFront == this.highlighters.gridHighlighterShown,
         nodeFront,
       });
     }
 
     this.store.dispatch(updateGrids(grids));
   }),
--- a/devtools/client/inspector/layout/reducers/grids.js
+++ b/devtools/client/inspector/layout/reducers/grids.js
@@ -1,37 +1,44 @@
 /* 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 {
+  UPDATE_GRID_COLOR,
   UPDATE_GRID_HIGHLIGHTED,
   UPDATE_GRIDS,
 } = require("../actions/index");
 
 const INITIAL_GRIDS = [];
 
 let reducers = {
 
-  [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) {
+  [UPDATE_GRID_COLOR](grids, { nodeFront, color }) {
     let newGrids = grids.map(g => {
       if (g.nodeFront == nodeFront) {
-        g.highlighted = highlighted;
-      } else {
-        g.highlighted = false;
+        g.color = color;
       }
 
       return g;
     });
 
     return newGrids;
   },
 
+  [UPDATE_GRID_HIGHLIGHTED](grids, { nodeFront, highlighted }) {
+    return grids.map(g => {
+      return Object.assign({}, g, {
+        highlighted: g.nodeFront === nodeFront ? highlighted : false
+      });
+    });
+  },
+
   [UPDATE_GRIDS](_, { grids }) {
     return grids;
   },
 
 };
 
 module.exports = function (grids = INITIAL_GRIDS, action) {
   let reducer = reducers[action.type];
--- a/devtools/client/inspector/layout/types.js
+++ b/devtools/client/inspector/layout/types.js
@@ -19,16 +19,19 @@ exports.boxModel = {
 /**
  * A single grid container in the document.
  */
 exports.grid = {
 
   // The id of the grid
   id: PropTypes.number,
 
+  // The color for the grid overlay highlighter
+  color: PropTypes.string,
+
   // The grid fragment object of the grid container
   gridFragments: PropTypes.array,
 
   // Whether or not the grid highlighter is highlighting the grid
   highlighted: PropTypes.bool,
 
   // The node front of the grid container
   nodeFront: PropTypes.object,
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -102,31 +102,31 @@ inspector.menu.copyUrlToClipboard.label=
 # element in the DOM (like with <label for="input-id">), and that allows to
 # select that element in the inspector.
 inspector.menu.selectElement.label=Select Element #%S
 
 # LOCALIZATION NOTE (inspectorEditAttribute.label): This is the label of a
 # sub-menu "Attribute" in the inspector contextual-menu that appears
 # when the user right-clicks on the node in the inspector, and that allows
 # to edit an attribute on this node.
-inspectorEditAttribute.label=Edit Attribute %S
+inspectorEditAttribute.label=Edit Attribute “%S”
 inspectorEditAttribute.accesskey=E
 
 # LOCALIZATION NOTE (inspectorRemoveAttribute.label): This is the label of a
 # sub-menu "Attribute" in the inspector contextual-menu that appears
 # when the user right-clicks on the attribute of a node in the inspector,
 # and that allows to remove this attribute.
-inspectorRemoveAttribute.label=Remove Attribute %S
+inspectorRemoveAttribute.label=Remove Attribute “%S”
 inspectorRemoveAttribute.accesskey=R
 
 # LOCALIZATION NOTE (inspectorCopyAttributeValue.label): This is the label of a
 # sub-menu "Attribute" in the inspector contextual-menu that appears
 # when the user right-clicks on the attribute of a node in the inspector,
 # and that allows to copy the attribute value to clipboard.
-inspectorCopyAttributeValue.label=Copy Attribute Value %S
+inspectorCopyAttributeValue.label=Copy Attribute Value “%S”
 inspectorCopyAttributeValue.accesskey=V
 
 # LOCALIZATION NOTE (inspector.nodePreview.selectNodeLabel):
 # This string is displayed in a tooltip that is shown when hovering over a DOM
 # node preview (e.g. something like "div#foo.bar").
 # DOM node previews can be displayed in places like the animation-inspector, the
 # console or the object inspector.
 # The tooltip invites the user to click on the node in order to select it in the
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -16,17 +16,17 @@
   --viewport-selection-arrow-hovered:
     url("./images/select-arrow.svg#light-hovered");
   --viewport-selection-arrow-selected:
     url("./images/select-arrow.svg#light-selected");
 }
 
 .theme-dark {
   --rdm-box-shadow: 0 4px 4px 0 rgba(105, 105, 105, 0.26);
-  --submit-button-active-background-color: var(--toolbar-tab-hover-active);
+  --submit-button-active-background-color: var(--theme-toolbar-hover-active);
   --submit-button-active-color: var(--theme-selection-color);
   --viewport-color: #c6ccd0;
   --viewport-hover-color: #dde1e4;
   --viewport-active-color: #fcfcfc;
   --viewport-selection-arrow: url("./images/select-arrow.svg#dark");
   --viewport-selection-arrow-hovered:
     url("./images/select-arrow.svg#dark-hovered");
   --viewport-selection-arrow-selected:
@@ -531,17 +531,17 @@ select > option.divider {
   color: var(--theme-body-color);
   width: 100%;
   height: 20px;
   position: absolute;
   bottom: 0;
 }
 
 #device-submit-button:hover {
-  background-color: var(--toolbar-tab-hover);
+  background-color: var(--theme-toolbar-hover);
 }
 
 #device-submit-button:hover:active {
   background-color: var(--submit-button-active-background-color);
   color: var(--submit-button-active-color);
 }
 
 /**
--- a/devtools/client/shared/components/tabs/tabs.css
+++ b/devtools/client/shared/components/tabs/tabs.css
@@ -90,22 +90,22 @@
 .theme-dark .tabs .tabs-menu-item a,
 .theme-light .tabs .tabs-menu-item a {
   color: inherit;
   padding: 3px 15px;
 }
 
 .theme-dark .tabs .tabs-menu-item:hover:not(.is-active),
 .theme-light .tabs .tabs-menu-item:hover:not(.is-active) {
-  background-color: var(--toolbar-tab-hover);
+  background-color: var(--theme-toolbar-hover);
 }
 
 .theme-dark .tabs .tabs-menu-item:hover:active:not(.is-active),
 .theme-light .tabs .tabs-menu-item:hover:active:not(.is-active) {
-  background-color: var(--toolbar-tab-hover-active);
+  background-color: var(--theme-toolbar-hover-active);
 }
 
 .theme-dark .tabs .tabs-menu-item.is-active,
 .theme-light .tabs .tabs-menu-item.is-active {
   background-color: var(--theme-selection-background);
   color: var(--theme-selection-color);
 }
 
@@ -160,17 +160,17 @@
 .theme-firebug .tabs .tabs-menu-item.is-active a {
   background-color: var(--theme-toolbar-tab-selected-background);
   border: 1px solid var(--theme-splitter-color);
   border-bottom-color: transparent;
   color: var(--theme-body-color);
 }
 
 .theme-firebug .tabs .tabs-menu-item:hover:active a {
-  background-color: var(--toolbar-tab-hover-active);
+  background-color: var(--theme-toolbar-hover-active);
 }
 
 .theme-firebug .tabs .tabs-menu-item.is-active:hover:active a {
   background-color: var(--theme-selection-background);
   color: var(--theme-selection-color);
 }
 
 .theme-firebug .tabs .tabs-menu-item a {
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -47,8 +47,34 @@
  * Container when no grids are present
  */
 
 .layout-no-grids {
   font-style: italic;
   text-align: center;
   padding: 0.5em;
 }
+
+/**
+ * Grid Item
+ */
+
+.grid-item {
+  display: flex;
+  align-items: center;
+}
+
+.grid-item input {
+  margin: 0 5px;
+}
+
+.grid-color-swatch {
+  width: 12px;
+  height: 12px;
+  margin-left: 5px;
+  border: 1px solid var(--theme-highlight-gray);
+  border-radius: 50%;
+  cursor: pointer;
+}
+
+.grid-color-value {
+  display: none;
+}
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -1,31 +1,27 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* CSS Variables specific to the devtools toolbar that aren't defined by the themes */
 .theme-light {
-  --toolbar-tab-hover: rgba(170, 170, 170, .2);
-  --toolbar-tab-hover-active: rgba(170, 170, 170, .4);
   --searchbox-background-color: #ffee99;
   --searchbox-border-color: #ffbf00;
   --searcbox-no-match-background-color: #ffe5e5;
   --searcbox-no-match-border-color: #e52e2e;
   --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
   --filter-image: url(chrome://devtools/skin/images/filter.svg);
   --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);
   --icon-filter: none;
   --checked-icon-filter: url(chrome://devtools/skin/images/filters.svg#checked-icon-state);
 }
 
 .theme-dark {
-  --toolbar-tab-hover: rgba(110,120,130,0.1);
-  --toolbar-tab-hover-active: rgba(110,120,130,0.2);
   --searchbox-background-color: #4d4222;
   --searchbox-border-color: #d99f2b;
   --searcbox-no-match-background-color: #402325;
   --searcbox-no-match-border-color: #cc3d3d;
   --magnifying-glass-image: url(chrome://devtools/skin/images/search.svg);
   --filter-image: url(chrome://devtools/skin/images/filter.svg);
   --tool-options-image: url(chrome://devtools/skin/images/tool-options.svg);
   --icon-filter: invert(1);
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -129,25 +129,25 @@
   border-color: var(--theme-splitter-color);
 }
 
 .theme-dark .devtools-tab:hover {
   color: #ced3d9;
 }
 
 .devtools-tab:hover {
-  background-color: var(--toolbar-tab-hover);
+  background-color: var(--theme-toolbar-hover);
 }
 
 .theme-dark .devtools-tab:hover:active {
   color: var(--theme-selection-color);
 }
 
 .devtools-tab:hover:active {
-  background-color: var(--toolbar-tab-hover-active);
+  background-color: var(--theme-toolbar-hover-active);
 }
 
 .theme-dark .devtools-tab:not(.selected).highlighted {
   background-color: hsla(99, 100%, 14%, .3);
 }
 
 .theme-light .devtools-tab:not(.selected).highlighted {
   background-color: rgba(44, 187, 15, .2);
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -16,16 +16,18 @@
 
 :root.theme-light {
   --theme-body-background: white;
   --theme-sidebar-background: white;
   --theme-contrast-background: #e6b064;
 
   --theme-tab-toolbar-background: #fcfcfc;
   --theme-toolbar-background: #fcfcfc;
+  --theme-toolbar-hover: rgba(170, 170, 170, .2);
+  --theme-toolbar-hover-active: rgba(170, 170, 170, .4);
   --theme-selection-background: #4c9ed9;
   --theme-selection-background-semitransparent: rgba(76, 158, 217, 0.15);
   --theme-selection-color: #f5f7fa;
   --theme-splitter-color: #dde1e4;
   --theme-comment: #696969;
 
   --theme-body-color: #393f4c;
   --theme-body-color-alt: #585959;
@@ -76,16 +78,18 @@
 
 :root.theme-dark {
   --theme-body-background: #393f4c;
   --theme-sidebar-background: #393f4c;
   --theme-contrast-background: #ffb35b;
 
   --theme-tab-toolbar-background: #272b35;
   --theme-toolbar-background: #272b35;
+  --theme-toolbar-hover: rgba(110, 120, 130, 0.1);
+  --theme-toolbar-hover-active: rgba(110, 120, 130, 0.2);
   --theme-selection-background: #5675B9;
   --theme-selection-background-semitransparent: rgba(86, 117, 185, 0.5);
   --theme-selection-color: #f5f7fa;
   --theme-splitter-color: #454d5d;
   --theme-comment: #757873;
 
   --theme-body-color: #8fa1b2;
   --theme-body-color-alt: #b6babf;
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -42,44 +42,27 @@ var WCUL10n = new WebConsoleUtils.L10n(W
 
 const DOCS_GA_PARAMS = "?utm_source=mozilla" +
                        "&utm_medium=firefox-console-errors" +
                        "&utm_campaign=default";
 
 flags.testing = true;
 
 function loadTab(url, preferredRemoteType) {
-  let deferred = promise.defer();
-
-  let tab = gBrowser.selectedTab = gBrowser.addTab(url, { preferredRemoteType });
-  let browser = gBrowser.getBrowserForTab(tab);
-
-  browser.addEventListener("load", function () {
-    deferred.resolve({tab: tab, browser: browser});
-  }, {capture: true, once: true});
-
-  return deferred.promise;
+  return addTab(url, { preferredRemoteType }).then( tab => {
+    return { tab, browser: tab.linkedBrowser };
+  });
 }
 
 function loadBrowser(browser) {
   return BrowserTestUtils.browserLoaded(browser);
 }
 
 function closeTab(tab) {
-  let deferred = promise.defer();
-
-  let container = gBrowser.tabContainer;
-
-  container.addEventListener("TabClose", function () {
-    deferred.resolve(null);
-  }, {capture: true, once: true});
-
-  gBrowser.removeTab(tab);
-
-  return deferred.promise;
+  return removeTab(tab);
 }
 
 /**
  * Load the page and return the associated HUD.
  *
  * @param string uri
  *   The URI of the page to load.
  * @param string consoleType [optional]
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -16,38 +16,40 @@ const {
 const {
   getCurrentZoom,
   setIgnoreLayoutChanges,
   getWindowDimensions
 } = require("devtools/shared/layout/utils");
 const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
+const DEFAULT_GRID_COLOR = "#4B0082";
 const ROWS = "rows";
 const COLUMNS = "cols";
+
 const GRID_LINES_PROPERTIES = {
   "edge": {
     lineDash: [0, 0],
-    strokeStyle: "#4B0082"
+    alpha: 1,
   },
   "explicit": {
     lineDash: [5, 3],
-    strokeStyle: "#8A2BE2"
+    alpha: 0.75,
   },
   "implicit": {
     lineDash: [2, 2],
-    strokeStyle: "#9370DB"
+    alpha: 0.5,
   }
 };
 
 // px
 const GRID_GAP_PATTERN_WIDTH = 14;
 const GRID_GAP_PATTERN_HEIGHT = 14;
 const GRID_GAP_PATTERN_LINE_DASH = [5, 3];
-const GRID_GAP_PATTERN_STROKE_STYLE = "#9370DB";
+const GRID_GAP_ALPHA = 0.5;
 
 /**
  * Cached used by `CssGridHighlighter.getGridGapPattern`.
  */
 const gCachedGridPattern = new WeakMap();
 // WeakMap key for the Row grid pattern.
 const ROW_KEY = {};
 // WeakMap key for the Column grid pattern.
@@ -59,16 +61,19 @@ const COLUMN_KEY = {};
  *
  * Usage example:
  * let h = new CssGridHighlighter(env);
  * h.show(node, options);
  * h.hide();
  * h.destroy();
  *
  * Available Options:
+ * - color(colorValue)
+ *     @param  {String} colorValue
+ *     The color that should be used to draw the highlighter for this grid.
  * - showGridArea(areaName)
  *     @param  {String} areaName
  *     Shows the grid area highlight for the given area name.
  * - showAllGridAreas
  *     Shows all the grid area highlights for the current grid.
  * - showGridLineNumbers(isShown)
  *     @param  {Boolean}
  *     Displays the grid line numbers on the grid lines if isShown is true.
@@ -246,16 +251,20 @@ CssGridHighlighter.prototype = extend(Au
   get ctx() {
     return this.canvas.getCanvasContext("2d");
   },
 
   get canvas() {
     return this.getElement("canvas");
   },
 
+  get color() {
+    return this.options.color || DEFAULT_GRID_COLOR;
+  },
+
   /**
    * Gets the grid gap pattern used to render the gap regions.
    *
    * @param  {Object} dimension
    *         Refers to the WeakMap key for the grid dimension type which is either the
    *         constant COLUMN or ROW.
    * @return {CanvasPattern} grid gap pattern.
    */
@@ -265,60 +274,70 @@ CssGridHighlighter.prototype = extend(Au
     }
 
     // Create the diagonal lines pattern for the rendering the grid gaps.
     let canvas = createNode(this.win, { nodeType: "canvas" });
     canvas.width = GRID_GAP_PATTERN_WIDTH;
     canvas.height = GRID_GAP_PATTERN_HEIGHT;
 
     let ctx = canvas.getContext("2d");
+    ctx.save();
     ctx.setLineDash(GRID_GAP_PATTERN_LINE_DASH);
     ctx.beginPath();
     ctx.translate(.5, .5);
 
     if (dimension === COLUMN_KEY) {
       ctx.moveTo(0, 0);
       ctx.lineTo(GRID_GAP_PATTERN_WIDTH, GRID_GAP_PATTERN_HEIGHT);
     } else {
       ctx.moveTo(GRID_GAP_PATTERN_WIDTH, 0);
       ctx.lineTo(0, GRID_GAP_PATTERN_HEIGHT);
     }
 
-    ctx.strokeStyle = GRID_GAP_PATTERN_STROKE_STYLE;
+    ctx.strokeStyle = this.color;
+    ctx.globalAlpha = GRID_GAP_ALPHA;
     ctx.stroke();
+    ctx.restore();
 
     let pattern = ctx.createPattern(canvas, "repeat");
     gCachedGridPattern.set(dimension, pattern);
     return pattern;
   },
 
   /**
    * Called when the page navigates. Used to clear the cached gap patterns and avoid
    * using DeadWrapper objects as gap patterns the next time.
    */
   onNavigate() {
-    gCachedGridPattern.delete(ROW_KEY);
-    gCachedGridPattern.delete(COLUMN_KEY);
+    this._clearCache();
   },
 
   onWillNavigate({ isTopLevel }) {
     if (isTopLevel) {
       this.hide();
     }
   },
 
   _show() {
     if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
       this.hide();
       return false;
     }
 
+    // The grid pattern cache should be cleared in case the color changed.
+    this._clearCache();
+
     return this._update();
   },
 
+  _clearCache() {
+    gCachedGridPattern.delete(ROW_KEY);
+    gCachedGridPattern.delete(COLUMN_KEY);
+  },
+
   /**
    * Shows the grid area highlight for the given area name.
    *
    * @param  {String} areaName
    *         Grid area name.
    */
   showGridArea(areaName) {
     this.renderGridArea(areaName);
@@ -605,17 +624,19 @@ CssGridHighlighter.prototype = extend(Au
     if (dimensionType === COLUMNS) {
       this.ctx.moveTo(linePos, startPos);
       this.ctx.lineTo(linePos, endPos);
     } else {
       this.ctx.moveTo(startPos, linePos);
       this.ctx.lineTo(endPos, linePos);
     }
 
-    this.ctx.strokeStyle = GRID_LINES_PROPERTIES[lineType].strokeStyle;
+    this.ctx.strokeStyle = this.color;
+    this.ctx.globalAlpha = GRID_LINES_PROPERTIES[lineType].alpha;
+
     this.ctx.stroke();
     this.ctx.restore();
   },
 
   /**
    * Render the grid line number on the css grid highlighter canvas.
    *
    * @param  {Number} lineNumber
@@ -626,21 +647,26 @@ CssGridHighlighter.prototype = extend(Au
    * @param  {Number} startPos
    *         The start position of the cross side of the grid line.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
    */
   renderGridLineNumber(lineNumber, linePos, startPos, dimensionType) {
     this.ctx.save();
 
+    let textWidth = this.ctx.measureText(lineNumber).width;
+    // Guess the font height based on the measured width
+    let textHeight = textWidth * 2;
+
     if (dimensionType === COLUMNS) {
-      this.ctx.fillText(lineNumber, linePos, startPos);
+      let yPos = Math.max(startPos, textHeight);
+      this.ctx.fillText(lineNumber, linePos, yPos);
     } else {
-      let textWidth = this.ctx.measureText(lineNumber).width;
-      this.ctx.fillText(lineNumber, startPos - textWidth, linePos);
+      let xPos = Math.max(startPos, textWidth);
+      this.ctx.fillText(lineNumber, xPos - textWidth, linePos);
     }
 
     this.ctx.restore();
   },
 
   /**
    * Render the grid gap area on the css grid highlighter canvas.
    *
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -61,16 +61,17 @@ DevToolsModules(
     'tab.js',
     'timeline.js',
     'webaudio.js',
     'webbrowser.js',
     'webconsole.js',
     'webextension-inspected-window.js',
     'webextension.js',
     'webgl.js',
+    'window.js',
     'worker-list.js',
     'worker.js',
 )
 
 with Files('animation.js'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Animation Inspector')
 
 with Files('breakpoint.js'):
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -2,23 +2,26 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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 { Cc, Ci, Cu } = require("chrome");
+const Services = require("Services");
 const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 
 loader.lazyGetter(this, "ppmm", () => {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(
     Ci.nsIMessageBroadcaster);
 });
+loader.lazyRequireGetter(this, "WindowActor",
+  "devtools/server/actors/window", true);
 
 /* Root actor for the remote debugging protocol. */
 
 /**
  * Create a remote debugging protocol root actor.
  *
  * @param connection
  *     The DebuggerServerConnection whose root actor we are constructing.
@@ -162,22 +165,25 @@ RootActor.prototype = {
     // grabbing allocations from the MemoryActor are available for the performance tool
     memoryActorAllocations: true,
     // Added in Gecko 40, indicating that the backend isn't stupid about
     // sending resumption packets on tab navigation.
     noNeedToFakeResumptionOnNavigation: true,
     // Added in Firefox 40. Indicates that the backend supports registering custom
     // commands through the WebConsoleCommands API.
     webConsoleCommands: true,
-    // Whether root actor exposes tab actors
-    // if allowChromeProcess is true, you can fetch a ChromeActor instance
-    // to debug chrome and any non-content ressource via getProcess request
-    // if allocChromeProcess is defined, but not true, it means that root actor
-    // no longer expose tab actors, but also that getProcess forbids
-    // exposing actors for security reasons
+    // Whether root actor exposes tab actors and access to any window.
+    // If allowChromeProcess is true, you can:
+    // * get a ChromeActor instance to debug chrome and any non-content
+    //   resource via getProcess requests
+    // * get a WindowActor instance to debug windows which could be chrome,
+    //   like browser windows via getWindow requests
+    // If allowChromeProcess is defined, but not true, it means that root actor
+    // no longer expose tab actors, but also that the above requests are
+    // forbidden for security reasons.
     get allowChromeProcess() {
       return DebuggerServer.allowChromeProcess;
     },
     // Whether or not `getProfile()` supports specifying a `startTime`
     // and `endTime` to filter out samples. Fx40+
     profilerDataFilterable: true,
     // Whether or not the MemoryActor's heap snapshot abilities are
     // fully equipped to handle heap snapshots for the memory tool. Fx44+
@@ -227,16 +233,17 @@ RootActor.prototype = {
     }
     if (typeof this._parameters.onShutdown === "function") {
       this._parameters.onShutdown();
     }
     this._extraActors = null;
     this.conn = null;
     this._tabActorPool = null;
     this._globalActorPool = null;
+    this._windowActorPool = null;
     this._parameters = null;
     this._chromeActor = null;
     this._processActors.clear();
   },
 
   /* The 'listTabs' request and the 'tabListChanged' notification. */
 
   /**
@@ -333,16 +340,48 @@ RootActor.prototype = {
                     }
                     return {
                       error: "noTab",
                       message: "Unexpected error while calling getTab(): " + error
                     };
                   });
   },
 
+  onGetWindow: function ({ outerWindowID }) {
+    if (!DebuggerServer.allowChromeProcess) {
+      return {
+        from: this.actorID,
+        error: "forbidden",
+        message: "You are not allowed to debug windows."
+      };
+    }
+    let window = Services.wm.getOuterWindowWithId(outerWindowID);
+    if (!window) {
+      return {
+        from: this.actorID,
+        error: "notFound",
+        message: `No window found with outerWindowID ${outerWindowID}`,
+      };
+    }
+
+    if (!this._windowActorPool) {
+      this._windowActorPool = new ActorPool(this.conn);
+      this.conn.addActorPool(this._windowActorPool);
+    }
+
+    let actor = new WindowActor(this.conn, window);
+    actor.parentID = this.actorID;
+    this._windowActorPool.addActor(actor);
+
+    return {
+      from: this.actorID,
+      window: actor.form(),
+    };
+  },
+
   onTabListChanged: function () {
     this.conn.send({ from: this.actorID, type: "tabListChanged" });
     /* It's a one-shot notification; no need to watch any more. */
     this._parameters.tabList.onListChanged = null;
   },
 
   onListAddons: function () {
     let addonList = this._parameters.addonList;
@@ -534,16 +573,17 @@ RootActor.prototype = {
       delete this._extraActors[name];
     }
   }
 };
 
 RootActor.prototype.requestTypes = {
   "listTabs": RootActor.prototype.onListTabs,
   "getTab": RootActor.prototype.onGetTab,
+  "getWindow": RootActor.prototype.onGetWindow,
   "listAddons": RootActor.prototype.onListAddons,
   "listWorkers": RootActor.prototype.onListWorkers,
   "listServiceWorkerRegistrations": RootActor.prototype.onListServiceWorkerRegistrations,
   "listProcesses": RootActor.prototype.onListProcesses,
   "getProcess": RootActor.prototype.onGetProcess,
   "echo": RootActor.prototype.onEcho,
   "protocolDescription": RootActor.prototype.onProtocolDescription
 };
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/window.js
@@ -0,0 +1,83 @@
+/* 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 { Ci } = require("chrome");
+const Services = require("Services");
+const { TabActor } = require("./tab");
+
+/**
+ * Creates a WindowActor for debugging a single window, like a browser window in Firefox,
+ * but it can be used to reach any window in the process.  (Currently this is parent
+ * process only because the root actor's `onGetWindow` doesn't try to cross process
+ * boundaries.)  Both chrome and content windows are supported.
+ *
+ * Most of the implementation is inherited from TabActor.  WindowActor exposes all tab
+ * actors via its form() request, like TabActor.
+ *
+ * You can request a specific window's actor via RootActor.getWindow().
+ *
+ * @param connection DebuggerServerConnection
+ *        The connection to the client.
+ * @param window DOMWindow
+ *        The window.
+ */
+function WindowActor(connection, window) {
+  TabActor.call(this, connection);
+
+  let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDocShell);
+  Object.defineProperty(this, "docShell", {
+    value: docShell,
+    configurable: true
+  });
+}
+
+WindowActor.prototype = Object.create(TabActor.prototype);
+
+// Bug 1266561: This setting is mysteriously named, we should split up the
+// functionality that is triggered by it.
+WindowActor.prototype.isRootActor = true;
+
+WindowActor.prototype.observe = function (subject, topic, data) {
+  TabActor.prototype.observe.call(this, subject, topic, data);
+  if (!this.attached) {
+    return;
+  }
+  if (topic == "chrome-webnavigation-destroy") {
+    this._onDocShellDestroy(subject);
+  }
+};
+
+WindowActor.prototype._attach = function () {
+  if (this.attached) {
+    return false;
+  }
+
+  TabActor.prototype._attach.call(this);
+
+  // Listen for chrome docshells in addition to content docshells
+  if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
+    Services.obs.addObserver(this, "chrome-webnavigation-destroy", false);
+  }
+
+  return true;
+};
+
+WindowActor.prototype._detach = function () {
+  if (!this.attached) {
+    return false;
+  }
+
+  if (this.docShell.itemType == Ci.nsIDocShellTreeItem.typeChrome) {
+    Services.obs.removeObserver(this, "chrome-webnavigation-destroy");
+  }
+
+  TabActor.prototype._detach.call(this);
+
+  return true;
+};
+
+exports.WindowActor = WindowActor;
--- a/devtools/server/docs/actor-hierarchy.md
+++ b/devtools/server/docs/actor-hierarchy.md
@@ -2,17 +2,18 @@
 
 To start with, actors are living within /devtools/server/actors/ folder.
 They are organized in a hierarchy for easier lifecycle/memory management:
 once a parent is removed from the pool, its children are removed as well.
 (See actor-registration.md for more information about how to implement one)
 
 The overall hierarchy of actors looks like this:
 
-  RootActor: First one, automatically instantiated when we start connecting.
+```
+RootActor: First one, automatically instantiated when we start connecting.
    |         Mostly meant to instantiate new actors.
    |
    |--> Global-scoped actors:
    |    Actors exposing features related to the main process,
    |    that are not specific to any particular context (document, tab, app,
    |    add-on, or worker).
    |    A good example is the preference actor.
    |
@@ -22,26 +23,28 @@ The overall hierarchy of actors looks li
           |    one of these for each thing you can point a toolbox at.
           |
           \--> Tab-scoped actors:
                Actors exposing one particular feature set, this time,
                specific to a given context (document, tab, app, add-on, or
                worker).  Examples include the console and inspector actors.
                These actors may extend this hierarchy by having their
                own children, like LongStringActor, WalkerActor, etc.
+```
 
 ## RootActor
 
 The root actor is special. It is automatically created when a client connects.
 It has a special `actorID` which is unique and is "root".
 All other actors have an `actorID` which is computed dynamically,
 so that you need to ask an existing actor to create an Actor
 and returns its `actorID`. That's the main role of RootActor.
 
-  RootActor (root.js)
+```
+RootActor (root.js)
    |
    |-- BrowserTabActor (webbrowser.js)
    |   Targets tabs living in the parent or child process. Note that this is
    |   just a proxy for ContentActor, which is loaded via the tab's message
    |   manager as a frame script in the process containing the tab. This proxy
    |   via message manager is always used, even when e10s is disabled.
    |   Returned by "listTabs" or "getTab" requests.
    |   |
@@ -55,29 +58,35 @@ and returns its `actorID`. That's the ma
    |   Targets a worker (applies to various kinds like web worker, service
    |   worker, etc.).
    |   Returned by "listWorkers" request to the root actor to get all workers.
    |   Returned by "listWorkers" request to a BrowserTabActor to get workers for
    |   a specific tab.
    |   Returned by "listWorkers" request to a ChildProcessActor to get workers
    |   for the chrome of the child process.
    |
+   |-- WindowActor (window.js)
+   |   Targets a single window, such as a browser window in Firefox, but it can
+   |   be used to reach any window in the parent process.
+   |   Returned by "getWindow" request to the root actor.
+   |
    |-- ChromeActor (chrome.js)
    |   Targets all resources in the parent process of firefox
    |   (chrome documents, JSM, JS XPCOM, etc.).
    |   Returned by "getProcess" request without any argument.
    |
    |-- ChildProcessActor (child-process.js)
    |   Targets the chrome of the child process (e10s).
    |   Returned by "getProcess" request with a id argument,
    |   matching the targeted process.
    |
    \-- BrowserAddonActor (addon.js)
        Targets the javascript of add-ons.
        Returned by "listAddons" request.
+```
 
 ## "TabActor"
 
 Those are the actors exposed by the root actors which are meant to track the
 lifetime of a given context: tab, app, process, add-on, or worker. It also
 allows to fetch the tab-scoped actors connected to this context. Actors like
 console, inspector, thread (for debugger), styleinspector, etc. Most of them
 inherit from TabActor (defined in tab.js) which is document centric. It
--- a/devtools/server/tests/browser/animation.html
+++ b/devtools/server/tests/browser/animation.html
@@ -7,18 +7,18 @@
     height: 50px;
     border-radius: 50%;
     background: #eee;
   }
 
   .simple-animation {
     display: inline-block;
 
-    width: 50px;
-    height: 50px;
+    width: 64px;
+    height: 64px;
     border-radius: 50%;
     background: red;
 
     animation: move 200s infinite;
   }
 
   .multiple-animations {
     display: inline-block;
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -1737,16 +1737,37 @@ RootClient.prototype = {
         throw new Error("Unsupported argument given to getTab request");
       }
     }
 
     return this.request(packet);
   },
 
   /**
+   * Fetch the WindowActor for a specific window, like a browser window in
+   * Firefox, but it can be used to reach any window in the process.
+   *
+   * @param number outerWindowID
+   *        The outerWindowID of the top level window you are looking for.
+   */
+  getWindow: function ({ outerWindowID }) {
+    if (!outerWindowID) {
+      throw new Error("Must specify outerWindowID");
+    }
+
+    let packet = {
+      to: this.actor,
+      type: "getWindow",
+      outerWindowID,
+    };
+
+    return this.request(packet);
+  },
+
+  /**
    * Description of protocol's actors and methods.
    *
    * @param function onResponse
    *        Called with the response packet.
    */
   protocolDescription: DebuggerClient.requester({ type: "protocolDescription" }),
 
   /*
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -41,27 +41,29 @@ namespace mozilla {
 NS_IMPL_ISUPPORTS(LoadContext, nsILoadContext, nsIInterfaceRequestor)
 
 LoadContext::LoadContext(nsIPrincipal* aPrincipal,
                          nsILoadContext* aOptionalBase)
   : mTopFrameElement(nullptr)
   , mNestedFrameId(0)
   , mIsContent(true)
   , mUseRemoteTabs(false)
+  , mUseTrackingProtection(false)
 #ifdef DEBUG
   , mIsNotNull(true)
 #endif
 {
   mOriginAttributes.Inherit(aPrincipal->OriginAttributesRef());
   if (!aOptionalBase) {
     return;
   }
 
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetIsContent(&mIsContent));
   MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseRemoteTabs(&mUseRemoteTabs));
+  MOZ_ALWAYS_SUCCEEDS(aOptionalBase->GetUseTrackingProtection(&mUseTrackingProtection));
 }
 
 //-----------------------------------------------------------------------------
 // LoadContext::nsILoadContext
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 LoadContext::GetAssociatedWindow(mozIDOMWindowProxy**)
@@ -175,30 +177,32 @@ LoadContext::GetOriginAttributes(JS::Mut
   MOZ_ASSERT(cx);
 
   bool ok = ToJSValue(cx, mOriginAttributes, aAttrs);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-LoadContext::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
+LoadContext::GetUseTrackingProtection(bool* aUseTrackingProtection)
 {
   MOZ_ASSERT(mIsNotNull);
 
-  if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
-    *aIsTrackingProtectionOn = true;
-  } else if ((mOriginAttributes.mPrivateBrowsingId > 0) &&
-             Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
-    *aIsTrackingProtectionOn = true;
-  } else {
-    *aIsTrackingProtectionOn = false;
-  }
+  NS_ENSURE_ARG_POINTER(aUseTrackingProtection);
+
+  *aUseTrackingProtection = mUseTrackingProtection;
+  return NS_OK;
+}
 
-  return NS_OK;
+NS_IMETHODIMP
+LoadContext::SetUseTrackingProtection(bool aUseTrackingProtection)
+{
+  MOZ_ASSERT_UNREACHABLE("Should only be set through nsDocShell");
+
+  return NS_ERROR_UNEXPECTED;
 }
 
 //-----------------------------------------------------------------------------
 // LoadContext::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 LoadContext::GetInterface(const nsIID& aIID, void** aResult)
 {
--- a/docshell/base/LoadContext.h
+++ b/docshell/base/LoadContext.h
@@ -40,62 +40,67 @@ public:
   // provided by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               dom::Element* aTopFrameElement,
               OriginAttributes& aAttrs)
     : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
     , mNestedFrameId(0)
     , mIsContent(aToCopy.mIsContent)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
+    , mUseTrackingProtection(aToCopy.mUseTrackingProtection)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
   }
 
   // appId/inIsolatedMozBrowser arguments override those in SerializedLoadContext
   // provided by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               uint64_t aNestedFrameId,
               OriginAttributes& aAttrs)
     : mTopFrameElement(nullptr)
     , mNestedFrameId(aNestedFrameId)
     , mIsContent(aToCopy.mIsContent)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
+    , mUseTrackingProtection(aToCopy.mUseTrackingProtection)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
   }
 
   LoadContext(dom::Element* aTopFrameElement,
               bool aIsContent,
               bool aUsePrivateBrowsing,
               bool aUseRemoteTabs,
+              bool aUseTrackingProtection,
               const OriginAttributes& aAttrs)
     : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
     , mNestedFrameId(0)
     , mIsContent(aIsContent)
     , mUseRemoteTabs(aUseRemoteTabs)
+    , mUseTrackingProtection(aUseTrackingProtection)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(true)
 #endif
   {
     MOZ_DIAGNOSTIC_ASSERT(aUsePrivateBrowsing == (aAttrs.mPrivateBrowsingId > 0));
   }
 
   // Constructor taking reserved origin attributes.
   explicit LoadContext(OriginAttributes& aAttrs)
     : mTopFrameElement(nullptr)
     , mNestedFrameId(0)
     , mIsContent(false)
     , mUseRemoteTabs(false)
+    , mUseTrackingProtection(false)
     , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(true)
 #endif
   {
   }
 
   // Constructor for creating a LoadContext with a given principal's appId and
@@ -105,16 +110,17 @@ public:
 
 private:
   ~LoadContext() {}
 
   nsWeakPtr mTopFrameElement;
   uint64_t mNestedFrameId;
   bool mIsContent;
   bool mUseRemoteTabs;
+  bool mUseTrackingProtection;
   OriginAttributes mOriginAttributes;
 #ifdef DEBUG
   bool mIsNotNull;
 #endif
 };
 
 } // namespace mozilla
 
--- a/docshell/base/SerializedLoadContext.cpp
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -56,22 +56,24 @@ SerializedLoadContext::SerializedLoadCon
 void
 SerializedLoadContext::Init(nsILoadContext* aLoadContext)
 {
   if (aLoadContext) {
     mIsNotNull = true;
     mIsPrivateBitValid = true;
     aLoadContext->GetIsContent(&mIsContent);
     aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
+    aLoadContext->GetUseTrackingProtection(&mUseTrackingProtection);
     if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
       NS_WARNING("GetOriginAttributes failed");
     }
   } else {
     mIsNotNull = false;
     mIsPrivateBitValid = false;
     // none of below values really matter when mIsNotNull == false:
     // we won't be GetInterfaced to nsILoadContext
     mIsContent = true;
     mUseRemoteTabs = false;
+    mUseTrackingProtection = false;
   }
 }
 
 } // namespace IPC
--- a/docshell/base/SerializedLoadContext.h
+++ b/docshell/base/SerializedLoadContext.h
@@ -27,16 +27,17 @@ namespace IPC {
 class SerializedLoadContext
 {
 public:
   SerializedLoadContext()
     : mIsNotNull(false)
     , mIsPrivateBitValid(false)
     , mIsContent(false)
     , mUseRemoteTabs(false)
+    , mUseTrackingProtection(false)
   {
     Init(nullptr);
   }
 
   explicit SerializedLoadContext(nsILoadContext* aLoadContext);
   explicit SerializedLoadContext(nsIChannel* aChannel);
   explicit SerializedLoadContext(nsIWebSocketChannel* aChannel);
 
@@ -47,16 +48,17 @@ public:
 
   // used to indicate if child-side LoadContext * was null.
   bool mIsNotNull;
   // used to indicate if child-side mUsePrivateBrowsing flag is valid, even if
   // mIsNotNull is false, i.e., child LoadContext was null.
   bool mIsPrivateBitValid;
   bool mIsContent;
   bool mUseRemoteTabs;
+  bool mUseTrackingProtection;
   mozilla::OriginAttributes mOriginAttributes;
 };
 
 // Function to serialize over IPDL
 template<>
 struct ParamTraits<SerializedLoadContext>
 {
   typedef SerializedLoadContext paramType;
@@ -65,26 +67,28 @@ struct ParamTraits<SerializedLoadContext
   {
     nsAutoCString suffix;
     aParam.mOriginAttributes.CreateSuffix(suffix);
 
     WriteParam(aMsg, aParam.mIsNotNull);
     WriteParam(aMsg, aParam.mIsContent);
     WriteParam(aMsg, aParam.mIsPrivateBitValid);
     WriteParam(aMsg, aParam.mUseRemoteTabs);
+    WriteParam(aMsg, aParam.mUseTrackingProtection);
     WriteParam(aMsg, suffix);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     nsAutoCString suffix;
     if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) ||
         !ReadParam(aMsg, aIter, &aResult->mIsContent) ||
         !ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) ||
         !ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) ||
+        !ReadParam(aMsg, aIter, &aResult->mUseTrackingProtection) ||
         !ReadParam(aMsg, aIter, &suffix)) {
       return false;
     }
     return aResult->mOriginAttributes.PopulateFromSuffix(suffix);
   }
 };
 
 } // namespace IPC
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -133,16 +133,22 @@ static const RedirEntry kRedirMap[] = {
   },
   {
     "telemetry", "chrome://global/content/aboutTelemetry.xhtml",
     nsIAboutModule::ALLOW_SCRIPT
   },
   {
     "webrtc", "chrome://global/content/aboutwebrtc/aboutWebrtc.html",
     nsIAboutModule::ALLOW_SCRIPT
+  },
+  {
+    "printpreview", "about:blank",
+    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+    nsIAboutModule::HIDE_FROM_ABOUTABOUT |
+    nsIAboutModule::URI_CAN_LOAD_IN_CHILD
   }
 };
 static const int kRedirTotal = mozilla::ArrayLength(kRedirMap);
 
 NS_IMETHODIMP
 nsAboutRedirector::NewChannel(nsIURI* aURI,
                               nsILoadInfo* aLoadInfo,
                               nsIChannel** aResult)
@@ -168,19 +174,21 @@ nsAboutRedirector::NewChannel(nsIURI* aU
       // chrome:// or resource://) then set the LOAD_REPLACE flag on the
       // channel which forces the channel owner to reflect the displayed
       // URL rather then being the systemPrincipal.
       bool isUIResource = false;
       rv = NS_URIChainHasFlags(tempURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
                                &isUIResource);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      nsLoadFlags loadFlags =
-        isUIResource ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
-                     : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
+      bool isAboutBlank = NS_IsAboutBlank(tempURI);
+
+      nsLoadFlags loadFlags = isUIResource || isAboutBlank
+                    ? static_cast<nsLoadFlags>(nsIChannel::LOAD_NORMAL)
+                    : static_cast<nsLoadFlags>(nsIChannel::LOAD_REPLACE);
 
       rv = NS_NewChannelInternal(getter_AddRefs(tempChannel),
                                  tempURI,
                                  aLoadInfo,
                                  nullptr, // aLoadGroup
                                  nullptr, // aCallbacks
                                  loadFlags);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -790,16 +790,17 @@ nsDocShell::nsDocShell()
   , mAllowKeywordFixup(false)
   , mIsOffScreenBrowser(false)
   , mIsActive(true)
   , mDisableMetaRefreshWhenInactive(false)
   , mIsPrerendered(false)
   , mIsAppTab(false)
   , mUseGlobalHistory(false)
   , mUseRemoteTabs(false)
+  , mUseTrackingProtection(false)
   , mDeviceSizeIsPageSize(false)
   , mWindowDraggingAllowed(false)
   , mInFrameSwap(false)
   , mInheritPrivateBrowsingId(true)
   , mCanExecuteScripts(false)
   , mFiredUnloadEvent(false)
   , mEODForCurrentDocument(false)
   , mURIResultedInDocument(false)
@@ -8847,24 +8848,17 @@ nsDocShell::RestoreFromHistory()
 
   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDoc);
   if (document) {
     RefPtr<nsDocShell> parent = GetParentDocshell();
     if (parent) {
       nsCOMPtr<nsIDocument> d = parent->GetDocument();
       if (d) {
         if (d->EventHandlingSuppressed()) {
-          document->SuppressEventHandling(nsIDocument::eEvents,
-                                          d->EventHandlingSuppressed());
-        }
-
-        // Ick, it'd be nicer to not rewalk all of the subdocs here.
-        if (d->AnimationsPaused()) {
-          document->SuppressEventHandling(nsIDocument::eAnimationsOnly,
-                                          d->AnimationsPaused());
+          document->SuppressEventHandling(d->EventHandlingSuppressed());
         }
       }
     }
 
     // Use the uri from the mLSHE we had when we entered this function
     // (which need not match the document's URI if anchors are involved),
     // since that's the history entry we're loading.  Note that if we use
     // origLSHE we don't have to worry about whether the entry in question
@@ -13681,27 +13675,50 @@ nsDocShell::GetTopFrameElement(nsIDOMEle
 NS_IMETHODIMP
 nsDocShell::GetNestedFrameId(uint64_t* aId)
 {
   *aId = 0;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocShell::IsTrackingProtectionOn(bool* aIsTrackingProtectionOn)
-{
-  if (Preferences::GetBool("privacy.trackingprotection.enabled", false)) {
-    *aIsTrackingProtectionOn = true;
-  } else if (UsePrivateBrowsing() &&
-             Preferences::GetBool("privacy.trackingprotection.pbmode.enabled", false)) {
-    *aIsTrackingProtectionOn = true;
-  } else {
-    *aIsTrackingProtectionOn = false;
-  }
-
+nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection)
+{
+  *aUseTrackingProtection  = false;
+
+  static bool sTPEnabled = false;
+  static bool sTPInPBEnabled = false;
+  static bool sPrefsInit = false;
+
+  if (!sPrefsInit) {
+    sPrefsInit = true;
+    Preferences::AddBoolVarCache(&sTPEnabled,
+      "privacy.trackingprotection.enabled", false);
+    Preferences::AddBoolVarCache(&sTPInPBEnabled,
+      "privacy.trackingprotection.pbmode.enabled", false);
+  }
+
+  if (mUseTrackingProtection || sTPEnabled ||
+      (UsePrivateBrowsing() && sTPInPBEnabled)) {
+    *aUseTrackingProtection = true;
+    return NS_OK;
+  }
+
+  RefPtr<nsDocShell> parent = GetParentDocshell();
+  if (parent) {
+    return parent->GetUseTrackingProtection(aUseTrackingProtection);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection)
+{
+  mUseTrackingProtection = aUseTrackingProtection;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetIsContent(bool* aIsContent)
 {
   *aIsContent = (mItemType == typeContent);
   return NS_OK;
@@ -14342,20 +14359,29 @@ nsDocShell::StopDocumentLoad(void)
 
 NS_IMETHODIMP
 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
 {
   *aPrintPreview = nullptr;
 #if NS_PRINT_PREVIEW
   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
   if (!print || !print->IsInitializedForPrintPreview()) {
+    // XXX: Creating a brand new content viewer to host preview every
+    // time we enter here seems overwork. We could skip ahead to where
+    // we QI the mContentViewer if the current URI is either about:blank
+    // or about:printpreview.
     Stop(nsIWebNavigation::STOP_ALL);
     nsCOMPtr<nsIPrincipal> principal = nsNullPrincipal::CreateWithInheritedAttributes(this);
-    nsresult rv = CreateAboutBlankContentViewer(principal, nullptr);
+    nsCOMPtr<nsIURI> uri;
+    NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:printpreview"));
+    nsresult rv = CreateAboutBlankContentViewer(principal, uri);
     NS_ENSURE_SUCCESS(rv, rv);
+    // Here we manually set current URI since we have just created a
+    // brand new content viewer (about:blank) to host preview.
+    SetCurrentURI(uri, nullptr, true, 0);
     print = do_QueryInterface(mContentViewer);
     NS_ENSURE_STATE(print);
     print->InitializeForPrintPreview();
   }
   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
   result.forget(aPrintPreview);
   return NS_OK;
 #else
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -229,17 +229,16 @@ public:
   NS_IMETHOD GetNestedFrameId(uint64_t*) override;
   NS_IMETHOD GetIsContent(bool*) override;
   NS_IMETHOD GetUsePrivateBrowsing(bool*) override;
   NS_IMETHOD SetUsePrivateBrowsing(bool) override;
   NS_IMETHOD SetPrivateBrowsing(bool) override;
   NS_IMETHOD GetUseRemoteTabs(bool*) override;
   NS_IMETHOD SetRemoteTabs(bool) override;
   NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
-  NS_IMETHOD IsTrackingProtectionOn(bool*) override;
 
   // Restores a cached presentation from history (mLSHE).
   // This method swaps out the content viewer and simulates loads for
   // subframes. It then simulates the completion of the toplevel load.
   nsresult RestoreFromHistory();
 
   // Perform a URI load from a refresh timer. This is just like the
   // ForceRefreshURI method on nsIRefreshURI, but makes sure to take
@@ -956,16 +955,17 @@ protected:
   bool mAllowKeywordFixup : 1;
   bool mIsOffScreenBrowser : 1;
   bool mIsActive : 1;
   bool mDisableMetaRefreshWhenInactive : 1;
   bool mIsPrerendered : 1;
   bool mIsAppTab : 1;
   bool mUseGlobalHistory : 1;
   bool mUseRemoteTabs : 1;
+  bool mUseTrackingProtection : 1;
   bool mDeviceSizeIsPageSize : 1;
   bool mWindowDraggingAllowed : 1;
   bool mInFrameSwap : 1;
   bool mInheritPrivateBrowsingId : 1;
 
   // Because scriptability depends on the mAllowJavascript values of our
   // ancestors, we cache the effective scriptability and recompute it when
   // it might have changed;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1126,9 +1126,14 @@ interface nsIDocShell : nsIDocShellTreeI
    */
   [infallible] readonly attribute boolean isOnlyToplevelInTabGroup;
 
   /**
    * Returns `true` if this docshell was created due to a Large-Allocation
    * header, and has not seen the initiating load yet.
    */
   [infallible] readonly attribute boolean awaitingLargeAlloc;
+
+  /**
+   * Attribute that determines whether tracking protection is enabled.
+   */
+  attribute boolean useTrackingProtection;
 };
--- a/docshell/base/nsILoadContext.idl
+++ b/docshell/base/nsILoadContext.idl
@@ -73,31 +73,46 @@ interface nsILoadContext : nsISupports
    */
   attribute boolean usePrivateBrowsing;
 
   /**
    * Attribute that determines if remote (out-of-process) tabs should be used.
    */
   readonly attribute boolean useRemoteTabs;
 
+  /*
+   * Attribute that determines if tracking protection should be used. May not be
+   * changed after a document has been loaded in this context.
+   */
+  attribute boolean useTrackingProtection;
+
 %{C++
   /**
    * De-XPCOMed getter to make call-sites cleaner.
    */
-  bool UsePrivateBrowsing() {
-    bool usingPB;
+  bool UsePrivateBrowsing()
+  {
+    bool usingPB = false;
     GetUsePrivateBrowsing(&usingPB);
     return usingPB;
   }
 
-  bool UseRemoteTabs() {
-    bool usingRT;
+  bool UseRemoteTabs()
+  {
+    bool usingRT = false;
     GetUseRemoteTabs(&usingRT);
     return usingRT;
   }
+
+  bool UseTrackingProtection()
+  {
+    bool usingTP = false;
+    GetUseTrackingProtection(&usingTP);
+    return usingTP;
+  }
 %}
 
   /**
    * Set the private browsing state of the load context, meant to be used internally.
    */
   [noscript] void SetPrivateBrowsing(in boolean aInPrivateBrowsing);
 
   /**
@@ -125,25 +140,9 @@ interface nsILoadContext : nsISupports
   /**
    * The C++ getter for origin attributes.
    *
    * Defined in LoadContext.cpp
    */
   bool GetOriginAttributes(mozilla::OriginAttributes& aAttrs);
 #endif
 %}
-
-  /**
-   * Returns true if tracking protection is enabled for the load context.
-   */
-  boolean IsTrackingProtectionOn();
-
-%{C++
-  /**
-   * De-XPCOMed getter to make call-sites cleaner.
-   */
-  bool UseTrackingProtection() {
-    bool usingTP;
-    IsTrackingProtectionOn(&usingTP);
-    return usingTP;
-  }
-%}
 };
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -184,16 +184,17 @@ const mozilla::Module::ContractIDEntry k
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #ifndef ANDROID
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "profiles", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "webrtc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
+  { NS_ABOUT_MODULE_CONTRACTID_PREFIX "printpreview", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID },
   { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID },
   { NS_HANDLERSERVICE_CONTRACTID, &kNS_CONTENTHANDLERSERVICE_CID, mozilla::Module::CONTENT_PROCESS_ONLY },
   { NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_MIMESERVICE_CONTRACTID, &kNS_EXTERNALHELPERAPPSERVICE_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX"default", &kNS_EXTERNALPROTOCOLHANDLER_CID },
   { NS_PREFETCHSERVICE_CONTRACTID, &kNS_PREFETCHSERVICE_CID },
--- a/dom/animation/AnimationPerformanceWarning.cpp
+++ b/dom/animation/AnimationPerformanceWarning.cpp
@@ -28,23 +28,16 @@ AnimationPerformanceWarning::ToLocalized
 
 bool
 AnimationPerformanceWarning::ToLocalizedString(
   nsXPIDLString& aLocalizedString) const
 {
   const char* key = nullptr;
 
   switch (mType) {
-    case Type::ContentTooSmall:
-      MOZ_ASSERT(mParams && mParams->Length() == 2,
-                 "Parameter's length should be 2 for ContentTooSmall");
-
-      return NS_SUCCEEDED(
-        ToLocalizedStringWithIntParams<2>(
-          "CompositorAnimationWarningContentTooSmall", aLocalizedString));
     case Type::ContentTooLarge:
       MOZ_ASSERT(mParams && mParams->Length() == 6,
                  "Parameter's length should be 6 for ContentTooLarge");
 
       return NS_SUCCEEDED(
         ToLocalizedStringWithIntParams<7>(
           "CompositorAnimationWarningContentTooLarge2", aLocalizedString));
     case Type::TransformBackfaceVisibilityHidden:
--- a/dom/animation/AnimationPerformanceWarning.h
+++ b/dom/animation/AnimationPerformanceWarning.h
@@ -15,17 +15,16 @@
 class nsXPIDLString;
 
 namespace mozilla {
 
 // Represents the reason why we can't run the CSS property on the compositor.
 struct AnimationPerformanceWarning
 {
   enum class Type : uint8_t {
-    ContentTooSmall,
     ContentTooLarge,
     TransformBackfaceVisibilityHidden,
     TransformPreserve3D,
     TransformSVG,
     TransformWithGeometricProperties,
     TransformWithSyncGeometricAnimations,
     TransformFrameInactive,
     OpacityFrameInactive,
--- a/dom/animation/test/chrome/file_animation_performance_warning.html
+++ b/dom/animation/test/chrome/file_animation_performance_warning.html
@@ -835,47 +835,45 @@ function testMultipleAnimationsWithGeome
       });
     }, 'Multiple async animations and geometric animation: ' + subtest.desc);
   });
 }
 
 function testSmallElements() {
   [
     {
-      desc: 'opacity on too small element',
+      desc: 'opacity on small element',
       frames: {
         opacity: [0, 1]
       },
       style: { style: 'width: 8px; height: 8px; background-color: red;' +
                       // We need to set transform here to try creating an
                       // individual frame for this opacity element.
                       // Without this, this small element is created on the same
                       // nsIFrame of mochitest iframe, i.e. the document which are
                       // running this test, as a result the layer corresponding
                       // to the frame is sent to compositor.
                       'transform: translateX(100px);' },
       expected: [
         {
           property: 'opacity',
-          runningOnCompositor: false,
-          warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
+          runningOnCompositor: true
         }
       ]
     },
     {
-      desc: 'transform on too small element',
+      desc: 'transform on small element',
       frames: {
         transform: ['translate(0px)', 'translate(100px)']
       },
       style: { style: 'width: 8px; height: 8px; background-color: red;' },
       expected: [
         {
           property: 'transform',
-          runningOnCompositor: false,
-          warning: /Animation cannot be run on the compositor because frame size \(8, 8\) is smaller than \(16, 16\)/
+          runningOnCompositor: true
         }
       ]
     },
   ].forEach(subtest => {
     promise_test(function(t) {
     var div = addDiv(t, subtest.style);
     var animation = div.animate(subtest.frames, 100 * MS_PER_SEC);
       return animation.ready.then(function() {
--- a/dom/base/GroupedSHistory.cpp
+++ b/dom/base/GroupedSHistory.cpp
@@ -1,15 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GroupedSHistory.h"
+
+#include "mozilla/dom/Promise.h"
 #include "TabParent.h"
 #include "PartialSHistory.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(GroupedSHistory)
 
--- a/dom/base/GroupedSHistory.h
+++ b/dom/base/GroupedSHistory.h
@@ -6,16 +6,19 @@
 
 #ifndef GroupedSHistory_h
 #define GroupedSHistory_h
 
 #include "nsIFrameLoader.h"
 #include "nsIGroupedSHistory.h"
 #include "nsIPartialSHistory.h"
 #include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsWeakReference.h"
 
 namespace mozilla {
 namespace dom {
 
 
 /**
  * GroupedSHistory connects session histories across multiple frameloaders.
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3439,22 +3439,22 @@ nsContentUtils::IsDraggableImage(nsICont
 bool
 nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
   nsCOMPtr<nsIURI> absURI;
   return aContent->IsLink(getter_AddRefs(absURI));
 }
 
 // static
 nsresult
-nsContentUtils::NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
-                            mozilla::dom::NodeInfo** aResult)
+nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
+                             mozilla::dom::NodeInfo** aResult)
 {
   nsNodeInfoManager *niMgr = aNodeInfo->NodeInfoManager();
 
-  *aResult = niMgr->GetNodeInfo(aName, aNodeInfo->GetPrefixAtom(),
+  *aResult = niMgr->GetNodeInfo(aName, nullptr,
                                 aNodeInfo->NamespaceID(),
                                 aNodeInfo->NodeType(),
                                 aNodeInfo->GetExtraName()).take();
   return NS_OK;
 }
 
 
 static bool
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -791,21 +791,23 @@ public:
    * Method that decides whether a content node is a draggable link
    *
    * @param aContent The content node to test.
    * @return whether it's a draggable link
    */
   static bool IsDraggableLink(const nsIContent* aContent);
 
   /**
-   * Convenience method to create a new nodeinfo that differs only by name
-   * from aNodeInfo.
+   * Convenience method to create a new nodeinfo that differs only by prefix and
+   * name from aNodeInfo. The new nodeinfo's name is set to aName, and prefix is
+   * set to null.
    */
-  static nsresult NameChanged(mozilla::dom::NodeInfo* aNodeInfo, nsIAtom* aName,
-                              mozilla::dom::NodeInfo** aResult);
+  static nsresult QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
+                               nsIAtom* aName,
+                               mozilla::dom::NodeInfo** aResult);
 
   /**
    * Returns the appropriate event argument names for the specified
    * namespace and event name.  Added because we need to switch between
    * SVG's "evt" and the rest of the world's "event", and because onerror
    * on window takes 5 args.
    */
   static void GetEventArgNames(int32_t aNameSpaceID, nsIAtom *aEventName,
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -750,18 +750,23 @@ nsCopySupport::FireClipboardEvent(EventM
                                   nsIPresShell* aPresShell,
                                   nsISelection* aSelection,
                                   bool* aActionTaken)
 {
   if (aActionTaken) {
     *aActionTaken = false;
   }
 
-  NS_ASSERTION(aEventMessage == eCut || aEventMessage == eCopy ||
-               aEventMessage == ePaste,
+  EventMessage originalEventMessage = aEventMessage;
+  if (originalEventMessage == ePasteNoFormatting) {
+    originalEventMessage = ePaste;
+  }
+
+  NS_ASSERTION(originalEventMessage == eCut || originalEventMessage == eCopy ||
+               originalEventMessage == ePaste,
                "Invalid clipboard event type");
 
   nsCOMPtr<nsIPresShell> presShell = aPresShell;
   if (!presShell)
     return false;
 
   nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
   if (!doc)
@@ -808,31 +813,31 @@ nsCopySupport::FireClipboardEvent(EventM
     docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
 
   // next, fire the cut, copy or paste event
   bool doDefault = true;
   RefPtr<DataTransfer> clipboardData;
   if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
     clipboardData =
       new DataTransfer(doc->GetScopeObject(), aEventMessage,
-                       aEventMessage == ePaste, aClipboardType);
+                       originalEventMessage == ePaste, aClipboardType);
 
     nsEventStatus status = nsEventStatus_eIgnore;
-    InternalClipboardEvent evt(true, aEventMessage);
+    InternalClipboardEvent evt(true, originalEventMessage);
     evt.mClipboardData = clipboardData;
     EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt,
                               nullptr, &status);
     // If the event was cancelled, don't do the clipboard operation
     doDefault = (status != nsEventStatus_eConsumeNoDefault);
   }
 
   // No need to do anything special during a paste. Either an event listener
   // took care of it and cancelled the event, or the caller will handle it.
   // Return true to indicate that the event wasn't cancelled.
-  if (aEventMessage == ePaste) {
+  if (originalEventMessage == ePaste) {
     // Clear and mark the clipboardData as readonly. This prevents someone
     // from reading the clipboard contents after the paste event has fired.
     if (clipboardData) {
       clipboardData->ClearAll();
       clipboardData->SetReadOnly();
     }
 
     if (aActionTaken) {
@@ -862,17 +867,17 @@ nsCopySupport::FireClipboardEvent(EventM
     if (formControl) {
       if (formControl->GetType() == NS_FORM_INPUT_PASSWORD) {
         return false;
       }
     }
 
     // when cutting non-editable content, do nothing
     // XXX this is probably the wrong editable flag to check
-    if (aEventMessage != eCut || content->IsEditable()) {
+    if (originalEventMessage != eCut || content->IsEditable()) {
       // get the data from the selection if any
       bool isCollapsed;
       sel->GetIsCollapsed(&isCollapsed);
       if (isCollapsed) {
         if (aActionTaken) {
           *aActionTaken = true;
         }
         return false;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -1645,19 +1645,19 @@ nsDOMWindowUtils::DisableNonTestMouseEve
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SuppressEventHandling(bool aSuppress)
 {
   nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   if (aSuppress) {
-    doc->SuppressEventHandling(nsIDocument::eEvents);
+    doc->SuppressEventHandling();
   } else {
-    doc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, true);
+    doc->UnsuppressEventHandlingAndFireEvents(true);
   }
 
   return NS_OK;
 }
 
 static nsresult
 getScrollXYAppUnits(const nsWeakPtr& aWindow, bool aFlushLayout, nsPoint& aScrollPos) {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(aWindow);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1353,17 +1353,16 @@ nsIDocument::nsIDocument()
 #endif
     mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
     mSandboxFlags(0),
     mPartID(0),
     mMarkedCCGeneration(0),
     mPresShell(nullptr),
     mSubtreeModifiedDepth(0),
     mEventsSuppressed(0),
-    mAnimationsPaused(0),
     mExternalScriptsBeingEvaluated(0),
     mFrameRequestCallbackCounter(0),
     mStaticCloneCount(0),
     mWindow(nullptr),
     mBFCacheEntry(nullptr),
     mInSyncOperationCount(0),
     mBlockDOMContentLoaded(0),
     mDidFireDOMContentLoaded(true),
@@ -3806,17 +3805,17 @@ nsDocument::CreateShell(nsPresContext* a
   RebuildUserFontSet();
 
   return shell.forget();
 }
 
 void
 nsDocument::MaybeRescheduleAnimationFrameNotifications()
 {
-  if (!mPresShell || !IsEventHandlingEnabled() || AnimationsPaused()) {
+  if (!mPresShell || !IsEventHandlingEnabled()) {
     // bail out for now, until one of those conditions changes
     return;
   }
 
   nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
   if (!mFrameRequestCallbacks.IsEmpty()) {
     rd->ScheduleFrameRequestCallbacks(this);
   }
@@ -3869,17 +3868,17 @@ nsIDocument::ShouldThrottleFrameRequests
   // We got painted during the last paint, so run at full speed.
   return false;
 }
 
 void
 nsDocument::DeleteShell()
 {
   mExternalResourceMap.HideViewers();
-  if (IsEventHandlingEnabled() && !AnimationsPaused()) {
+  if (IsEventHandlingEnabled()) {
     RevokeAnimationFrameNotifications();
   }
   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
     presContext->RefreshDriver()->CancelPendingEvents(this);
   }
 
   // When our shell goes away, request that all our images be immediately
   // discarded, so we don't carry around decoded image data for a document we
@@ -4651,17 +4650,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
                nsSMILTimeContainer::PAUSE_BEGIN),
              "Clearing window pointer while animations are unpaused");
 
   if (mScriptGlobalObject && !aScriptGlobalObject) {
     // We're detaching from the window.  We need to grab a pointer to
     // our layout history state now.
     mLayoutHistoryState = GetLayoutHistoryState();
 
-    if (mPresShell && !EventHandlingSuppressed() && !AnimationsPaused()) {
+    if (mPresShell && !EventHandlingSuppressed()) {
       RevokeAnimationFrameNotifications();
     }
 
     // Also make sure to remove our onload blocker now if we haven't done it yet
     if (mOnloadBlockCount != 0) {
       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
       if (loadGroup) {
         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
@@ -9320,54 +9319,38 @@ nsIDocument::GetReadyState(nsAString& aR
   case READYSTATE_COMPLETE :
     aReadyState.AssignLiteral(u"complete");
     break;
   default:
     aReadyState.AssignLiteral(u"uninitialized");
   }
 }
 
-namespace {
-
-struct SuppressArgs
-{
-  nsIDocument::SuppressionType mWhat;
-  uint32_t mIncrease;
-};
-
-} // namespace
-
 static bool
 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
 {
-  SuppressArgs* args = static_cast<SuppressArgs*>(aData);
-  aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
+  aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
+
   return true;
 }
 
 void
-nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
-                                  uint32_t aIncrease)
-{
-  if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
-      aIncrease != 0 && mPresShell && mScriptGlobalObject) {
+nsDocument::SuppressEventHandling(uint32_t aIncrease)
+{
+  if (mEventsSuppressed == 0 && aIncrease != 0 && mPresShell &&
+      mScriptGlobalObject) {
     RevokeAnimationFrameNotifications();
   }
 
-  if (aWhat == eAnimationsOnly) {
-    mAnimationsPaused += aIncrease;
-  } else {
-    mEventsSuppressed += aIncrease;
-    for (uint32_t i = 0; i < aIncrease; ++i) {
-      ScriptLoader()->AddExecuteBlocker();
-    }
-  }
-
-  SuppressArgs args = { aWhat, aIncrease };
-  EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
+  mEventsSuppressed += aIncrease;
+  for (uint32_t i = 0; i < aIncrease; ++i) {
+    ScriptLoader()->AddExecuteBlocker();
+  }
+
+  EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
 }
 
 static void
 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
                          bool aFireEvents)
 {
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   if (!fm)
@@ -9659,72 +9642,45 @@ public:
     FireOrClearDelayedEvents(mDocuments, true);
     return NS_OK;
   }
 
 private:
   nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
 };
 
-namespace {
-
-struct UnsuppressArgs
-{
-  explicit UnsuppressArgs(nsIDocument::SuppressionType aWhat)
-    : mWhat(aWhat)
-  {
-  }
-
-  nsIDocument::SuppressionType mWhat;
-  nsTArray<nsCOMPtr<nsIDocument>> mDocs;
-};
-
-} // namespace
-
 static bool
 GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
                              void* aData)
 {
-  UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
-  if (args->mWhat != nsIDocument::eAnimationsOnly &&
-      aDocument->EventHandlingSuppressed() > 0) {
+  if (aDocument->EventHandlingSuppressed() > 0) {
     static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
     aDocument->ScriptLoader()->RemoveExecuteBlocker();
-  } else if (args->mWhat == nsIDocument::eAnimationsOnly &&
-             aDocument->AnimationsPaused()) {
-    static_cast<nsDocument*>(aDocument)->ResumeAnimations();
-  }
-
-  if (args->mWhat != nsIDocument::eAnimationsOnly) {
-    // No need to remember documents if we only care about animation frames.
-    args->mDocs.AppendElement(aDocument);
-  }
-
+  }
+
+  nsTArray<nsCOMPtr<nsIDocument> >* docs =
+    static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
+
+  docs->AppendElement(aDocument);
   aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
   return true;
 }
 
 void
-nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
-                                                 bool aFireEvents)
-{
-  UnsuppressArgs args(aWhat);
-  GetAndUnsuppressSubDocuments(this, &args);
-
-  if (aWhat == nsIDocument::eAnimationsOnly) {
-    // No need to fire events if we only care about animations here.
-    return;
-  }
+nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
+{
+  nsTArray<nsCOMPtr<nsIDocument>> documents;
+  GetAndUnsuppressSubDocuments(this, &documents);
 
   if (aFireEvents) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
-    nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(args.mDocs);
+    nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
     Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget());
   } else {
-    FireOrClearDelayedEvents(args.mDocs, false);
+    FireOrClearDelayedEvents(documents, false);
   }
 }
 
 nsISupports*
 nsDocument::GetCurrentContentSink()
 {
   return mParser ? mParser->GetContentSink() : nullptr;
 }
@@ -10039,33 +9995,32 @@ nsIDocument::ScheduleFrameRequestCallbac
     return NS_ERROR_NOT_AVAILABLE;
   }
   int32_t newHandle = ++mFrameRequestCallbackCounter;
 
   bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
   DebugOnly<FrameRequest*> request =
     mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
   NS_ASSERTION(request, "This is supposed to be infallible!");
-  if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled() &&
-      !AnimationsPaused()) {
+  if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
     mPresShell->GetPresContext()->RefreshDriver()->
       ScheduleFrameRequestCallbacks(this);
   }
 
   *aHandle = newHandle;
   return NS_OK;
 }
 
 void
 nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
 {
   // mFrameRequestCallbacks is stored sorted by handle
   if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
       mFrameRequestCallbacks.IsEmpty() &&
-      mPresShell && IsEventHandlingEnabled() && !AnimationsPaused()) {
+      mPresShell && IsEventHandlingEnabled()) {
     mPresShell->GetPresContext()->RefreshDriver()->
       RevokeFrameRequestCallbacks(this);
   }
 }
 
 nsresult
 nsDocument::GetStateObject(nsIVariant** aState)
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -902,34 +902,26 @@ public:
   GetPendingAnimationTracker() final override
   {
     return mPendingAnimationTracker;
   }
 
   virtual mozilla::PendingAnimationTracker*
   GetOrCreatePendingAnimationTracker() override;
 
-  virtual void SuppressEventHandling(SuppressionType aWhat,
-                                     uint32_t aIncrease) override;
+  virtual void SuppressEventHandling(uint32_t aIncrease) override;
 
-  virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat,
-                                                    bool aFireEvents) override;
+  virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) override;
 
   void DecreaseEventSuppression() {
     MOZ_ASSERT(mEventsSuppressed);
     --mEventsSuppressed;
     MaybeRescheduleAnimationFrameNotifications();
   }
 
-  void ResumeAnimations() {
-    MOZ_ASSERT(mAnimationsPaused);
-    --mAnimationsPaused;
-    MaybeRescheduleAnimationFrameNotifications();
-  }
-
   virtual nsIDocument* GetTemplateContentsOwner() override;
 
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
                                                                    nsIDocument)
 
   void DoNotifyPossibleTitleChange();
 
   nsExternalResourceMap& ExternalResourceMap()
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1999,21 +1999,18 @@ nsGlobalWindow::FreeInnerObjects()
 
   if (mDoc) {
     // Remember the document's principal and URI.
     mDocumentPrincipal = mDoc->NodePrincipal();
     mDocumentURI = mDoc->GetDocumentURI();
     mDocBaseURI = mDoc->GetDocBaseURI();
 
     while (mDoc->EventHandlingSuppressed()) {
-      mDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents, false);
-    }
-
-    // Note: we don't have to worry about eAnimationsOnly suppressions because
-    // they won't leak.
+      mDoc->UnsuppressEventHandlingAndFireEvents(false);
+    }
   }
 
   // Remove our reference to the document and the document principal.
   mFocusedNode = nullptr;
 
   if (mApplicationCache) {
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->Disconnect();
     mApplicationCache = nullptr;
@@ -9261,17 +9258,17 @@ nsGlobalWindow::EnterModalState()
     nsIPresShell::SetCapturingContent(nullptr, 0);
   }
 
   if (topWin->mModalStateDepth == 0) {
     NS_ASSERTION(!topWin->mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
 
     topWin->mSuspendedDoc = topDoc;
     if (topDoc) {
-      topDoc->SuppressEventHandling(nsIDocument::eEvents);
+      topDoc->SuppressEventHandling();
     }
 
     nsGlobalWindow* inner = topWin->GetCurrentInnerWindowInternal();
     if (inner) {
       topWin->GetCurrentInnerWindowInternal()->Suspend();
     }
   }
   topWin->mModalStateDepth++;
@@ -9298,18 +9295,17 @@ nsGlobalWindow::LeaveModalState()
 
   if (topWin->mModalStateDepth == 0) {
     if (inner) {
       inner->Resume();
     }
 
     if (topWin->mSuspendedDoc) {
       nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
-      topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
-                                                                  currentDoc == topWin->mSuspendedDoc);
+      topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(currentDoc == topWin->mSuspendedDoc);
       topWin->mSuspendedDoc = nullptr;
     }
   }
 
   // Remember the time of the last dialog quit.
   if (inner) {
     inner->mLastDialogQuitTime = TimeStamp::Now();
   }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2153,41 +2153,30 @@ public:
   virtual mozilla::PendingAnimationTracker* GetPendingAnimationTracker() = 0;
 
   // Gets the tracker for animations that are waiting to start and
   // creates it if it doesn't already exist. As a result, the return value
   // will never be nullptr.
   virtual mozilla::PendingAnimationTracker*
   GetOrCreatePendingAnimationTracker() = 0;
 
-  enum SuppressionType {
-    eAnimationsOnly = 0x1,
-
-    // Note that suppressing events also suppresses animation frames, so
-    // there's no need to split out events in its own bitmask.
-    eEvents = 0x3,
-  };
-
   /**
    * Prevents user initiated events from being dispatched to the document and
    * subdocuments.
    */
-  virtual void SuppressEventHandling(SuppressionType aWhat,
-                                     uint32_t aIncrease = 1) = 0;
+  virtual void SuppressEventHandling(uint32_t aIncrease = 1) = 0;
 
   /**
    * Unsuppress event handling.
    * @param aFireEvents If true, delayed events (focus/blur) will be fired
    *                    asynchronously.
    */
-  virtual void UnsuppressEventHandlingAndFireEvents(SuppressionType aWhat,
-                                                    bool aFireEvents) = 0;
+  virtual void UnsuppressEventHandlingAndFireEvents(bool aFireEvents) = 0;
 
   uint32_t EventHandlingSuppressed() const { return mEventsSuppressed; }
-  uint32_t AnimationsPaused() const { return mAnimationsPaused; }
 
   bool IsEventHandlingEnabled() {
     return !EventHandlingSuppressed() && mScriptGlobalObject;
   }
 
   /**
    * Increment the number of external scripts being evaluated.
    */
@@ -3277,18 +3266,16 @@ protected:
 
   // If we're an external resource document, this will be non-null and will
   // point to our "display document": the one that all resource lookups should
   // go to.
   nsCOMPtr<nsIDocument> mDisplayDocument;
 
   uint32_t mEventsSuppressed;
 
-  uint32_t mAnimationsPaused;
-
   /**
    * The number number of external scripts (ones with the src attribute) that
    * have this document as their owner and that are being evaluated right now.
    */
   uint32_t mExternalScriptsBeingEvaluated;
 
   /**
    * The current frame request callback handle
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2782,17 +2782,17 @@ void
 HandlePrerenderingViolation(nsPIDOMWindowInner* aWindow)
 {
   // Freeze the window and its workers, and its children too.
   aWindow->Freeze();
 
   // Suspend event handling on the document
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (doc) {
-    doc->SuppressEventHandling(nsIDocument::eEvents);
+    doc->SuppressEventHandling();
   }
 }
 
 bool
 EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj)
 {
   JS::Rooted<JSObject*> thisObj(aCx, js::CheckedUnwrap(aObj));
   if (!thisObj) {
--- a/dom/bindings/UnionMember.h
+++ b/dom/bindings/UnionMember.h
@@ -16,17 +16,24 @@ namespace dom {
 
 // The union type has an enum to keep track of which of its UnionMembers has
 // been constructed.
 template<class T>
 class UnionMember
 {
   AlignedStorage2<T> mStorage;
 
+  // Copy construction can't be supported because C++ requires that any enclosed
+  // T be initialized in a way C++ knows about -- that is, by |new| or similar.
+  UnionMember(const UnionMember&) = delete;
+
 public:
+  UnionMember() = default;
+  ~UnionMember() = default;
+
   T& SetValue()
   {
     new (mStorage.addr()) T();
     return *mStorage.addr();
   }
   template <typename T1>
   T& SetValue(const T1& aValue)
   {
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -31,32 +31,23 @@ namespace CanvasUtils {
 bool
 GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
 {
   if (str.EqualsLiteral("2d")) {
     *out_type = dom::CanvasContextType::Canvas2D;
     return true;
   }
 
-  if (str.EqualsLiteral("experimental-webgl")) {
+  if (str.EqualsLiteral("webgl") ||
+      str.EqualsLiteral("experimental-webgl"))
+  {
     *out_type = dom::CanvasContextType::WebGL1;
     return true;
   }
 
-#ifdef MOZ_WEBGL_CONFORMANT
-  if (str.EqualsLiteral("webgl")) {
-    /* WebGL 1.0, $2.1 "Context Creation":
-     *   If the user agent supports both the webgl and experimental-webgl
-     *   canvas context types, they shall be treated as aliases.
-     */
-    *out_type = dom::CanvasContextType::WebGL1;
-    return true;
-  }
-#endif
-
   if (WebGL2Context::IsSupported()) {
     if (str.EqualsLiteral("webgl2")) {
       *out_type = dom::CanvasContextType::WebGL2;
       return true;
     }
   }
 
   if (str.EqualsLiteral("bitmaprenderer")) {
--- a/dom/canvas/test/reftest/filters/reftest-stylo.list
+++ b/dom/canvas/test/reftest/filters/reftest-stylo.list
@@ -1,13 +1,13 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 default-preferences pref(canvas.filters.enabled,true)
 
 fails asserts-if(stylo,2) == default-color.html default-color.html # bug 1324700
-# == drop-shadow.html drop-shadow.html
+fails asserts-if(stylo,2) == drop-shadow.html drop-shadow.html # bug 1324700
 fails asserts-if(stylo,2) == drop-shadow-transformed.html drop-shadow-transformed.html # bug 1324700
 fails asserts-if(stylo,2) == global-alpha.html global-alpha.html # bug 1324700
 fails asserts-if(stylo,2) == global-composite-operation.html global-composite-operation.html # bug 1324700
 fails asserts-if(stylo,1) == liveness.html liveness.html # bug 1324700
 fails asserts-if(stylo,2) == multiple-drop-shadows.html multiple-drop-shadows.html # bug 1324700
 fails asserts-if(stylo,2) == shadow.html shadow.html # bug 1324700
 fails asserts-if(stylo,2) == subregion-fill-paint.html subregion-fill-paint.html # bug 1324700
 fails asserts-if(stylo,2) == subregion-stroke-paint.html subregion-stroke-paint.html # bug 1324700
--- a/dom/canvas/test/reftest/reftest-stylo.list
+++ b/dom/canvas/test/reftest/reftest-stylo.list
@@ -54,17 +54,17 @@ fuzzy(1,30000) fails-if(winWidget&&layer
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&aa&________&_______&alpha webgl-color-test.html?frame=1&aa&________&_______&alpha
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&__&preserve&_______&alpha webgl-color-test.html?frame=1&__&preserve&_______&alpha
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=1&aa&preserve&_______&alpha webgl-color-test.html?frame=1&aa&preserve&_______&alpha
 skip-if(Android) == webgl-color-test.html?frame=1&__&________&premult&alpha webgl-color-test.html?frame=1&__&________&premult&alpha
 skip-if(Android) == webgl-color-test.html?frame=1&aa&________&premult&alpha webgl-color-test.html?frame=1&aa&________&premult&alpha
 skip-if(Android) == webgl-color-test.html?frame=1&__&preserve&premult&alpha webgl-color-test.html?frame=1&__&preserve&premult&alpha
 skip-if(Android) == webgl-color-test.html?frame=1&aa&preserve&premult&alpha webgl-color-test.html?frame=1&aa&preserve&premult&alpha
 
-# == webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____
+== webgl-color-test.html?frame=6&__&________&_______&_____ webgl-color-test.html?frame=6&__&________&_______&_____
 skip-if(Android) == webgl-color-test.html?frame=6&aa&________&_______&_____ webgl-color-test.html?frame=6&aa&________&_______&_____
 skip-if(Android) == webgl-color-test.html?frame=6&__&preserve&_______&_____ webgl-color-test.html?frame=6&__&preserve&_______&_____
 skip-if(Android) == webgl-color-test.html?frame=6&aa&preserve&_______&_____ webgl-color-test.html?frame=6&aa&preserve&_______&_____
 == webgl-color-test.html?frame=6&__&________&premult&_____ webgl-color-test.html?frame=6&__&________&premult&_____
 skip-if(Android) == webgl-color-test.html?frame=6&aa&________&premult&_____ webgl-color-test.html?frame=6&aa&________&premult&_____
 skip-if(Android) == webgl-color-test.html?frame=6&__&preserve&premult&_____ webgl-color-test.html?frame=6&__&preserve&premult&_____
 skip-if(Android) == webgl-color-test.html?frame=6&aa&preserve&premult&_____ webgl-color-test.html?frame=6&aa&preserve&premult&_____
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) == webgl-color-test.html?frame=6&__&________&_______&alpha webgl-color-test.html?frame=6&__&________&_______&alpha
--- a/dom/encoding/test/reftest/reftest-stylo.list
+++ b/dom/encoding/test/reftest/reftest-stylo.list
@@ -1,6 +1,6 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
-# == bug863728-1.html bug863728-1.html
+== bug863728-1.html bug863728-1.html
 == bug863728-2.html bug863728-2.html
 == bug863728-3.html bug863728-3.html
-# == bug945215-1.html bug945215-1.html
+== bug945215-1.html bug945215-1.html
 == bug945215-2.html bug945215-2.html
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -100,18 +100,21 @@ DataTransfer::DataTransfer(nsISupports* 
   // For these events, we want to be able to add data to the data transfer, so
   // clear the readonly state. Otherwise, the data is already present. For
   // external usage, cache the data from the native clipboard or drag.
   if (aEventMessage == eCut ||
       aEventMessage == eCopy ||
       aEventMessage == eDragStart) {
     mReadOnly = false;
   } else if (mIsExternal) {
-    if (aEventMessage == ePaste) {
-      CacheExternalClipboardFormats();
+    if (aEventMessage == ePasteNoFormatting) {
+      mEventMessage = ePaste;
+      CacheExternalClipboardFormats(true);
+    } else if (aEventMessage == ePaste) {
+      CacheExternalClipboardFormats(false);
     } else if (aEventMessage >= eDragDropEventFirst &&
                aEventMessage <= eDragDropEventLast) {
       CacheExternalDragFormats();
     }
   }
 }
 
 DataTransfer::DataTransfer(nsISupports* aParent,
@@ -1351,17 +1354,17 @@ DataTransfer::CacheExternalDragFormats()
       if (supported) {
         CacheExternalData(formats[f], c, sysPrincipal, /* hidden = */ f && hasFileData);
       }
     }
   }
 }
 
 void
-DataTransfer::CacheExternalClipboardFormats()
+DataTransfer::CacheExternalClipboardFormats(bool aPlainTextOnly)
 {
   NS_ASSERTION(mEventMessage == ePaste,
                "caching clipboard data for invalid event");
 
   // Called during the constructor for paste events to cache the formats
   // available on the clipboard. As with CacheExternalDragFormats, the
   // data will only be retrieved when needed.
 
@@ -1370,16 +1373,27 @@ DataTransfer::CacheExternalClipboardForm
   if (!clipboard || mClipboardType < 0) {
     return;
   }
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> sysPrincipal;
   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
 
+  if (aPlainTextOnly) {
+    bool supported;
+    const char* unicodeMime[] = { kUnicodeMime };
+    clipboard->HasDataMatchingFlavors(unicodeMime, 1, mClipboardType,
+                                      &supported);
+    if (supported) {
+      CacheExternalData(kUnicodeMime, 0, sysPrincipal, false);
+    }
+    return;
+  }
+
   // Check if the clipboard has any files
   bool hasFileData = false;
   const char *fileMime[] = { kFileMime };
   clipboard->HasDataMatchingFlavors(fileMime, 1, mClipboardType, &hasFileData);
 
   // We will be ignoring any application/x-moz-file files found in the paste
   // datatransfer within e10s, as they will fail to be sent over IPC. Because of
   // that, we will unset hasFileData, whether or not it would have been set.
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -302,17 +302,17 @@ protected:
   nsresult CacheExternalData(const char* aFormat, uint32_t aIndex,
                              nsIPrincipal* aPrincipal, bool aHidden);
 
   // caches the formats that exist in the drag service that were added by an
   // external drag
   void CacheExternalDragFormats();
 
   // caches the formats that exist in the clipboard
-  void CacheExternalClipboardFormats();
+  void CacheExternalClipboardFormats(bool aPlainTextOnly);
 
   FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
   nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
                              nsIPrincipal* aSubjectPrincipal,
                              nsIVariant** aData);
 
   nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
                              uint32_t aIndex, nsIPrincipal* aSubjectPrincipal);
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -134,16 +134,18 @@ support-files = bug1017086_inner.html
 [test_bug1017086_enable.html]
 support-files = bug1017086_inner.html
 [test_bug1079236.html]
 [test_bug1145910.html]
 [test_bug1150308.html]
 [test_bug1248459.html]
 [test_bug1264380.html]
 run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode.
+[test_bug1327798.html]
+subsuite = clipboard
 [test_clickevent_on_input.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_continuous_wheel_events.html]
 [test_dblclick_explicit_original_target.html]
 [test_dom_activate_event.html]
 [test_dom_keyboard_event.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dom_mouse_event.html]
new file mode 100644
--- /dev/null
+++ b/dom/events/test/test_bug1327798.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test for bug 1327798</title>
+<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?=id=1327798">Mozilla Bug 1327798</a>
+<p id="display"></p>
+<div id="content" style="display: none;"></div>
+
+<div contenteditable="true" id="editable1"><b>Formatted Text</b><br></div>
+<pre>
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+  var editable = document.getElementById("editable1");
+  editable.focus();
+
+  window.getSelection().selectAllChildren(editable1);
+
+  SpecialPowers.doCommand(window, "cmd_copy");
+
+  //--------- now check the content of the clipboard
+  var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
+                               .getService(SpecialPowers.Ci.nsIClipboard);
+  // does the clipboard contain text/unicode data ?
+  ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1, clipboard.kGlobalClipboard),
+     "clipboard contains unicode text");
+  // does the clipboard contain text/html data ?
+  ok(clipboard.hasDataMatchingFlavors(["text/html"], 1, clipboard.kGlobalClipboard),
+     "clipboard contains html text");
+
+  window.addEventListener("paste", (e) => {
+    ok(e.clipboardData.types.indexOf('text/html'), -1, "clipboardData shouldn't have text/html");
+    ok(e.clipboardData.getData('text/plain'), "Formatted Text",  "getData(text/plain) should return plain text");
+    SimpleTest.finish();
+  });
+
+  SpecialPowers.doCommand(window, "cmd_pasteNoFormatting");
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/file/File.cpp
+++ b/dom/file/File.cpp
@@ -8,16 +8,17 @@
 #include "FileBlobImpl.h"
 #include "MemoryBlobImpl.h"
 #include "MultipartBlobImpl.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/FileBinding.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "mozilla/dom/Promise.h"
+#include "nsXULAppAPI.h"
 
 namespace mozilla {
 namespace dom {
 
 File::File(nsISupports* aParent, BlobImpl* aImpl)
   : Blob(aParent, aImpl)
 {
   MOZ_ASSERT(aImpl->IsFile());
@@ -55,24 +56,26 @@ File::CreateMemoryFile(nsISupports* aPar
     new MemoryBlobImpl(aMemoryBuffer, aLength, aName,
                        aContentType, aLastModifiedDate));
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::CreateFromFile(nsISupports* aParent, nsIFile* aFile)
 {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile));
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::CreateFromFile(nsISupports* aParent, nsIFile* aFile,
                      const nsAString& aName, const nsAString& aContentType)
 {
+  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
   RefPtr<File> file = new File(aParent,
     new FileBlobImpl(aFile, aName, aContentType));
   return file.forget();
 }
 
 JSObject*
 File::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
@@ -161,18 +164,16 @@ File::Constructor(const GlobalObject& aG
 
 /* static */ already_AddRefed<Promise>
 File::CreateFromNsIFile(const GlobalObject& aGlobal,
                         nsIFile* aData,
                         const ChromeFilePropertyBag& aBag,
                         SystemCallerGuarantee aGuarantee,
                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
   RefPtr<Promise> promise =
     FileCreatorHelper::CreateFile(global, aData, aBag, true, aRv);
   return promise.forget();
 }
 
 /* static */ already_AddRefed<Promise>
--- a/dom/file/FileBlobImpl.cpp
+++ b/dom/file/FileBlobImpl.cpp
@@ -23,62 +23,67 @@ using namespace workers;
 NS_IMPL_ISUPPORTS_INHERITED0(FileBlobImpl, BlobImpl)
 
 FileBlobImpl::FileBlobImpl(nsIFile* aFile)
   : BaseBlobImpl(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
   , mFile(aFile)
   , mWholeFile(true)
 {
   MOZ_ASSERT(mFile, "must have file");
+  MOZ_ASSERT(XRE_IsParentProcess());
   // Lazily get the content type and size
   mContentType.SetIsVoid(true);
   mFile->GetLeafName(mName);
 }
 
 FileBlobImpl::FileBlobImpl(const nsAString& aName,
                            const nsAString& aContentType,
                            uint64_t aLength, nsIFile* aFile)
   : BaseBlobImpl(aName, aContentType, aLength, UINT64_MAX)
   , mFile(aFile)
   , mWholeFile(true)
 {
   MOZ_ASSERT(mFile, "must have file");
+  MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 FileBlobImpl::FileBlobImpl(const nsAString& aName,
                            const nsAString& aContentType,
                            uint64_t aLength, nsIFile* aFile,
                            int64_t aLastModificationDate)
   : BaseBlobImpl(aName, aContentType, aLength, aLastModificationDate)
   , mFile(aFile)
   , mWholeFile(true)
 {
   MOZ_ASSERT(mFile, "must have file");
+  MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 FileBlobImpl::FileBlobImpl(nsIFile* aFile, const nsAString& aName,
                            const nsAString& aContentType)
   : BaseBlobImpl(aName, aContentType, UINT64_MAX, INT64_MAX)
   , mFile(aFile)
   , mWholeFile(true)
 {
   MOZ_ASSERT(mFile, "must have file");
+  MOZ_ASSERT(XRE_IsParentProcess());
   if (aContentType.IsEmpty()) {
     // Lazily get the content type and size
     mContentType.SetIsVoid(true);
   }
 }
 
 FileBlobImpl::FileBlobImpl(const FileBlobImpl* aOther, uint64_t aStart,
                            uint64_t aLength, const nsAString& aContentType)
   : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength)
   , mFile(aOther->mFile)
   , mWholeFile(false)
 {
   MOZ_ASSERT(mFile, "must have file");
+  MOZ_ASSERT(XRE_IsParentProcess());
   mImmutable = aOther->mImmutable;
 }
 
 already_AddRefed<BlobImpl>
 FileBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                           const nsAString& aContentType,
                           ErrorResult& aRv)
 {
--- a/dom/file/FileCreatorHelper.cpp
+++ b/dom/file/FileCreatorHelper.cpp
@@ -26,17 +26,17 @@ namespace dom {
 
 /* static */ already_AddRefed<Promise>
 FileCreatorHelper::CreateFile(nsIGlobalObject* aGlobalObject,
                               nsIFile* aFile,
                               const ChromeFilePropertyBag& aBag,
                               bool aIsFromNsIFile,
                               ErrorResult& aRv)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
 
   RefPtr<Promise> promise = Promise::Create(aGlobalObject, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobalObject);
 
--- a/dom/file/tests/fileutils.js
+++ b/dom/file/tests/fileutils.js
@@ -1,14 +1,13 @@
 // Utility functions
 var testRanCounter = 0;
 var expectedTestCount = 0;
 
 function testHasRun() {
- //alert(testRanCounter);
  ++testRanCounter;
  if (testRanCounter == expectedTestCount) {
     SimpleTest.finish();
   }
 }
 
 
 function testFile(file, contents, test) {
@@ -113,24 +112,16 @@ function getXHRLoadHandler(expectedResul
 function convertXHRBinary(s) {
   var res = "";
   for (var i = 0; i < s.length; ++i) {
     res += String.fromCharCode(s.charCodeAt(i) & 255);
   }
   return res;
 }
 
-function testHasRun() {
- //alert(testRanCounter);
- ++testRanCounter;
- if (testRanCounter == expectedTestCount) {
-    SimpleTest.finish();
-  }
-}
-
 function gc() {
   window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
         .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils)
         .garbageCollect();
 }
 
 function checkMPSubmission(sub, expected) {
   function getPropCount(o) {
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -991,17 +991,17 @@ private:
             mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
             mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
   }
 
   AudibleState
   IsOwnerAudible() const
   {
     // Muted or the volume should not be ~0
-    if (mOwner->Muted() || (std::fabs(mOwner->Volume()) <= 1e-7)) {
+    if (mOwner->mMuted || (std::fabs(mOwner->Volume()) <= 1e-7)) {
       return AudioChannelService::AudibleState::eNotAudible;
     }
 
     // No audio track.
     if (!mOwner->HasAudio()) {
       return AudioChannelService::AudibleState::eNotAudible;
     }
 
@@ -2034,25 +2034,29 @@ void HTMLMediaElement::NotifyMediaTrackD
                         this, aTrack->AsAudioTrack() ? "Audio" : "Video",
                         NS_ConvertUTF16toUTF8(id).get()));
 #endif
 
   MOZ_ASSERT((!aTrack->AsAudioTrack() || !aTrack->AsAudioTrack()->Enabled()) &&
              (!aTrack->AsVideoTrack() || !aTrack->AsVideoTrack()->Selected()));
 
   if (aTrack->AsAudioTrack()) {
-    bool shouldMute = true;
-    for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) {
-      if ((*AudioTracks())[i]->Enabled()) {
-        shouldMute = false;
-        break;
+    // If we don't have any alive track , we don't need to mute MediaElement.
+    if (AudioTracks()->Length() > 0) {
+      bool shouldMute = true;
+      for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) {
+        if ((*AudioTracks())[i]->Enabled()) {
+          shouldMute = false;
+          break;
+        }
       }
-    }
-    if (shouldMute) {
-      SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
+
+      if (shouldMute) {
+        SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
+      }
     }
   } else if (aTrack->AsVideoTrack()) {
     if (mSrcStream) {
       MOZ_ASSERT(mSelectedVideoStreamTrack);
       if (mSelectedVideoStreamTrack && mMediaStreamSizeListener) {
         mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
         mMediaStreamSizeListener->Forget();
         mMediaStreamSizeListener = nullptr;
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -712,18 +712,18 @@ HTMLSelectElement::SetLength(uint32_t aL
   } else if (aLength > curlen) {
     if (aLength > MAX_DYNAMIC_SELECT_LENGTH) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
 
     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
 
-    nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::option,
-                                getter_AddRefs(nodeInfo));
+    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::option,
+                                 getter_AddRefs(nodeInfo));
 
     nsCOMPtr<nsINode> node = NS_NewHTMLOptionElement(nodeInfo.forget());
 
     RefPtr<nsTextNode> text = new nsTextNode(mNodeInfo->NodeInfoManager());
 
     aRv = node->AppendChildTo(text, false);
     if (aRv.Failed()) {
       return;
--- a/dom/html/HTMLTableElement.cpp
+++ b/dom/html/HTMLTableElement.cpp
@@ -398,26 +398,37 @@ HTMLTableElement::TBodies()
 
 already_AddRefed<nsGenericHTMLElement>
 HTMLTableElement::CreateTHead()
 {
   RefPtr<nsGenericHTMLElement> head = GetTHead();
   if (!head) {
     // Create a new head rowgroup.
     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-    nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::thead,
-                                getter_AddRefs(nodeInfo));
+    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::thead,
+                                 getter_AddRefs(nodeInfo));
 
     head = NS_NewHTMLTableSectionElement(nodeInfo.forget());
     if (!head) {
       return nullptr;
     }
 
-    ErrorResult rv;
-    nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild();
+    nsCOMPtr<nsIContent> refNode = nullptr;
+    for (refNode = nsINode::GetFirstChild();
+         refNode;
+         refNode = refNode->GetNextSibling()) {
+
+      if (refNode->IsHTMLElement() &&
+          !refNode->IsHTMLElement(nsGkAtoms::caption) &&
+          !refNode->IsHTMLElement(nsGkAtoms::colgroup)) {
+        break;
+      }
+    }
+
+    IgnoredErrorResult rv;
     nsINode::InsertBefore(*head, refNode, rv);
   }
   return head.forget();
 }
 
 void
 HTMLTableElement::DeleteTHead()
 {
@@ -431,18 +442,18 @@ HTMLTableElement::DeleteTHead()
 
 already_AddRefed<nsGenericHTMLElement>
 HTMLTableElement::CreateTFoot()
 {
   RefPtr<nsGenericHTMLElement> foot = GetTFoot();
   if (!foot) {
     // create a new foot rowgroup
     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-    nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tfoot,
-                                getter_AddRefs(nodeInfo));
+    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tfoot,
+                                 getter_AddRefs(nodeInfo));
 
     foot = NS_NewHTMLTableSectionElement(nodeInfo.forget());
     if (!foot) {
       return nullptr;
     }
     AppendChildTo(foot, true);
   }
 
@@ -462,25 +473,27 @@ HTMLTableElement::DeleteTFoot()
 
 already_AddRefed<nsGenericHTMLElement>
 HTMLTableElement::CreateCaption()
 {
   RefPtr<nsGenericHTMLElement> caption = GetCaption();
   if (!caption) {
     // Create a new caption.
     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-    nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::caption,
-                                getter_AddRefs(nodeInfo));
+    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::caption,
+                                 getter_AddRefs(nodeInfo));
 
     caption = NS_NewHTMLTableCaptionElement(nodeInfo.forget());
     if (!caption) {
       return nullptr;
     }
 
-    AppendChildTo(caption, true);
+    IgnoredErrorResult rv;
+    nsCOMPtr<nsINode> firsChild = nsINode::GetFirstChild();
+    nsINode::InsertBefore(*caption, firsChild, rv);
   }
   return caption.forget();
 }
 
 void
 HTMLTableElement::DeleteCaption()
 {
   HTMLTableCaptionElement* caption = GetCaption();
@@ -509,17 +522,17 @@ HTMLTableElement::CreateTBody()
        child;
        child = child->GetPreviousSibling()) {
     if (child->IsHTMLElement(nsGkAtoms::tbody)) {
       referenceNode = child->GetNextSibling();
       break;
     }
   }
 
-  ErrorResult rv;
+  IgnoredErrorResult rv;
   nsINode::InsertBefore(*newBody, referenceNode, rv);
 
   return newBody.forget();
 }
 
 already_AddRefed<nsGenericHTMLElement>
 HTMLTableElement::InsertRow(int32_t aIndex, ErrorResult& aError)
 {
@@ -555,18 +568,18 @@ HTMLTableElement::InsertRow(int32_t aInd
       refIndex = rowCount - 1;
     }
 
     RefPtr<Element> refRow = rows->Item(refIndex);
     nsCOMPtr<nsINode> parent = refRow->GetParentNode();
 
     // create the row
     RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-    nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
-                                getter_AddRefs(nodeInfo));
+    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr,
+                                 getter_AddRefs(nodeInfo));
 
     newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
 
     if (newRow) {
       // If aIndex is -1 or equal to the number of rows, the new row
       // is appended.
       if (aIndex == -1 || uint32_t(aIndex) == rowCount) {
         parent->AppendChild(*newRow, aError);
@@ -589,32 +602,32 @@ HTMLTableElement::InsertRow(int32_t aInd
       if (child->IsHTMLElement(nsGkAtoms::tbody)) {
         rowGroup = child;
         break;
       }
     }
 
     if (!rowGroup) { // need to create a TBODY
       RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-      nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody,
-                                  getter_AddRefs(nodeInfo));
+      nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tbody,
+                                   getter_AddRefs(nodeInfo));
 
       rowGroup = NS_NewHTMLTableSectionElement(nodeInfo.forget());
       if (rowGroup) {
         aError = AppendChildTo(rowGroup, true);
         if (aError.Failed()) {
           return nullptr;
         }
       }
     }
 
     if (rowGroup) {
       RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-      nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
-                                  getter_AddRefs(nodeInfo));
+      nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr,
+                                   getter_AddRefs(nodeInfo));
 
       newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
       if (newRow) {
         HTMLTableSectionElement* section =
           static_cast<HTMLTableSectionElement*>(rowGroup.get());
         nsIHTMLCollection* rows = section->Rows();
         nsCOMPtr<nsINode> refNode = rows->Item(0);
         rowGroup->InsertBefore(*newRow, refNode, aError);
--- a/dom/html/HTMLTableElement.h
+++ b/dom/html/HTMLTableElement.h
@@ -31,17 +31,18 @@ public:
   HTMLTableCaptionElement* GetCaption() const
   {
     return static_cast<HTMLTableCaptionElement*>(GetChild(nsGkAtoms::caption));
   }
   void SetCaption(HTMLTableCaptionElement* aCaption, ErrorResult& aError)
   {
     DeleteCaption();
     if (aCaption) {
-      nsINode::AppendChild(*aCaption, aError);
+      nsCOMPtr<nsINode> firstChild = nsINode::GetFirstChild();
+      nsINode::InsertBefore(*aCaption, firstChild, aError);
     }
   }
 
   void DeleteTFoot();
 
   already_AddRefed<nsGenericHTMLElement> CreateCaption();
 
   void DeleteCaption();
@@ -54,17 +55,28 @@ public:
   {
     if (aTHead && !aTHead->IsHTMLElement(nsGkAtoms::thead)) {
       aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
       return;
     }
 
     DeleteTHead();
     if (aTHead) {
-      nsCOMPtr<nsINode> refNode = nsINode::GetFirstChild();
+
+      nsCOMPtr<nsIContent> refNode = nullptr;
+      for (refNode = nsINode::GetFirstChild();
+           refNode;
+           refNode = refNode->GetNextSibling()) {
+        if (refNode->IsHTMLElement() &&
+            !refNode->IsHTMLElement(nsGkAtoms::caption) &&
+            !refNode->IsHTMLElement(nsGkAtoms::colgroup)) {
+          break;
+        }
+      }
+
       nsINode::InsertBefore(*aTHead, refNode, aError);
     }
   }
   already_AddRefed<nsGenericHTMLElement> CreateTHead();
 
   void DeleteTHead();
 
   HTMLTableSectionElement* GetTFoot() const
--- a/dom/html/HTMLTableRowElement.cpp
+++ b/dom/html/HTMLTableRowElement.cpp
@@ -170,18 +170,18 @@ HTMLTableRowElement::InsertCell(int32_t 
         aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
         return nullptr;
       }
     }
   }
 
   // create the cell
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-  nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::td,
-                              getter_AddRefs(nodeInfo));
+  nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::td,
+                               getter_AddRefs(nodeInfo));
 
   RefPtr<nsGenericHTMLElement> cell =
     NS_NewHTMLTableCellElement(nodeInfo.forget());
   if (!cell) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
--- a/dom/html/HTMLTableSectionElement.cpp
+++ b/dom/html/HTMLTableSectionElement.cpp
@@ -75,18 +75,18 @@ HTMLTableSectionElement::InsertRow(int32
     aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
   bool doInsert = (aIndex < int32_t(rowCount)) && (aIndex != -1);
 
   // create the row
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
-  nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
-                              getter_AddRefs(nodeInfo));
+  nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr,
+                               getter_AddRefs(nodeInfo));
 
   RefPtr<nsGenericHTMLElement> rowContent =
     NS_NewHTMLTableRowElement(nodeInfo.forget());
   if (!rowContent) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
--- a/dom/html/reftests/reftest-stylo.list
+++ b/dom/html/reftests/reftest-stylo.list
@@ -1,66 +1,65 @@
 # DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
 # autofocus attribute (we can't test with mochitests)
 include autofocus/reftest-stylo.list
 include toblob-todataurl/reftest-stylo.list
 
 fails == 41464-1a.html 41464-1a.html
 fails == 41464-1b.html 41464-1b.html
 fails == 52019-1.html 52019-1.html
-fails == 82711-1.html 82711-1.html
+fails == 82711-1.html 82711-1.html # Bug 1341637
 fails == 82711-2.html 82711-2.html
-# == 82711-1-ref.html 82711-1-ref.html
+fails == 82711-1-ref.html 82711-1-ref.html
 == 468263-1a.html 468263-1a.html
 == 468263-1b.html 468263-1b.html
 == 468263-1c.html 468263-1c.html
-# == 468263-1d.html 468263-1d.html
-# == 468263-2.html 468263-2.html
-# == 468263-2.html 468263-2.html
+== 468263-1d.html 468263-1d.html
+fails == 468263-2.html 468263-2.html # Bug 1341642
 fails == 484200-1.html 484200-1.html
-# == 485377.html 485377.html
+== 485377.html 485377.html
 == 557840.html 557840.html
 == 560059-video-dimensions.html 560059-video-dimensions.html
 fails == 573322-quirks.html 573322-quirks.html
 fails == 573322-no-quirks.html 573322-no-quirks.html
 fails == 596455-1a.html 596455-1a.html
 fails == 596455-1b.html 596455-1b.html
 fails == 596455-2a.html 596455-2a.html
 fails == 596455-2b.html 596455-2b.html
 fails == 610935.html 610935.html
 == 649134-1.html 649134-1.html
-# == 649134-2.html 649134-2.html
+== 649134-2.html 649134-2.html
 fails == 741776-1.vtt 741776-1.vtt
 
-# == bug448564-1_malformed.html bug448564-1_malformed.html
-# == bug448564-1_malformed.html bug448564-1_malformed.html
+== bug448564-1_malformed.html bug448564-1_malformed.html
+== bug448564-1_malformed.html bug448564-1_malformed.html
 
-# == bug448564-4a.html bug448564-4a.html
-# == bug502168-1_malformed.html bug502168-1_malformed.html
+== bug448564-4a.html bug448564-4a.html
+== bug502168-1_malformed.html bug502168-1_malformed.html
 
-# == responsive-image-load-shortcircuit.html responsive-image-load-shortcircuit.html
+fails == responsive-image-load-shortcircuit.html responsive-image-load-shortcircuit.html
 == image-load-shortcircuit-1.html image-load-shortcircuit-1.html
 == image-load-shortcircuit-2.html image-load-shortcircuit-2.html
 
 # Test that image documents taken into account CSS properties like
 # image-orientation when determining the size of the image.
 # (Fuzzy necessary due to pixel-wise comparison of different JPEGs.
 # The vast majority of the fuzziness comes from Linux and WinXP.)
-# == bug917595-iframe-1.html bug917595-iframe-1.html
+fails == bug917595-iframe-1.html bug917595-iframe-1.html # Bug 1341647
 fails == bug917595-exif-rotated.jpg bug917595-exif-rotated.jpg
 
 # Test support for SVG-as-image in <picture> elements.
 == bug1106522-1.html bug1106522-1.html
 == bug1106522-2.html bug1106522-2.html
 
 fails == href-attr-change-restyles.html href-attr-change-restyles.html
-# == figure.html figure.html
+== figure.html figure.html
 fails == pre-1.html pre-1.html
-# == table-border-1.html table-border-1.html
-# == table-border-2.html table-border-2.html
-# == table-border-2.html table-border-2.html
+fails == table-border-1.html table-border-1.html # Bug 1341651
+== table-border-2.html table-border-2.html
+== table-border-2.html table-border-2.html
 
 # Test imageset is using permissions.default.image
 # pref(permissions.default.image,1) HTTP == bug1196784-with-srcset.html bug1196784-with-srcset.html
 # pref(permissions.default.image,2) HTTP == bug1196784-with-srcset.html bug1196784-with-srcset.html
 
 # Test video with rotation information can be rotated.
 == bug1228601-video-rotation-90.html bug1228601-video-rotation-90.html
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -83,19 +83,17 @@
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
-#ifdef MOZ_GECKO_PROFILER
-#include "mozilla/ProfileGatherer.h"
-#endif
+#include "GeckoProfiler.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/WebBrowserPersistDocumentParent.h"
 #include "mozilla/Unused.h"
 #include "nsAnonymousTemporaryFile.h"
@@ -262,19 +260,16 @@ static NS_DEFINE_CID(kCClipboardCID, NS_
 
 #if defined(XP_WIN)
 // e10s forced enable pref, defined in nsAppRunner.cpp
 extern const char* kForceEnableE10sPref;
 #endif
 
 using base::ChildPrivileges;
 using base::KillProcess;
-#ifdef MOZ_GECKO_PROFILER
-using mozilla::ProfileGatherer;
-#endif
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
 #endif
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::power;
 using namespace mozilla::media;
 using namespace mozilla::embedding;
@@ -1196,20 +1191,17 @@ ContentParent::Init()
   DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   if (profilerActive) {
     nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
     rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-    nsCOMPtr<nsISupports> gatherer;
-    rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
-    MOZ_ASSERT(NS_SUCCEEDED(rv));
-    mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+    mIsProfilerActive = true;
 
     StartProfiler(currentProfilerParams);
   }
 #endif
 
   RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
   gmps->UpdateContentProcessGMPCapabilities();
 }
@@ -1584,18 +1576,18 @@ ContentParent::ActorDestroy(ActorDestroy
     gpu->RemoveListener(this);
   }
 
   RecvRemoveGeolocationListener();
 
   mConsoleService = nullptr;
 
 #ifdef MOZ_GECKO_PROFILER
-  if (mGatherer && !mProfile.IsEmpty()) {
-    mGatherer->OOPExitProfile(mProfile);
+  if (mIsProfilerActive && !mProfile.IsEmpty()) {
+    profiler_OOP_exit_profile(mProfile);
   }
 #endif
 
   if (obs) {
     RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
     props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
 
@@ -1823,34 +1815,16 @@ ContentParent::DestroyTestShell(TestShel
 
 TestShellParent*
 ContentParent::GetTestShellSingleton()
 {
   PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
   return static_cast<TestShellParent*>(p);
 }
 
-void
-ContentParent::InitializeMembers()
-{
-  mSubprocess = nullptr;
-  mChildID = gContentChildID++;
-  mGeolocationWatchID = -1;
-  mNumDestroyingTabs = 0;
-  mIsAvailable = true;
-  mIsAlive = true;
-  mSendPermissionUpdates = false;
-  mCalledClose = false;
-  mCalledKillHard = false;
-  mCreatedPairedMinidumps = false;
-  mShutdownPending = false;
-  mIPCOpen = true;
-  mHangMonitorActor = nullptr;
-}
-
 bool
 ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
 
   std::vector<std::string> extraArgs;
   extraArgs.push_back("-childID");
   char idStr[21];
@@ -1929,23 +1903,37 @@ ContentParent::LaunchSubprocess(ProcessP
   }
 
   return true;
 }
 
 ContentParent::ContentParent(ContentParent* aOpener,
                              const nsAString& aRemoteType)
   : nsIContentParent()
+  , mSubprocess(nullptr)
   , mLaunchTS(TimeStamp::Now())
   , mOpener(aOpener)
   , mRemoteType(aRemoteType)
+  , mChildID(gContentChildID++)
+  , mGeolocationWatchID(-1)
+  , mNumDestroyingTabs(0)
+  , mIsAvailable(true)
+  , mIsAlive(true)
+  , mSendPermissionUpdates(false)
   , mIsForBrowser(!mRemoteType.IsEmpty())
-{
-  InitializeMembers();  // Perform common initialization.
-
+  , mCalledClose(false)
+  , mCalledKillHard(false)
+  , mCreatedPairedMinidumps(false)
+  , mShutdownPending(false)
+  , mIPCOpen(true)
+  , mHangMonitorActor(nullptr)
+#ifdef MOZ_GECKO_PROFILER
+  , mIsProfilerActive(false)
+#endif
+{
   // Insert ourselves into the global linked list of ContentParent objects.
   if (!sContentParents) {
     sContentParents = new LinkedList<ContentParent>();
   }
   sContentParents->insertBack(this);
 
   // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
   // PID along with the warning.
@@ -2573,18 +2561,18 @@ ContentParent::Observe(nsISupports* aSub
       NS_ProcessNextEvent(nullptr, true);
     }
     NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
   }
 
 #ifdef MOZ_GECKO_PROFILER
   // Need to do this before the mIsAlive check to avoid missing profiles.
   if (!strcmp(aTopic, "profiler-subprocess-gather")) {
-    if (mGatherer) {
-      mGatherer->WillGatherOOPProfile();
+    if (mIsProfilerActive) {
+      profiler_will_gather_OOP_profile();
       if (mIsAlive && mSubprocess) {
         Unused << SendGatherProfile();
       }
     }
   }
   else if (!strcmp(aTopic, "profiler-subprocess")) {
     nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
     if (pse) {
@@ -2686,17 +2674,17 @@ ContentParent::Observe(nsISupports* aSub
   }
 #endif
 #ifdef MOZ_GECKO_PROFILER
   else if (!strcmp(aTopic, "profiler-started")) {
     nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
     StartProfiler(params);
   }
   else if (!strcmp(aTopic, "profiler-stopped")) {
-    mGatherer = nullptr;
+    mIsProfilerActive = false;
     Unused << SendStopProfiler();
   }
   else if (!strcmp(aTopic, "profiler-paused")) {
     Unused << SendPauseProfiler(true);
   }
   else if (!strcmp(aTopic, "profiler-resumed")) {
     Unused << SendPauseProfiler(false);
   }
@@ -4538,21 +4526,21 @@ ContentParent::RecvCreateWindowInDiffere
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvProfile(const nsCString& aProfile)
 {
 #ifdef MOZ_GECKO_PROFILER
-  if (NS_WARN_IF(!mGatherer)) {
+  if (NS_WARN_IF(!mIsProfilerActive)) {
     return IPC_OK();
   }
   mProfile = aProfile;
-  mGatherer->GatheredOOPProfile();
+  profiler_gathered_OOP_profile();
 #endif
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut)
 {
   gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
@@ -4674,19 +4662,17 @@ ContentParent::StartProfiler(nsIProfiler
   ipcParams.threadFilters() = aParams->GetThreadFilterNames();
 
   Unused << SendStartProfiler(ipcParams);
 
   nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
   if (NS_WARN_IF(!profiler)) {
     return;
   }
-  nsCOMPtr<nsISupports> gatherer;
-  profiler->GetProfileGatherer(getter_AddRefs(gatherer));
-  mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+  mIsProfilerActive = true;
 #endif
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvNotifyPushObservers(const nsCString& aScope,
                                        const IPC::Principal& aPrincipal,
                                        const nsString& aMessageId)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -49,19 +49,16 @@ class nsICycleCollectorLogSink;
 class nsIDumpGCAndCCLogsCallback;
 class nsITabParent;
 class nsITimer;
 class ParentIdleListener;
 class nsIWidget;
 
 namespace mozilla {
 class PRemoteSpellcheckEngineParent;
-#ifdef MOZ_GECKO_PROFILER
-class ProfileGatherer;
-#endif
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 class SandboxBroker;
 class SandboxBrokerPolicyFactory;
 #endif
 
 class PreallocatedProcessManagerImpl;
 
@@ -670,19 +667,16 @@ private:
                      nsCOMPtr<nsITabParent>& aNewTabParent,
                      bool* aWindowIsNew);
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
   ContentParent(ContentParent* aOpener,
                 const nsAString& aRemoteType);
 
-  // The common initialization for the constructors.
-  void InitializeMembers();
-
   // Launch the subprocess and associated initialization.
   // Returns false if the process fails to start.
   bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
 
   // Common initialization after sub process launch or adoption.
   void InitInternal(ProcessPriority aPriority,
                     bool aSetupOffMainThreadCompositing,
                     bool aSendRegisteredChrome);
@@ -1186,17 +1180,17 @@ private:
   // Dup of child's X socket, used to scope its resources to this
   // object instead of the child process's lifetime.
   ScopedClose mChildXSocketFdDup;
 #endif
 
   PProcessHangMonitorParent* mHangMonitorActor;
 
 #ifdef MOZ_GECKO_PROFILER
-  RefPtr<mozilla::ProfileGatherer> mGatherer;
+  bool mIsProfilerActive;
 #endif
   nsCString mProfile;
 
   UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
   UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
   mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -1,278 +1,279 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ContentPrefs.h"
-
-const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
-  "accessibility.monoaudio.enable",
-  "accessibility.mouse_focuses_formcontrol",
-  "accessibility.tabfocus_applies_to_xul",
-  "app.update.channel",
-  "browser.dom.window.dump.enabled",
-  "browser.sessionhistory.max_entries",
-  "browser.sessionhistory.max_total_viewers",
-  "content.cors.disable",
-  "content.cors.no_private_data",
-  "content.notify.backoffcount",
-  "content.notify.interval",
-  "content.notify.ontimer",
-  "content.sink.enable_perf_mode",
-  "content.sink.event_probe_rate",
-  "content.sink.initial_perf_time",
-  "content.sink.interactive_deflect_count",
-  "content.sink.interactive_parse_time",
-  "content.sink.interactive_time",
-  "content.sink.pending_event_mode",
-  "content.sink.perf_deflect_count",
-  "content.sink.perf_parse_time",
-  "device.storage.prompt.testing",
-  "device.storage.writable.name",
-  "dom.allow_XUL_XBL_for_file",
-  "dom.allow_cut_copy",
-  "dom.enable_frame_timing",
-  "dom.enable_performance",
-  "dom.enable_resource_timing",
-  "dom.event.handling-user-input-time-limit",
-  "dom.event.touch.coalescing.enabled",
-  "dom.forms.autocomplete.experimental",
-  "dom.ipc.processPriorityManager.backgroundGracePeriodMS",
-  "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS",
-  "dom.max_chrome_script_run_time",
-  "dom.max_script_run_time",
-  "dom.mozBrowserFramesEnabled",
-  "dom.performance.enable_notify_performance_timing",
-  "dom.performance.enable_user_timing_logging",
-  "dom.storage.testing",
-  "dom.url.encode_decode_hash",
-  "dom.url.getters_decode_hash",
-  "dom.use_watchdog",
-  "dom.vibrator.enabled",
-  "dom.vibrator.max_vibrate_list_len",
-  "dom.vibrator.max_vibrate_ms",
-  "focusmanager.testmode",
-  "font.size.inflation.disabledInMasterProcess",
-  "font.size.inflation.emPerLine",
-  "font.size.inflation.forceEnabled",
-  "font.size.inflation.lineThreshold",
-  "font.size.inflation.mappingIntercept",
-  "font.size.inflation.maxRatio",
-  "font.size.inflation.minTwips",
-  "full-screen-api.allow-trusted-requests-only",
-  "full-screen-api.enabled",
-  "full-screen-api.unprefix.enabled",
-  "gfx.font_rendering.opentype_svg.enabled",
-  "hangmonitor.timeout",
-  "html5.flushtimer.initialdelay",
-  "html5.flushtimer.subsequentdelay",
-  "html5.offmainthread",
-  "intl.charset.fallback.tld",
-  "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
-  "javascript.enabled",
-  "javascript.options.asmjs",
-  "javascript.options.asyncstack",
-  "javascript.options.baselinejit",
-  "javascript.options.baselinejit.threshold",
-  "javascript.options.baselinejit.unsafe_eager_compilation",
-  "javascript.options.discardSystemSource",
-  "javascript.options.dump_stack_on_debuggee_would_run",
-  "javascript.options.gczeal",
-  "javascript.options.gczeal.frequency",
-  "javascript.options.ion",
-  "javascript.options.ion.offthread_compilation",
-  "javascript.options.ion.threshold",
-  "javascript.options.ion.unsafe_eager_compilation",
-  "javascript.options.jit.full_debug_checks",
-  "javascript.options.native_regexp",
-  "javascript.options.parallel_parsing",
-  "javascript.options.shared_memory",
-  "javascript.options.strict",
-  "javascript.options.strict.debug",
-  "javascript.options.throw_on_asmjs_validation_failure",
-  "javascript.options.throw_on_debuggee_would_run",
-  "javascript.options.wasm",
-  "javascript.options.wasm_baselinejit",
-  "javascript.options.werror",
-  "javascript.use_us_english_locale",
-  "jsloader.reuseGlobal",
-  "layout.css.all-shorthand.enabled",
-  "layout.css.background-blend-mode.enabled",
-  "layout.css.background-clip-text.enabled",
-  "layout.css.box-decoration-break.enabled",
-  "layout.css.color-adjust.enabled",
-  "layout.css.contain.enabled",
-  "layout.css.control-characters.visible",
-  "layout.css.display-flow-root.enabled",
-  "layout.css.expensive-style-struct-assertions.enabled",
-  "layout.css.float-logical-values.enabled",
-  "layout.css.font-variations.enabled",
-  "layout.css.grid.enabled",
-  "layout.css.image-orientation.enabled",
-  "layout.css.initial-letter.enabled",
-  "layout.css.isolation.enabled",
-  "layout.css.mix-blend-mode.enabled",
-  "layout.css.object-fit-and-position.enabled",
-  "layout.css.osx-font-smoothing.enabled",
-  "layout.css.overflow-clip-box.enabled",
-  "layout.css.prefixes.animations",
-  "layout.css.prefixes.border-image",
-  "layout.css.prefixes.box-sizing",
-  "layout.css.prefixes.device-pixel-ratio-webkit",
-  "layout.css.prefixes.font-features",
-  "layout.css.prefixes.gradients",
-  "layout.css.prefixes.transforms",
-  "layout.css.prefixes.transitions",
-  "layout.css.prefixes.webkit",
-  "layout.css.scope-pseudo.enabled",
-  "layout.css.scroll-behavior.property-enabled",
-  "layout.css.scroll-snap.enabled",
-  "layout.css.servo.enabled",
-  "layout.css.shape-outside.enabled",
-  "layout.css.text-align-unsafe-value.enabled",
-  "layout.css.text-combine-upright-digits.enabled",
-  "layout.css.text-combine-upright.enabled",
-  "layout.css.touch_action.enabled",
-  "layout.css.unprefixing-service.enabled",
-  "layout.css.unprefixing-service.globally-whitelisted",
-  "layout.css.unprefixing-service.include-test-domains",
-  "layout.css.variables.enabled",
-  "layout.css.visited_links_enabled",
-  "layout.idle_period.required_quiescent_frames",
-  "layout.idle_period.time_limit",
-  "layout.interruptible-reflow.enabled",
-  "mathml.disabled",
-  "media.apple.forcevda",
-  "media.clearkey.persistent-license.enabled",
-  "media.cubeb_latency_msg_frames",
-  "media.cubeb_latency_playback_ms",
-  "media.decoder-doctor.wmf-disabled-is-failure",
-  "media.decoder.fuzzing.dont-delay-inputexhausted",
-  "media.decoder.fuzzing.enabled",
-  "media.decoder.fuzzing.video-output-minimum-interval-ms",
-  "media.decoder.limit",
-  "media.decoder.recycle.enabled",
-  "media.dormant-on-pause-timeout-ms",
-  "media.eme.audio.blank",
-  "media.eme.enabled",
-  "media.eme.video.blank",
-  "media.ffmpeg.enabled",
-  "media.ffvpx.enabled",
-  "media.flac.enabled",
-  "media.forcestereo.enabled",
-  "media.gmp.async-shutdown-timeout",
-  "media.gmp.decoder.aac",
-  "media.gmp.decoder.enabled",
-  "media.gmp.decoder.h264",
-  "media.gmp.insecure.allow",
-  "media.gpu-process-decoder",
-  "media.libavcodec.allow-obsolete",
-  "media.num-decode-threads",
-  "media.ogg.enabled",
-  "media.ogg.flac.enabled",
-  "media.resampling.enabled",
-  "media.resampling.rate",
-  "media.ruin-av-sync.enabled",
-  "media.rust.test_mode",
-  "media.suspend-bkgnd-video.delay-ms",
-  "media.suspend-bkgnd-video.enabled",
-  "media.use-blank-decoder",
-  "media.video_stats.enabled",
-  "media.volume_scale",
-  "media.webspeech.recognition.enable",
-  "media.webspeech.recognition.force_enable",
-  "media.webspeech.synth.force_global_queue",
-  "media.webspeech.test.enable",
-  "media.webspeech.test.fake_fsm_events",
-  "media.webspeech.test.fake_recognition_service",
-  "media.wmf.allow-unsupported-resolutions",
-  "media.wmf.decoder.thread-count",
-  "media.wmf.enabled",
-  "media.wmf.skip-blacklist",
-  "media.wmf.vp9.enabled",
-  "memory.free_dirty_pages",
-  "memory.low_commit_space_threshold_mb",
-  "memory.low_memory_notification_interval_ms",
-  "memory.low_physical_memory_threshold_mb",
-  "memory.low_virtual_mem_threshold_mb",
-  "network.IDN.blacklist_chars",
-  "network.IDN.restriction_profile",
-  "network.IDN.use_whitelist",
-  "network.IDN_show_punycode",
-  "network.buffer.cache.count",
-  "network.buffer.cache.size",
-  "network.captive-portal-service.enabled",
-  "network.cookie.cookieBehavior",
-  "network.cookie.lifetimePolicy",
-  "network.dns.disablePrefetch",
-  "network.dns.disablePrefetchFromHTTPS",
-  "network.jar.block-remote-files",
-  "network.loadinfo.skip_type_assertion",
-  "network.notify.changed",
-  "network.offline-mirrors-connectivity",
-  "network.protocol-handler.external.jar",
-  "network.proxy.type",
-  "network.security.ports.banned",
-  "network.security.ports.banned.override",
-  "network.standard-url.enable-rust",
-  "network.standard-url.max-length",
-  "network.sts.max_time_for_events_between_two_polls",
-  "network.sts.max_time_for_pr_close_during_shutdown",
-  "network.tcp.keepalive.enabled",
-  "network.tcp.keepalive.idle_time",
-  "network.tcp.keepalive.probe_count",
-  "network.tcp.keepalive.retry_interval",
-  "network.tcp.sendbuffer",
-  "nglayout.debug.invalidation",
-  "privacy.donottrackheader.enabled",
-  "privacy.firstparty.isolate",
-  "privacy.firstparty.isolate.restrict_opener_access",
-  "privacy.resistFingerprinting",
-  "security.data_uri.inherit_security_context",
-  "security.fileuri.strict_origin_policy",
-  "security.sandbox.content.level",
-  "security.sandbox.content.tempDirSuffix",
-  "security.sandbox.logging.enabled",
-  "security.sandbox.mac.track.violations",
-  "security.sandbox.windows.log",
-  "security.sandbox.windows.log.stackTraceDepth",
-  "shutdown.watchdog.timeoutSecs",
-  "signed.applets.codebase_principal_support",
-  "svg.disabled",
-  "svg.display-lists.hit-testing.enabled",
-  "svg.display-lists.painting.enabled",
-  "svg.new-getBBox.enabled",
-  "svg.paint-order.enabled",
-  "svg.path-caching.enabled",
-  "svg.transform-box.enabled",
-  "toolkit.asyncshutdown.crash_timeout",
-  "toolkit.asyncshutdown.log",
-  "toolkit.osfile.log",
-  "toolkit.osfile.log.redirect",
-  "toolkit.telemetry.enabled",
-  "toolkit.telemetry.idleTimeout",
-  "toolkit.telemetry.initDelay",
-  "toolkit.telemetry.log.dump",
-  "toolkit.telemetry.log.level",
-  "toolkit.telemetry.minSubsessionLength",
-  "toolkit.telemetry.scheduler.idleTickInterval",
-  "toolkit.telemetry.scheduler.tickInterval",
-  "toolkit.telemetry.unified",
-  "ui.key.menuAccessKeyFocuses",
-  "ui.popup.disable_autohide",
-  "ui.use_activity_cursor",
-  "view_source.editor.external"};
-
-const char** mozilla::dom::ContentPrefs::GetContentPrefs(size_t* aCount)
-{
-  *aCount = ArrayLength(ContentPrefs::gInitPrefs);
-  return gInitPrefs;
-}
-
-const char*  mozilla::dom::ContentPrefs::GetContentPref(size_t aIndex)
-{
-  MOZ_ASSERT(aIndex < ArrayLength(ContentPrefs::gInitPrefs));
-  return gInitPrefs[aIndex];
-}
-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContentPrefs.h"
+
+const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
+  "accessibility.monoaudio.enable",
+  "accessibility.mouse_focuses_formcontrol",
+  "accessibility.tabfocus_applies_to_xul",
+  "app.update.channel",
+  "browser.dom.window.dump.enabled",
+  "browser.sessionhistory.max_entries",
+  "browser.sessionhistory.max_total_viewers",
+  "content.cors.disable",
+  "content.cors.no_private_data",
+  "content.notify.backoffcount",
+  "content.notify.interval",
+  "content.notify.ontimer",
+  "content.sink.enable_perf_mode",
+  "content.sink.event_probe_rate",
+  "content.sink.initial_perf_time",
+  "content.sink.interactive_deflect_count",
+  "content.sink.interactive_parse_time",
+  "content.sink.interactive_time",
+  "content.sink.pending_event_mode",
+  "content.sink.perf_deflect_count",
+  "content.sink.perf_parse_time",
+  "device.storage.prompt.testing",
+  "device.storage.writable.name",
+  "dom.allow_XUL_XBL_for_file",
+  "dom.allow_cut_copy",
+  "dom.enable_frame_timing",
+  "dom.enable_performance",
+  "dom.enable_resource_timing",
+  "dom.event.handling-user-input-time-limit",
+  "dom.event.touch.coalescing.enabled",
+  "dom.forms.autocomplete.experimental",
+  "dom.ipc.processPriorityManager.backgroundGracePeriodMS",
+  "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS",
+  "dom.max_chrome_script_run_time",
+  "dom.max_script_run_time",
+  "dom.mozBrowserFramesEnabled",
+  "dom.performance.enable_notify_performance_timing",
+  "dom.performance.enable_user_timing_logging",
+  "dom.storage.testing",
+  "dom.url.encode_decode_hash",
+  "dom.url.getters_decode_hash",
+  "dom.use_watchdog",
+  "dom.vibrator.enabled",
+  "dom.vibrator.max_vibrate_list_len",
+  "dom.vibrator.max_vibrate_ms",
+  "focusmanager.testmode",
+  "font.size.inflation.disabledInMasterProcess",
+  "font.size.inflation.emPerLine",
+  "font.size.inflation.forceEnabled",
+  "font.size.inflation.lineThreshold",
+  "font.size.inflation.mappingIntercept",
+  "font.size.inflation.maxRatio",
+  "font.size.inflation.minTwips",
+  "full-screen-api.allow-trusted-requests-only",
+  "full-screen-api.enabled",
+  "full-screen-api.unprefix.enabled",
+  "gfx.font_rendering.opentype_svg.enabled",
+  "hangmonitor.timeout",
+  "html5.flushtimer.initialdelay",
+  "html5.flushtimer.subsequentdelay",
+  "html5.offmainthread",
+  "intl.charset.fallback.tld",
+  "intl.ime.hack.on_ime_unaware_apps.fire_key_events_for_composition",
+  "javascript.enabled",
+  "javascript.options.asmjs",
+  "javascript.options.asyncstack",
+  "javascript.options.baselinejit",
+  "javascript.options.baselinejit.threshold",
+  "javascript.options.baselinejit.unsafe_eager_compilation",
+  "javascript.options.discardSystemSource",
+  "javascript.options.dump_stack_on_debuggee_would_run",
+  "javascript.options.gczeal",
+  "javascript.options.gczeal.frequency",
+  "javascript.options.ion",
+  "javascript.options.ion.offthread_compilation",
+  "javascript.options.ion.threshold",
+  "javascript.options.ion.unsafe_eager_compilation",
+  "javascript.options.jit.full_debug_checks",
+  "javascript.options.native_regexp",
+  "javascript.options.parallel_parsing",
+  "javascript.options.shared_memory",
+  "javascript.options.strict",
+  "javascript.options.strict.debug",
+  "javascript.options.throw_on_asmjs_validation_failure",
+  "javascript.options.throw_on_debuggee_would_run",
+  "javascript.options.wasm",
+  "javascript.options.wasm_baselinejit",
+  "javascript.options.werror",
+  "javascript.use_us_english_locale",
+  "jsloader.reuseGlobal",
+  "layout.css.all-shorthand.enabled",
+  "layout.css.background-blend-mode.enabled",
+  "layout.css.background-clip-text.enabled",
+  "layout.css.box-decoration-break.enabled",
+  "layout.css.color-adjust.enabled",
+  "layout.css.contain.enabled",
+  "layout.css.control-characters.visible",
+  "layout.css.display-flow-root.enabled",
+  "layout.css.expensive-style-struct-assertions.enabled",
+  "layout.css.float-logical-values.enabled",
+  "layout.css.font-variations.enabled",
+  "layout.css.grid.enabled",
+  "layout.css.image-orientation.enabled",
+  "layout.css.initial-letter.enabled",
+  "layout.css.isolation.enabled",
+  "layout.css.mix-blend-mode.enabled",
+  "layout.css.object-fit-and-position.enabled",
+  "layout.css.osx-font-smoothing.enabled",
+  "layout.css.overflow-clip-box.enabled",
+  "layout.css.prefixes.animations",
+  "layout.css.prefixes.border-image",
+  "layout.css.prefixes.box-sizing",
+  "layout.css.prefixes.device-pixel-ratio-webkit",
+  "layout.css.prefixes.font-features",
+  "layout.css.prefixes.gradients",
+  "layout.css.prefixes.transforms",
+  "layout.css.prefixes.transitions",
+  "layout.css.prefixes.webkit",
+  "layout.css.scope-pseudo.enabled",
+  "layout.css.scroll-behavior.property-enabled",
+  "layout.css.scroll-snap.enabled",
+  "layout.css.servo.enabled",
+  "layout.css.shape-outside.enabled",
+  "layout.css.text-align-unsafe-value.enabled",
+  "layout.css.text-combine-upright-digits.enabled",
+  "layout.css.text-combine-upright.enabled",
+  "layout.css.touch_action.enabled",
+  "layout.css.unprefixing-service.enabled",
+  "layout.css.unprefixing-service.globally-whitelisted",
+  "layout.css.unprefixing-service.include-test-domains",
+  "layout.css.variables.enabled",
+  "layout.css.visited_links_enabled",
+  "layout.idle_period.required_quiescent_frames",
+  "layout.idle_period.time_limit",
+  "layout.interruptible-reflow.enabled",
+  "mathml.disabled",
+  "media.apple.forcevda",
+  "media.clearkey.persistent-license.enabled",
+  "media.cubeb_latency_msg_frames",
+  "media.cubeb_latency_playback_ms",
+  "media.decoder-doctor.wmf-disabled-is-failure",
+  "media.decoder.fuzzing.dont-delay-inputexhausted",
+  "media.decoder.fuzzing.enabled",
+  "media.decoder.fuzzing.video-output-minimum-interval-ms",
+  "media.decoder.limit",
+  "media.decoder.recycle.enabled",
+  "media.dormant-on-pause-timeout-ms",
+  "media.eme.audio.blank",
+  "media.eme.enabled",
+  "media.eme.video.blank",
+  "media.ffmpeg.enabled",
+  "media.ffvpx.enabled",
+  "media.ffvpx.low-latency.enabled",
+  "media.flac.enabled",
+  "media.forcestereo.enabled",
+  "media.gmp.async-shutdown-timeout",
+  "media.gmp.decoder.aac",
+  "media.gmp.decoder.enabled",
+  "media.gmp.decoder.h264",
+  "media.gmp.insecure.allow",
+  "media.gpu-process-decoder",
+  "media.libavcodec.allow-obsolete",
+  "media.num-decode-threads",
+  "media.ogg.enabled",
+  "media.ogg.flac.enabled",
+  "media.resampling.enabled",
+  "media.resampling.rate",
+  "media.ruin-av-sync.enabled",
+  "media.rust.test_mode",
+  "media.suspend-bkgnd-video.delay-ms",
+  "media.suspend-bkgnd-video.enabled",
+  "media.use-blank-decoder",
+  "media.video_stats.enabled",
+  "media.volume_scale",
+  "media.webspeech.recognition.enable",
+  "media.webspeech.recognition.force_enable",
+  "media.webspeech.synth.force_global_queue",
+  "media.webspeech.test.enable",
+  "media.webspeech.test.fake_fsm_events",
+  "media.webspeech.test.fake_recognition_service",
+  "media.wmf.allow-unsupported-resolutions",
+  "media.wmf.decoder.thread-count",
+  "media.wmf.enabled",
+  "media.wmf.skip-blacklist",
+  "media.wmf.vp9.enabled",
+  "memory.free_dirty_pages",
+  "memory.low_commit_space_threshold_mb",
+  "memory.low_memory_notification_interval_ms",
+  "memory.low_physical_memory_threshold_mb",
+  "memory.low_virtual_mem_threshold_mb",
+  "network.IDN.blacklist_chars",
+  "network.IDN.restriction_profile",
+  "network.IDN.use_whitelist",
+  "network.IDN_show_punycode",
+  "network.buffer.cache.count",
+  "network.buffer.cache.size",
+  "network.captive-portal-service.enabled",
+  "network.cookie.cookieBehavior",
+  "network.cookie.lifetimePolicy",
+  "network.dns.disablePrefetch",
+  "network.dns.disablePrefetchFromHTTPS",
+  "network.jar.block-remote-files",
+  "network.loadinfo.skip_type_assertion",
+  "network.notify.changed",
+  "network.offline-mirrors-connectivity",
+  "network.protocol-handler.external.jar",
+  "network.proxy.type",
+  "network.security.ports.banned",
+  "network.security.ports.banned.override",
+  "network.standard-url.enable-rust",
+  "network.standard-url.max-length",
+  "network.sts.max_time_for_events_between_two_polls",
+  "network.sts.max_time_for_pr_close_during_shutdown",
+  "network.tcp.keepalive.enabled",
+  "network.tcp.keepalive.idle_time",
+  "network.tcp.keepalive.probe_count",
+  "network.tcp.keepalive.retry_interval",
+  "network.tcp.sendbuffer",
+  "nglayout.debug.invalidation",
+  "privacy.donottrackheader.enabled",
+  "privacy.firstparty.isolate",
+  "privacy.firstparty.isolate.restrict_opener_access",
+  "privacy.resistFingerprinting",
+  "security.data_uri.inherit_security_context",
+  "security.fileuri.strict_origin_policy",
+  "security.sandbox.content.level",
+  "security.sandbox.content.tempDirSuffix",
+  "security.sandbox.logging.enabled",
+  "security.sandbox.mac.track.violations",
+  "security.sandbox.windows.log",
+  "security.sandbox.windows.log.stackTraceDepth",
+  "shutdown.watchdog.timeoutSecs",
+  "signed.applets.codebase_principal_support",
+  "svg.disabled",
+  "svg.display-lists.hit-testing.enabled",
+  "svg.display-lists.painting.enabled",
+  "svg.new-getBBox.enabled",
+  "svg.paint-order.enabled",
+  "svg.path-caching.enabled",
+  "svg.transform-box.enabled",
+  "toolkit.asyncshutdown.crash_timeout",
+  "toolkit.asyncshutdown.log",
+  "toolkit.osfile.log",
+  "toolkit.osfile.log.redirect",
+  "toolkit.telemetry.enabled",
+  "toolkit.telemetry.idleTimeout",
+  "toolkit.telemetry.initDelay",
+  "toolkit.telemetry.log.dump",
+  "toolkit.telemetry.log.level",
+  "toolkit.telemetry.minSubsessionLength",
+  "toolkit.telemetry.scheduler.idleTickInterval",
+  "toolkit.telemetry.scheduler.tickInterval",
+  "toolkit.telemetry.unified",
+  "ui.key.menuAccessKeyFocuses",
+  "ui.popup.disable_autohide",
+  "ui.use_activity_cursor",
+  "view_source.editor.external"};
+
+const char** mozilla::dom::ContentPrefs::GetContentPrefs(size_t* aCount)
+{
+  *aCount = ArrayLength(ContentPrefs::gInitPrefs);
+  return gInitPrefs;
+}
+
+const char*  mozilla::dom::ContentPrefs::GetContentPref(size_t aIndex)
+{
+  MOZ_ASSERT(aIndex < ArrayLength(ContentPrefs::gInitPrefs));
+  return gInitPrefs[aIndex];
+}
+
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2676,20 +2676,26 @@ already_AddRefed<nsILoadContext>
 TabParent::GetLoadContext()
 {
   nsCOMPtr<nsILoadContext> loadContext;
   if (mLoadContext) {
     loadContext = mLoadContext;
   } else {
     bool isPrivate = mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
     SetPrivateBrowsingAttributes(isPrivate);
+    bool useTrackingProtection = false;
+    nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+    if (docShell) {
+      docShell->GetUseTrackingProtection(&useTrackingProtection);
+    }
     loadContext = new LoadContext(GetOwnerElement(),
                                   true /* aIsContent */,
                                   isPrivate,
                                   mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW,
+                                  useTrackingProtection,
                                   OriginAttributesRef());
     mLoadContext = loadContext;
   }
   return loadContext.forget();
 }
 
 NS_IMETHODIMP
 TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
@@ -2989,17 +2995,18 @@ public:
   NS_IMETHOD GetIsContent(bool*) NO_IMPL
   NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL
   NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL
   NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL
   NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL
   NS_IMETHOD SetRemoteTabs(bool) NO_IMPL
-  NS_IMETHOD IsTrackingProtectionOn(bool*) NO_IMPL
+  NS_IMETHOD GetUseTrackingProtection(bool*) NO_IMPL
+  NS_IMETHOD SetUseTrackingProtection(bool) NO_IMPL
 #undef NO_IMPL
 
 protected:
   ~FakeChannel() {}
 
   nsCOMPtr<nsIURI> mUri;
   uint64_t mCallbackId;
   nsCOMPtr<Element> mElement;
--- a/dom/locales/en-US/chrome/layout/layout_errors.properties
+++ b/dom/locales/en-US/chrome/layout/layout_errors.properties
@@ -6,19 +6,16 @@ ImageMapRectBoundsError=The “coords” attribute of the <area shape="rect"> tag is not in the “left,top,right,bottom” format.
 ImageMapCircleWrongNumberOfCoords=The “coords” attribute of the <area shape="circle"> tag is not in the “center-x,center-y,radius” format.
 ImageMapCircleNegativeRadius=The “coords” attribute of the <area shape="circle"> tag has a negative radius.
 ImageMapPolyWrongNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is not in the “x1,y1,x2,y2 …” format.
 ImageMapPolyOddNumberOfCoords=The “coords” attribute of the <area shape="poly"> tag is missing the last “y” coordinate (the correct format is “x1,y1,x2,y2 …”).
 
 TablePartRelPosWarning=Relative positioning of table rows and row groups is now supported. This site may need to be updated because it may depend on this feature having no effect.
 ScrollLinkedEffectFound2=This site appears to use a scroll-linked positioning effect. This may not work well with asynchronous panning; see https://developer.mozilla.org/docs/Mozilla/Performance/ScrollLinkedEffects for further details and to join the discussion on related tools and features!
 
-## LOCALIZATION NOTE(CompositorAnimationWarningContentTooSmall):
-## (%1$S, %2$S) is a pair of integer values of the frame size
-CompositorAnimationWarningContentTooSmall=Animation cannot be run on the compositor because frame size (%1$S, %2$S) is smaller than (16, 16)
 ## LOCALIZATION NOTE(CompositorAnimationWarningContentTooLarge2):
 ## (%1$S, %2$S) is a pair of integer values of the frame size
 ## (%3$S, %4$S) is a pair of integer values of a limit based on the viewport size
 ## (%5$S, %6$S) is a pair of integer values of an absolute limit
 CompositorAnimationWarningContentTooLarge2=Animation cannot be run on the compositor because the frame size (%1$S, %2$S) is too large relative to the viewport (larger than (%3$S, %4$S)) or larger than the maximum allowed value (%5$S, %6$S)
 ## LOCALIZATION NOTE(CompositorAnimationWarningTransformBackfaceVisibilityHidden):
 ## 'backface-visibility: hidden' is a CSS property, don't translate it.
 CompositorAnimationWarningTransformBackfaceVisibilityHidden=Animations of ‘backface-visibility: hidden’ transforms cannot be run on the compositor
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -16,24 +16,26 @@
 #include "gfx2DGlue.h"
 
 #include "mediasink/AudioSinkWrapper.h"
 #include "mediasink/DecodedAudioDataSink.h"
 #include "mediasink/DecodedStream.h"
 #include "mediasink/OutputStreamManager.h"
 #include "mediasink/VideoSink.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IndexSequence.h"
 #include "mozilla/Logging.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TaskQueue.h"
+#include "mozilla/Tuple.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsIEventTarget.h"
 #include "nsITimer.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsDeque.h"
@@ -279,37 +281,56 @@ protected:
     // have a waiting promise pending (such as with non-MSE EME).
     return Resource()->IsExpectingMoreData()
            || mMaster->IsWaitingAudioData()
            || mMaster->IsWaitingVideoData();
   }
   MediaQueue<MediaData>& AudioQueue() const { return mMaster->mAudioQueue; }
   MediaQueue<MediaData>& VideoQueue() const { return mMaster->mVideoQueue; }
 
+  template <class S, typename... Args, size_t... Indexes>
+  auto
+  CallEnterMemberFunction(S* aS,
+                          Tuple<Args...>& aTuple,
+                          IndexSequence<Indexes...>)
+    -> decltype(ReturnTypeHelper(&S::Enter))
+  {
+    return aS->Enter(Move(Get<Indexes>(aTuple))...);
+  }
+
   // Note this function will delete the current state object.
   // Don't access members to avoid UAF after this call.
   template <class S, typename... Ts>
-  auto SetState(Ts... aArgs)
+  auto SetState(Ts&&... aArgs)
     -> decltype(ReturnTypeHelper(&S::Enter))
   {
+    // |aArgs| must be passed by reference to avoid passing MOZ_NON_PARAM class
+    // SeekJob by value.  See bug 1287006 and bug 1338374.  But we still *must*
+    // copy the parameters, because |Exit()| can modify them.  See bug 1312321.
+    // So we 1) pass the parameters by reference, but then 2) immediately copy
+    // them into a Tuple to be safe against modification, and finally 3) move
+    // the elements of the Tuple into the final function call.
+    auto copiedArgs = MakeTuple(Forward<Ts>(aArgs)...);
+
     // keep mMaster in a local object because mMaster will become invalid after
     // the current state object is deleted.
     auto master = mMaster;
 
-    auto s = new S(master);
+    auto* s = new S(master);
 
     MOZ_ASSERT(GetState() != s->GetState()
                || GetState() == DECODER_STATE_SEEKING);
 
     SLOG("change state to: %s", ToStateStr(s->GetState()));
 
     Exit();
 
     master->mStateObj.reset(s);
-    return s->Enter(Move(aArgs)...);
+    return CallEnterMemberFunction(s, copiedArgs,
+                                   typename IndexSequenceFor<Ts...>::Type());
   }
 
   RefPtr<MediaDecoder::SeekPromise>
   SetSeekingState(SeekJob&& aSeekJob, EventVisibility aVisibility);
 
   // Take a raw pointer in order not to change the life cycle of MDSM.
   // It is guaranteed to be valid by MDSM.
   Master* mMaster;
@@ -899,17 +920,17 @@ private:
  *   DECODING otherwise.
  */
 class MediaDecoderStateMachine::SeekingState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit SeekingState(Master* aPtr) : StateObject(aPtr) { }
 
-  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
+  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob,
                                           EventVisibility aVisibility)
   {
     mSeekJob = Move(aSeekJob);
 
     // Always switch off the blank decoder otherwise we might become visible
     // in the middle of seeking and won't have a valid video frame to show
     // when seek is done.
     if (mMaster->mVideoDecodeSuspended) {
@@ -977,17 +998,17 @@ private:
 class MediaDecoderStateMachine::AccurateSeekingState
   : public MediaDecoderStateMachine::SeekingState
 {
 public:
   explicit AccurateSeekingState(Master* aPtr) : SeekingState(aPtr)
   {
   }
 
-  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
+  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob,
                                           EventVisibility aVisibility)
   {
     MOZ_ASSERT(aSeekJob.mTarget->IsAccurate() || aSeekJob.mTarget->IsFast());
     mCurrentTimeBeforeSeek =
       TimeUnit::FromMicroseconds(mMaster->GetMediaTime());
     return SeekingState::Enter(Move(aSeekJob), aVisibility);
   }
 
@@ -1436,17 +1457,17 @@ DiscardFrames(MediaQueue<MediaData>& aQu
 class MediaDecoderStateMachine::NextFrameSeekingState
   : public MediaDecoderStateMachine::SeekingState
 {
 public:
   explicit NextFrameSeekingState(Master* aPtr) : SeekingState(aPtr)
   {
   }
 
-  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob aSeekJob,
+  RefPtr<MediaDecoder::SeekPromise> Enter(SeekJob&& aSeekJob,
                                           EventVisibility aVisibility)
   {
     MOZ_ASSERT(aSeekJob.mTarget->IsNextFrame());
     mCurrentTime = mMaster->GetMediaTime();
     mDuration = mMaster->Duration();
     return SeekingState::Enter(Move(aSeekJob), aVisibility);
   }
 
--- a/dom/media/MediaPrefs.h
+++ b/dom/media/MediaPrefs.h
@@ -85,20 +85,21 @@ private:
   };
 
   // This is where DECL_MEDIA_PREF for each of the preferences should go.
 
   // AudioSink
   DECL_MEDIA_PREF("accessibility.monoaudio.enable",           MonoAudio, bool, false);
   DECL_MEDIA_PREF("media.resampling.enabled",                 AudioSinkResampling, bool, false);
   DECL_MEDIA_PREF("media.resampling.rate",                    AudioSinkResampleRate, uint32_t, 48000);
-#if defined(ANDROID)
+#if defined(XP_WIN) || defined(XP_DARWIN) || defined(MOZ_PULSEAUDIO)
+  // libcubeb backend implement .get_preferred_channel_layout
+  DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, false);
+#else
   DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, true);
-#else
-  DECL_MEDIA_PREF("media.forcestereo.enabled",                AudioSinkForceStereo, bool, false);
 #endif
   // VideoSink
   DECL_MEDIA_PREF("media.ruin-av-sync.enabled",               RuinAvSync, bool, false);
 
   // Encrypted Media Extensions
   DECL_MEDIA_PREF("media.clearkey.persistent-license.enabled", ClearKeyPersistentLicenseEnabled, bool, false);
 
   // PlatformDecoderModule
@@ -115,16 +116,17 @@ private:
   DECL_MEDIA_PREF("media.android-media-codec.preferred",      PDMAndroidMediaCodecPreferred, bool, false);
 #endif
 #ifdef MOZ_FFMPEG
   DECL_MEDIA_PREF("media.ffmpeg.enabled",                     PDMFFmpegEnabled, bool, true);
   DECL_MEDIA_PREF("media.libavcodec.allow-obsolete",          LibavcodecAllowObsolete, bool, false);
 #endif
 #ifdef MOZ_FFVPX
   DECL_MEDIA_PREF("media.ffvpx.enabled",                      PDMFFVPXEnabled, bool, true);
+  DECL_MEDIA_PREF("media.ffvpx.low-latency.enabled",          PDMFFVPXLowLatencyEnabled, bool, false);
 #endif
 #ifdef XP_WIN
   DECL_MEDIA_PREF("media.wmf.enabled",                        PDMWMFEnabled, bool, true);
   DECL_MEDIA_PREF("media.wmf.skip-blacklist",                 PDMWMFSkipBlacklist, bool, false);
   DECL_MEDIA_PREF("media.decoder-doctor.wmf-disabled-is-failure", DecoderDoctorWMFDisabledIsFailure, bool, false);
   DECL_MEDIA_PREF("media.wmf.vp9.enabled",                    PDMWMFVP9DecoderEnabled, bool, true);
   DECL_MEDIA_PREF("media.wmf.decoder.thread-count",           PDMWMFThreadCount, int32_t, -1);
   DECL_MEDIA_PREF("media.wmf.allow-unsupported-resolutions",  PDMWMFAllowUnsupportedResolutions, bool, false);
--- a/dom/media/MediaResult.h
+++ b/dom/media/MediaResult.h
@@ -2,16 +2,18 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaResult_h_
 #define MediaResult_h_
 
+#include "nsString.h" // Required before 'mozilla/ErrorNames.h'!?
+#include "mozilla/ErrorNames.h"
 #include "nsError.h"
 #include "nsPrintfCString.h"
 
 // MediaResult can be used interchangeably with nsresult.
 // It allows to store extra information such as where the error occurred.
 // While nsresult is typically passed by value; due to its potential size, using
 // MediaResult const references is recommended.
 namespace mozilla {
@@ -46,20 +48,30 @@ public:
   bool operator!=(nsresult aResult) const { return aResult != mCode; }
   operator nsresult () const { return mCode; }
 
   nsCString Description() const
   {
     if (NS_SUCCEEDED(mCode)) {
       return nsCString();
     }
-    return nsPrintfCString("0x%08" PRIx32 ": %s", static_cast<uint32_t>(mCode), mMessage.get());
+    nsCString name;
+    GetErrorName(mCode, static_cast<nsACString&>(name));
+    return nsPrintfCString("%s (0x%08" PRIx32 ")%s%s",
+                           name.get(),
+                           static_cast<uint32_t>(mCode),
+                           mMessage.IsEmpty() ? "" : " - ",
+                           mMessage.get());
   }
 
 private:
   nsresult mCode;
   nsCString mMessage;
 };
 
-#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __func__, ##__VA_ARGS__)
+#ifdef _MSC_VER
+#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __FUNCSIG__, ##__VA_ARGS__)
+#else
+#define RESULT_DETAIL(arg, ...) nsPrintfCString("%s: " arg, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+#endif
 
 } // namespace mozilla
 #endif // MediaResult_h_
--- a/dom/media/gmp/GMPCrashHelper.h
+++ b/dom/media/gmp/GMPCrashHelper.h
@@ -2,18 +2,19 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(GMPCrashHelper_h_)
 #define GMPCrashHelper_h_
 
+#include "MainThreadUtils.h"
+#include "nsISupportsImpl.h"
 #include "nsPIDOMWindow.h"
-#include "nsISupportsImpl.h"
 
 namespace mozilla {
 
 // For every GMP actor requested, the caller can specify a crash helper,
 // which is an object which supplies the nsPIDOMWindowInner to which we'll
 // dispatch the PluginCrashed event if the GMP crashes.
 // GMPCrashHelper has threadsafe refcounting. Its release method ensures
 // that instances are destroyed on the main thread.
--- a/dom/media/gmp/GMPVideoDecoderParent.cpp
+++ b/dom/media/gmp/GMPVideoDecoderParent.cpp
@@ -10,16 +10,17 @@
 #include "nsAutoRef.h"
 #include "nsThreadUtils.h"
 #include "GMPUtils.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "GMPVideoi420FrameImpl.h"
 #include "GMPContentParent.h"
 #include "GMPMessageUtils.h"
 #include "mozilla/gmp/GMPTypes.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 extern LogModule* GetGMPLog();
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp
+++ b/dom/media/gmp/GMPVideoEncoderParent.cpp
@@ -252,20 +252,19 @@ GMPVideoEncoderParent::ActorDestroy(Acto
   if (mCallback) {
     // May call Close() (and Shutdown()) immediately or with a delay
     mCallback->Terminated();
     mCallback = nullptr;
   }
   // Must be shut down before VideoEncoderDestroyed(), since this can recurse
   // the GMPThread event loop.  See bug 1049501
   if (mEncodedThread) {
-    // Can't get it to allow me to use WrapRunnable with a nsCOMPtr<nsIThread>()
     NS_DispatchToMainThread(
-      WrapRunnableNM<decltype(&ShutdownEncodedThread),
-                     nsCOMPtr<nsIThread> >(&ShutdownEncodedThread, mEncodedThread));
+      WrapRunnableNM(&ShutdownEncodedThread, nsCOMPtr<nsIThread>(mEncodedThread))
+    );
     mEncodedThread = nullptr;
   }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoEncoderDestroyed(this);
     mPlugin = nullptr;
   }
   mVideoHost.ActorDestroyed(); // same as DoneWithAPI
--- a/dom/media/gtest/TestMozPromise.cpp
+++ b/dom/media/gtest/TestMozPromise.cpp
@@ -34,17 +34,17 @@ private:
   RefPtr<TaskQueue> mTaskQueue;
 };
 
 class DelayedResolveOrReject : public Runnable
 {
 public:
   DelayedResolveOrReject(TaskQueue* aTaskQueue,
                          TestPromise::Private* aPromise,
-                         TestPromise::ResolveOrRejectValue aValue,
+                         const TestPromise::ResolveOrRejectValue& aValue,
                          int aIterations)
   : mTaskQueue(aTaskQueue)
   , mPromise(aPromise)
   , mValue(aValue)
   , mIterations(aIterations)
   {}
 
   NS_IMETHOD Run() override
--- a/dom/media/ipc/PVideoDecoder.ipdl
+++ b/dom/media/ipc/PVideoDecoder.ipdl
@@ -21,16 +21,17 @@ struct MediaDataIPDL
   uint32_t frames;
   bool keyframe;
 };
 
 struct VideoDataIPDL
 {
   MediaDataIPDL base;
   IntSize display;
+  IntSize frameSize;
   SurfaceDescriptorGPUVideo sd;
   int32_t frameID;
 };
 
 struct MediaRawDataIPDL
 {
   MediaDataIPDL base;
   Shmem buffer;
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -39,17 +39,17 @@ mozilla::ipc::IPCResult
 VideoDecoderChild::RecvOutput(const VideoDataIPDL& aData)
 {
   AssertOnManagerThread();
   VideoInfo info(aData.display().width, aData.display().height);
 
   // The Image here creates a TextureData object that takes ownership
   // of the SurfaceDescriptor, and is responsible for making sure that
   // it gets deallocated.
-  RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.display());
+  RefPtr<Image> image = new GPUVideoImage(GetManager(), aData.sd(), aData.frameSize());
 
   RefPtr<VideoData> video = VideoData::CreateFromImage(info,
                                                        aData.base().offset(),
                                                        aData.base().time(),
                                                        aData.base().duration(),
                                                        image,
                                                        aData.base().keyframe(),
                                                        aData.base().timecode(),
--- a/dom/media/ipc/VideoDecoderParent.cpp
+++ b/dom/media/ipc/VideoDecoderParent.cpp
@@ -184,16 +184,17 @@ VideoDecoderParent::ProcessDecodedData(
       texture->InitIPDLActor(mKnowsCompositor);
       texture->SetAddedToCompositableClient();
     }
 
     VideoDataIPDL output(
       MediaDataIPDL(data->mOffset, data->mTime, data->mTimecode,
                     data->mDuration, data->mFrames, data->mKeyframe),
       video->mDisplay,
+      texture ? texture->GetSize() : IntSize(),
       texture ? mParent->StoreImage(video->mImage, texture)
               : SurfaceDescriptorGPUVideo(0),
       video->mFrameID);
     Unused << SendOutput(output);
   }
 }
 
 mozilla::ipc::IPCResult
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -110,16 +110,18 @@ MediaSource::IsTypeSupported(const nsASt
       mimeType == MEDIAMIMETYPE("audio/mp4")) {
     if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
     return NS_OK;
   }
   if (mimeType == MEDIAMIMETYPE("video/webm")) {
     if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
+          containerType->ExtendedType().Codecs().Contains(
+            NS_LITERAL_STRING("vp8")) ||
           IsWebMForced(aDiagnostics))) {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
     return NS_OK;
   }
   if (mimeType == MEDIAMIMETYPE("audio/webm")) {
     if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
           Preferences::GetBool("media.mediasource.webm.audio.enabled", true))) {
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -420,17 +420,17 @@ SourceBuffer::AppendData(const uint8_t* 
   mTrackBuffersManager->AppendData(data, mCurrentAttributes)
     ->Then(mAbstractMainThread, __func__, this,
            &SourceBuffer::AppendDataCompletedWithSuccess,
            &SourceBuffer::AppendDataErrored)
     ->Track(mPendingAppend);
 }
 
 void
-SourceBuffer::AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult)
+SourceBuffer::AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult)
 {
   MOZ_ASSERT(mUpdating);
   mPendingAppend.Complete();
 
   if (aResult.first()) {
     if (!mActive) {
       mActive = true;
       mMediaSource->SourceBufferIsActive(this);
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -159,17 +159,17 @@ private:
   void AppendError(const MediaResult& aDecodeError);
 
   // Implements the "Prepare Append Algorithm". Returns MediaByteBuffer object
   // on success or nullptr (with aRv set) on error.
   already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData,
                                                   uint32_t aLength,
                                                   ErrorResult& aRv);
 
-  void AppendDataCompletedWithSuccess(SourceBufferTask::AppendBufferResult aResult);
+  void AppendDataCompletedWithSuccess(const SourceBufferTask::AppendBufferResult& aResult);
   void AppendDataErrored(const MediaResult& aError);
 
   RefPtr<MediaSource> mMediaSource;
   const RefPtr<AbstractThread> mAbstractMainThread;
 
   RefPtr<TrackBuffersManager> mTrackBuffersManager;
   SourceBufferAttributes mCurrentAttributes;
 
--- a/dom/media/mediasource/test/test_MediaSource.html
+++ b/dom/media/mediasource/test/test_MediaSource.html
@@ -35,16 +35,19 @@ runWithMSE(function () {
   v.src = o;
   document.body.appendChild(v);
 
   var loadedmetadataCount = 0;
   var updatestartCount = 0;
   var updateendCount = 0;
   var updateCount = 0;
 
+  ok(MediaSource.isTypeSupported("video/webm; codecs=vp8"), "VP8 MSE is always supported");
+  ok(MediaSource.isTypeSupported("audio/webm", "Audio MSE is always supported"));
+
   ms.addEventListener("sourceopen", function () {
     ok(true, "Receive a sourceopen event");
     is(ms.readyState, "open", "MediaSource must be in open state after sourceopen");
     var sb = ms.addSourceBuffer("video/webm");
     ok(sb, "Create a SourceBuffer");
     is(ms.sourceBuffers.length, 1, "MediaSource.sourceBuffers is expected length");
     is(ms.sourceBuffers[0], sb, "SourceBuffer in list matches our SourceBuffer");
     is(ms.activeSourceBuffers.length, 0, "MediaSource.activeSourceBuffers is expected length");
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -2,26 +2,27 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(PlatformDecoderModule_h_)
 #define PlatformDecoderModule_h_
 
+#include "GMPCrashHelper.h"
 #include "MediaDecoderReader.h"
 #include "MediaInfo.h"
+#include "MediaResult.h"
+#include "mozilla/EnumSet.h"
 #include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/KnowsCompositor.h"
 #include "mozilla/layers/LayersTypes.h"
-#include "mozilla/layers/KnowsCompositor.h"
 #include "nsTArray.h"
-#include "mozilla/RefPtr.h"
-#include "GMPCrashHelper.h"
 #include <queue>
-#include "MediaResult.h"
 
 namespace mozilla {
 class TrackInfo;
 class AudioInfo;
 class VideoInfo;
 class MediaRawData;
 class DecoderDoctorDiagnostics;
 
@@ -36,20 +37,24 @@ class RemoteDecoderModule;
 class MediaDataDecoder;
 class TaskQueue;
 class CDMProxy;
 
 static LazyLogModule sPDMLog("PlatformDecoderModule");
 
 struct MOZ_STACK_CLASS CreateDecoderParams final
 {
-  explicit CreateDecoderParams(const TrackInfo& aConfig)
-    : mConfig(aConfig)
+  explicit CreateDecoderParams(const TrackInfo& aConfig) : mConfig(aConfig) { }
+
+  enum class Option
   {
-  }
+    Default,
+    LowLatency,
+  };
+  using OptionSet = EnumSet<Option>;
 
   template <typename T1, typename... Ts>
   CreateDecoderParams(const TrackInfo& aConfig, T1&& a1, Ts&&... args)
     : mConfig(aConfig)
   {
     Set(mozilla::Forward<T1>(a1), mozilla::Forward<Ts>(args)...);
   }
 
@@ -78,30 +83,32 @@ struct MOZ_STACK_CLASS CreateDecoderPara
   DecoderDoctorDiagnostics* mDiagnostics = nullptr;
   layers::ImageContainer* mImageContainer = nullptr;
   MediaResult* mError = nullptr;
   RefPtr<layers::KnowsCompositor> mKnowsCompositor;
   RefPtr<GMPCrashHelper> mCrashHelper;
   bool mUseBlankDecoder = false;
   TrackInfo::TrackType mType = TrackInfo::kUndefinedTrack;
   MediaEventProducer<TrackInfo::TrackType>* mOnWaitingForKeyEvent = nullptr;
+  OptionSet mOptions = OptionSet(Option::Default);
 
 private:
   void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
   void Set(DecoderDoctorDiagnostics* aDiagnostics)
   {
     mDiagnostics = aDiagnostics;
   }
   void Set(layers::ImageContainer* aImageContainer)
   {
     mImageContainer = aImageContainer;
   }
   void Set(MediaResult* aError) { mError = aError; }
   void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
   void Set(bool aUseBlankDecoder) { mUseBlankDecoder = aUseBlankDecoder; }
+  void Set(OptionSet aOptions) { mOptions = aOptions; }
   void Set(layers::KnowsCompositor* aKnowsCompositor)
   {
     mKnowsCompositor = aKnowsCompositor;
   }
   void Set(TrackInfo::TrackType aType)
   {
     mType = aType;
   }
@@ -133,24 +140,25 @@ private:
 
 class PlatformDecoderModule
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PlatformDecoderModule)
 
   // Perform any per-instance initialization.
   // This is called on the decode task queue.
-  virtual nsresult Startup() { return NS_OK; };
+  virtual nsresult Startup() { return NS_OK; }
 
   // Indicates if the PlatformDecoderModule supports decoding of aMimeType.
-  virtual bool SupportsMimeType(
-    const nsACString& aMimeType,
-    DecoderDoctorDiagnostics* aDiagnostics) const = 0;
-  virtual bool Supports(const TrackInfo& aTrackInfo,
-                        DecoderDoctorDiagnostics* aDiagnostics) const
+  virtual bool
+  SupportsMimeType(const nsACString& aMimeType,
+                   DecoderDoctorDiagnostics* aDiagnostics) const = 0;
+  virtual bool
+  Supports(const TrackInfo& aTrackInfo,
+           DecoderDoctorDiagnostics* aDiagnostics) const
   {
     // By default, fall back to SupportsMimeType with just the MIME string.
     // (So PDMs do not need to override this method -- yet.)
     return SupportsMimeType(aTrackInfo.mMimeType, aDiagnostics);
   }
 
 protected:
   PlatformDecoderModule() { }
@@ -202,17 +210,17 @@ protected:
 //
 // Decoding is done asynchronously. Any async work can be done on the
 // TaskQueue passed into the PlatformDecoderModules's Create*Decoder()
 // function. This may not be necessary for platforms with async APIs
 // for decoding.
 class MediaDataDecoder
 {
 protected:
-  virtual ~MediaDataDecoder() {};
+  virtual ~MediaDataDecoder() { }
 
 public:
   typedef TrackInfo::TrackType TrackType;
   typedef nsTArray<RefPtr<MediaData>> DecodedData;
   typedef MozPromise<TrackType, MediaResult, /* IsExclusive = */ true>
     InitPromise;
   typedef MozPromise<DecodedData, MediaResult, /* IsExclusive = */ true>
     DecodePromise;
@@ -276,17 +284,17 @@ public:
   virtual const char* GetDescriptionName() const = 0;
 
   // Set a hint of seek target time to decoder. Decoder will drop any decoded
   // data which pts is smaller than this value. This threshold needs to be clear
   // after reset decoder.
   // Decoder may not honor this value. However, it'd be better that
   // video decoder implements this API to improve seek performance.
   // Note: it should be called before Input() or after Flush().
-  virtual void SetSeekThreshold(const media::TimeUnit& aTime) {}
+  virtual void SetSeekThreshold(const media::TimeUnit& aTime) { }
 
   // When playing adaptive playback, recreating an Android video decoder will
   // cause the transition not smooth during resolution change.
   // Reuse the decoder if the decoder support recycling.
   // Currently, only Android video decoder will return true.
   virtual bool SupportDecoderRecycling() const { return false; }
 
   // ConfigurationChanged will be called to inform the video or audio decoder
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -169,38 +169,47 @@ OpusDataDecoder::ProcessDecode(MediaRawD
 
   if (!mLastFrameTime || mLastFrameTime.ref() != aSample->mTime) {
     // We are starting a new block.
     mFrames = 0;
     mLastFrameTime = Some(aSample->mTime);
   }
 
   // Maximum value is 63*2880, so there's no chance of overflow.
-  uint32_t frames_number = opus_packet_get_nb_frames(aSample->Data(),
-                                                    aSample->Size());
+  int frames_number =
+    opus_packet_get_nb_frames(aSample->Data(), aSample->Size());
   if (frames_number <= 0) {
-    OPUS_DEBUG("Invalid packet header: r=%" PRIu32 " length=%" PRIuSIZE, frames_number,
+    OPUS_DEBUG("Invalid packet header: r=%d length=%" PRIuSIZE, frames_number,
                aSample->Size());
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
                   RESULT_DETAIL("Invalid packet header: r=%d length=%u",
                                 frames_number, uint32_t(aSample->Size()))),
       __func__);
   }
 
-  uint32_t samples = opus_packet_get_samples_per_frame(
+  int samples = opus_packet_get_samples_per_frame(
     aSample->Data(), opus_int32(mOpusParser->mRate));
 
   // A valid Opus packet must be between 2.5 and 120 ms long (48kHz).
-  uint32_t frames = frames_number*samples;
-  if (frames < 120 || frames > 5760) {
-    OPUS_DEBUG("Invalid packet frames: %u", frames);
+  CheckedInt32 totalFrames =
+    CheckedInt32(frames_number) * CheckedInt32(samples);
+  if (!totalFrames.isValid()) {
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
-                  RESULT_DETAIL("Invalid packet frames:%u", frames)),
+                  RESULT_DETAIL("Frames count overflow")),
+      __func__);
+  }
+
+  int frames = totalFrames.value();
+  if (frames < 120 || frames > 5760) {
+    OPUS_DEBUG("Invalid packet frames: %d", frames);
+    return DecodePromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
+                  RESULT_DETAIL("Invalid packet frames:%d", frames)),
       __func__);
   }
 
   AlignedAudioBuffer buffer(frames * channels);
   if (!buffer) {
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
   }
@@ -216,39 +225,40 @@ OpusDataDecoder::ProcessDecode(MediaRawD
                                     buffer.get(), frames, false);
 #endif
   if (ret < 0) {
     return DecodePromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR,
                   RESULT_DETAIL("Opus decoding error:%d", ret)),
       __func__);
   }
-  NS_ASSERTION(uint32_t(ret) == frames, "Opus decoded too few audio samples");
+  NS_ASSERTION(ret == frames, "Opus decoded too few audio samples");
   CheckedInt64 startTime = aSample->mTime;
 
   // Trim the initial frames while the decoder is settling.
   if (mSkip > 0) {
     int32_t skipFrames = std::min<int32_t>(mSkip, frames);
     int32_t keepFrames = frames - skipFrames;
-    OPUS_DEBUG("Opus decoder skipping %d of %d frames", skipFrames, frames);
+    OPUS_DEBUG(
+      "Opus decoder skipping %d of %d frames", skipFrames, frames);
     PodMove(buffer.get(),
             buffer.get() + skipFrames * channels,
             keepFrames * channels);
     startTime = startTime + FramesToUsecs(skipFrames, mOpusParser->mRate);
     frames = keepFrames;
     mSkip -= skipFrames;
   }
 
   if (aSample->mDiscardPadding > 0) {
-    OPUS_DEBUG("Opus decoder discarding %u of %u frames",
+    OPUS_DEBUG("Opus decoder discarding %u of %d frames",
                aSample->mDiscardPadding, frames);
     // Padding discard is only supposed to happen on the final packet.
     // Record the discard so we can return an error if another packet is
     // decoded.
-    if (aSample->mDiscardPadding > frames) {
+    if (aSample->mDiscardPadding > uint32_t(frames)) {
       // Discarding more than the entire packet is invalid.
       OPUS_DEBUG("Opus error, discard padding larger than packet");
       return DecodePromise::CreateAndReject(
         MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                     RESULT_DETAIL("Discard padding larger than packet")),
         __func__);
     }
 
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -6,16 +6,17 @@
 
 #ifndef __FFmpegDecoderModule_h__
 #define __FFmpegDecoderModule_h__
 
 #include "PlatformDecoderModule.h"
 #include "FFmpegLibWrapper.h"
 #include "FFmpegAudioDecoder.h"
 #include "FFmpegVideoDecoder.h"
+#include "MediaPrefs.h"
 
 namespace mozilla {
 
 template <int V>
 class FFmpegDecoderModule : public PlatformDecoderModule
 {
 public:
   static already_AddRefed<PlatformDecoderModule>
@@ -34,21 +35,27 @@ public:
   {