merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 24 Nov 2016 16:41:59 +0100 (2016-11-24)
changeset 358257 2cd5b97ccf5a44569b87bb8f30ca0d7bebe060e8
parent 358256 9aef92f7911d35abc9520ffa0e802be3f4b92f5a (current diff)
parent 358144 7e68f80c7826318c9e15ae693c607e9fa689272a (diff)
child 358258 1d7ac245464255493c907d5048ea9036542da981
child 358259 e43617e146e463a96c32a367e75c2a3b63a86e9e
child 358329 076d3bc7ff2320f5155518fa90f026d2efd77427
push id166708
push usercbook@mozilla.com
push dateThu, 24 Nov 2016 15:42:22 +0000 (2016-11-24)
reviewersmerge
milestone53.0a1
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/test/general/browser_bug1064280_changeUrlInPinnedTab.js
browser/base/content/test/general/browser_overflowScroll.js
dom/base/DOMIntersectionObserver.cpp
dom/base/nsGkAtomList.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/media/gmp/GMPParent.cpp
gfx/thebes/gfxPrefs.h
js/src/jit-test/tests/wasm/spec.js
layout/base/nsLayoutUtils.cpp
layout/mathml/nsMathMLmtableFrame.cpp
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsCSSRenderingBorders.h
layout/style/StyleAnimationValue.cpp
media/gmp-clearkey/0.1/AudioDecoder.cpp
media/gmp-clearkey/0.1/AudioDecoder.h
media/gmp-clearkey/0.1/WMFAACDecoder.cpp
media/gmp-clearkey/0.1/WMFAACDecoder.h
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/url-classifier/Classifier.cpp
toolkit/components/url-classifier/Classifier.h
toolkit/components/url-classifier/tests/unit/test_listmanager.js
toolkit/xre/nsAppRunner.cpp
widget/nsXPLookAndFeel.cpp
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -5,25 +5,25 @@ support-files =
   shared-head.js
 
 [browser_shutdown_acc_reference.js]
 [browser_shutdown_doc_acc_reference.js]
 [browser_shutdown_multi_acc_reference_obj.js]
 [browser_shutdown_multi_acc_reference_doc.js]
 [browser_shutdown_multi_reference.js]
 [browser_shutdown_parent_own_reference.js]
-skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_proxy_acc_reference.js]
 skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_proxy_doc_acc_reference.js]
 skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_multi_proxy_acc_reference_doc.js]
 skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_multi_proxy_acc_reference_obj.js]
 skip-if = !e10s || (os == 'win') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_remote_no_reference.js]
-skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_remote_only.js]
-skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_remote_own_reference.js]
-skip-if = !e10s # e10s specific test for a11y start/shutdown between parent and content.
+skip-if = !e10s || (os == 'win' && os_version == '5.1') # e10s specific test for a11y start/shutdown between parent and content.
 [browser_shutdown_scope_lifecycle.js]
 [browser_shutdown_start_restart.js]
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -267,17 +267,16 @@ tags = mcb
 tags = mcb
 [browser_mixedContentFromOnunload.js]
 tags = mcb
 [browser_mixedContentFramesOnHttp.js]
 tags = mcb
 [browser_bug970746.js]
 [browser_bug1015721.js]
 skip-if = os == 'win'
-[browser_bug1064280_changeUrlInPinnedTab.js]
 [browser_accesskeys.js]
 [browser_clipboard.js]
 subsuite = clipboard
 [browser_clipboard_pastefile.js]
 skip-if = true # Disabled due to the clipboard not supporting real file types yet (bug 1288773)
 [browser_contentAreaClick.js]
 skip-if = e10s # Clicks in content don't go through contentAreaClick with e10s.
 [browser_contentAltClick.js]
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -3,16 +3,17 @@ skip-if = (os == 'linux') # Bug 1243103,
 support-files =
   head.js
 
 [browser_newtab_1188015.js]
 [browser_newtab_background_captures.js]
 [browser_newtab_block.js]
 [browser_newtab_bug721442.js]
 [browser_newtab_bug722273.js]
+skip-if = (os == "mac" && debug) # temporary skip-if due to increase in intermittent failures on Mac debug - bug 1119906
 [browser_newtab_bug723102.js]
 [browser_newtab_bug723121.js]
 [browser_newtab_bug725996.js]
 [browser_newtab_bug734043.js]
 [browser_newtab_bug735987.js]
 [browser_newtab_bug752841.js]
 [browser_newtab_bug765628.js]
 [browser_newtab_bug876313.js]
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -1,4 +1,5 @@
 [browser_tabSpinnerProbe.js]
 skip-if = !e10s # Tab spinner is e10s only.
 [browser_tabSwitchPrintPreview.js]
 skip-if = os == 'mac'
+[browser_navigatePinnedTab.js]
rename from browser/base/content/test/general/browser_bug1064280_changeUrlInPinnedTab.js
rename to browser/base/content/test/tabs/browser_navigatePinnedTab.js
--- a/browser/base/content/test/general/browser_bug1064280_changeUrlInPinnedTab.js
+++ b/browser/base/content/test/tabs/browser_navigatePinnedTab.js
@@ -24,13 +24,36 @@ add_task(function* () {
   gURLBar.value = TEST_LINK_CHANGED;
 
   goButton.click();
   yield BrowserTestUtils.browserLoaded(browser);
 
   is(appTab.linkedBrowser.currentURI.spec, TEST_LINK_CHANGED,
      "New page loaded in the app tab");
   is(gBrowser.tabs.length, initialTabsNo, "No additional tabs were opened");
+
+  // Now check that opening a link that does create a new tab works,
+  // and also that it nulls out the opener.
+  let pageLoadPromise = BrowserTestUtils.browserLoaded(appTab.linkedBrowser, "http://example.com/");
+  yield BrowserTestUtils.loadURI(appTab.linkedBrowser, "http://example.com/");
+  info("Started loading example.com");
+  yield pageLoadPromise;
+  info("Loaded example.com");
+  let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.org/");
+  yield ContentTask.spawn(browser, null, function* () {
+    let link = content.document.createElement("a");
+    link.href = "http://example.org/";
+    content.document.body.appendChild(link);
+    link.click();
+  });
+  info("Created & clicked link");
+  let extraTab = yield newTabPromise;
+  info("Got a new tab");
+  yield ContentTask.spawn(extraTab.linkedBrowser, null, function* () {
+    is(content.opener, null, "No opener should be available");
+  });
+  yield BrowserTestUtils.removeTab(extraTab);
 });
 
+
 registerCleanupFunction(function() {
   gBrowser.removeTab(gBrowser.selectedTab);
 });
--- a/browser/branding/aurora/branding.nsi
+++ b/browser/branding/aurora/branding.nsi
@@ -9,17 +9,18 @@
 # BrandFullNameInternal is used for some registry and file system values
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Firefox Developer Edition"
 !define BrandShortName        "Firefox Developer Edition"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-aurora-latest"
+!define URLStubDownload32 "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-aurora-latest"
+!define URLStubDownload64 "http://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-aurora-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=aurora&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "aurora"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/nightly/branding.nsi
+++ b/browser/branding/nightly/branding.nsi
@@ -8,17 +8,18 @@
 
 # BrandFullNameInternal is used for some registry and file system values
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Nightly"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-nightly-latest"
+!define URLStubDownload32 "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-nightly-latest"
+!define URLStubDownload64 "http://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-nightly-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=nightly&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "nightly"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/official/branding.nsi
+++ b/browser/branding/official/branding.nsi
@@ -13,17 +13,18 @@
 !define URLInfoAbout          "https://www.mozilla.org"
 !define URLUpdateInfo         "https://www.mozilla.org/firefox/${AppVersion}/releasenotes"
 !define HelpLink              "https://support.mozilla.org"
 
 ; The OFFICIAL define is a workaround to support different urls for Release and
 ; Beta since they share the same branding when building with other branches that
 ; set the update channel to beta.
 !define OFFICIAL
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownload32 "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownload64 "http://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=release&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "release"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/branding/unofficial/branding.nsi
+++ b/browser/branding/unofficial/branding.nsi
@@ -8,17 +8,18 @@
 
 # BrandFullNameInternal is used for some registry and file system values
 # instead of BrandFullName and typically should not be modified.
 !define BrandFullNameInternal "Mozilla Developer Preview"
 !define CompanyName           "mozilla.org"
 !define URLInfoAbout          "https://www.mozilla.org"
 !define HelpLink              "https://support.mozilla.org"
 
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownload32 "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-latest"
+!define URLStubDownload64 "http://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-latest"
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=release&installer_lang=${AB_CD}"
 !define URLSystemRequirements "https://www.mozilla.org/firefox/system-requirements/"
 !define Channel "unofficial"
 
 # The installer's certificate name and issuer expected by the stub installer
 !define CertNameDownload   "Mozilla Corporation"
 !define CertIssuerDownload "DigiCert SHA2 Assured ID Code Signing CA"
 
--- a/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
+++ b/browser/components/contextualidentity/test/browser/browser_count_and_remove.js
@@ -17,18 +17,31 @@ add_task(function* setup() {
         ]});
 });
 
 add_task(function* test() {
   is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs by default.");
 
   openTabInUserContext(1);
   is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
+  is(ContextualIdentityService.countContainerTabs(1), 1, "1 container tab created with id 1");
+  is(ContextualIdentityService.countContainerTabs(2), 0, "0 container tabs created with id 2");
 
   openTabInUserContext(1);
-  is(ContextualIdentityService.countContainerTabs(), 2, "2 container tab created");
+  is(ContextualIdentityService.countContainerTabs(), 2, "2 container tabs created");
+  is(ContextualIdentityService.countContainerTabs(1), 2, "2 container tabs created with id 1");
+  is(ContextualIdentityService.countContainerTabs(2), 0, "0 container tabs created with id 2");
 
   openTabInUserContext(2);
   is(ContextualIdentityService.countContainerTabs(), 3, "3 container tab created");
+  is(ContextualIdentityService.countContainerTabs(1), 2, "2 container tabs created with id 1");
+  is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
 
-  ContextualIdentityService.closeAllContainerTabs();
-  is(ContextualIdentityService.countContainerTabs(), 0, "0 container tab at the end.");
+  ContextualIdentityService.closeContainerTabs(1);
+  is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
+  is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs created with id 1");
+  is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
+
+  ContextualIdentityService.closeContainerTabs();
+  is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs at the end.");
+  is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs at the end with id 1.");
+  is(ContextualIdentityService.countContainerTabs(2), 0, "0 container tabs at the end with id 2.");
 });
--- a/browser/components/preferences/in-content/containers.js
+++ b/browser/components/preferences/in-content/containers.js
@@ -34,20 +34,44 @@ let gContainersPane = {
       item.setAttribute("containerColor", container.color);
       item.setAttribute("userContextId", container.userContextId);
 
       this._list.appendChild(item);
     }
   },
 
   onRemoveClick(button) {
-    let userContextId = button.getAttribute("value");
+    let userContextId = parseInt(button.getAttribute("value"), 10);
+
+    let count = ContextualIdentityService.countContainerTabs(userContextId);
+    if (count > 0) {
+      let bundlePreferences = document.getElementById("bundlePreferences");
+
+      let title = bundlePreferences.getString("removeContainerAlertTitle");
+      let message = PluralForm.get(count, bundlePreferences.getString("removeContainerMsg"))
+                              .replace("#S", count)
+      let okButton = bundlePreferences.getString("removeContainerOkButton");
+      let cancelButton = bundlePreferences.getString("removeContainerButton2");
+
+      let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
+                        (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
+
+      let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
+                                         okButton, cancelButton, null, null, {});
+      if (rv != 0) {
+        return;
+      }
+
+      ContextualIdentityService.closeContainerTabs(userContextId);
+    }
+
     ContextualIdentityService.remove(userContextId);
     this._rebuildView();
   },
+
   onPeferenceClick(button) {
     this.openPreferenceDialog(button.getAttribute("value"));
   },
 
   onAddButtonClick(button) {
     this.openPreferenceDialog(null);
   },
 
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -99,17 +99,17 @@ var gPrivacyPane = {
     let cancelButton = bundlePreferences.getString("disableContainersButton2");
 
     let buttonFlags = (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0) +
                       (Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_1);
 
     let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
                                        okButton, cancelButton, null, null, {});
     if (rv == 0) {
-      ContextualIdentityService.closeAllContainerTabs();
+      ContextualIdentityService.closeContainerTabs();
       Services.prefs.setBoolPref("privacy.userContext.enabled", false);
       return;
     }
 
     checkbox.checked = true;
   },
 
   /**
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -30,21 +30,20 @@ RequestExecutionLevel user
 
 Var Dialog
 Var Progressbar
 Var ProgressbarMarqueeIntervalMS
 Var LabelDownloading
 Var LabelInstalling
 Var LabelFreeSpace
 Var CheckboxSetAsDefault
-Var CheckboxShortcutOnBar ; Used for Quicklaunch or Taskbar as appropriate
-Var CheckboxShortcutInStartMenu
-Var CheckboxShortcutOnDesktop
+Var CheckboxShortcuts
 Var CheckboxSendPing
 Var CheckboxInstallMaintSvc
+Var DroplistArch
 Var DirRequest
 Var ButtonBrowse
 Var LabelBlurb1
 Var LabelBlurb2
 Var LabelBlurb3
 Var BitmapBlurb1
 Var BitmapBlurb2
 Var BitmapBlurb3
@@ -68,17 +67,16 @@ Var SpaceAvailableBytes
 Var InitialInstallDir
 Var HandleDownload
 Var CanSetAsDefault
 Var InstallCounterStep
 Var InstallStepSize
 Var InstallTotalSteps
 Var ProgressCompleted
 Var ProgressTotal
-Var TmpVal
 
 Var ExitCode
 Var FirefoxLaunchCode
 
 ; The first three tick counts are for the start of a phase and equate equate to
 ; the display of individual installer pages.
 Var StartIntroPhaseTickCount
 Var StartOptionsPhaseTickCount
@@ -103,16 +101,18 @@ Var InitialInstallRequirementsCode
 Var ExistingProfile
 Var ExistingVersion
 Var ExistingBuildID
 Var DownloadedBytes
 Var DownloadRetryCount
 Var OpenedDownloadPage
 Var DownloadServerIP
 Var PostSigningData
+Var PreviousInstallDir
+Var PreviousInstallArch
 
 Var ControlHeightPX
 Var ControlRightPX
 
 ; Uncomment the following to prevent pinging the metrics server when testing
 ; the stub installer
 ;!define STUB_DEBUG
 
@@ -213,43 +213,51 @@ Var ControlRightPX
 !endif
 !ifndef OPEN_EXISTING
   !define OPEN_EXISTING 3
 !endif
 !ifndef INVALID_HANDLE_VALUE
   !define INVALID_HANDLE_VALUE -1
 !endif
 
+!define DefaultInstDir32bit "$PROGRAMFILES32\${BrandFullName}"
+!define DefaultInstDir64bit "$PROGRAMFILES64\${BrandFullName}"
+
 !include "nsDialogs.nsh"
 !include "LogicLib.nsh"
 !include "FileFunc.nsh"
 !include "TextFunc.nsh"
 !include "WinVer.nsh"
 !include "WordFunc.nsh"
 
 !insertmacro GetParameters
 !insertmacro GetOptions
 !insertmacro LineFind
 !insertmacro StrFilter
 
+!include "StrFunc.nsh"
+${StrTok}
+
 !include "locales.nsi"
 !include "branding.nsi"
 
 !include "defines.nsi"
 
 ; Must be included after defines.nsi
 !include "locale-fonts.nsh"
 
 ; The OFFICIAL define is a workaround to support different urls for Release and
 ; Beta since they share the same branding when building with other branches that
 ; set the update channel to beta.
 !ifdef OFFICIAL
 !ifdef BETA_UPDATE_CHANNEL
-!undef URLStubDownload
-!define URLStubDownload "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
+!undef URLStubDownload32
+!undef URLStubDownload64
+!define URLStubDownload32 "http://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
+!define URLStubDownload64 "http://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-beta-latest"
 !undef URLManualDownload
 !define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=beta&installer_lang=${AB_CD}"
 !undef Channel
 !define Channel "beta"
 !endif
 !endif
 
 !include "common.nsh"
@@ -270,21 +278,16 @@ VIAddVersionKey "FileDescription" "${Bra
 VIAddVersionKey "OriginalFilename" "setup-stub.exe"
 
 Name "$BrandFullName"
 OutFile "setup-stub.exe"
 icon "setup.ico"
 XPStyle on
 BrandingText " "
 ChangeUI all "nsisui.exe"
-!ifdef HAVE_64BIT_BUILD
-  InstallDir "$PROGRAMFILES64\${BrandFullName}\"
-!else
-  InstallDir "$PROGRAMFILES32\${BrandFullName}\"
-!endif
 
 !ifdef ${AB_CD}_rtl
   LoadLanguageFile "locale-rtl.nlf"
 !else
   LoadLanguageFile "locale.nlf"
 !endif
 
 !include "nsisstrings.nlf"
@@ -335,62 +338,89 @@ Function .onInit
 
   ; SSE2 CPU support
   ${If} "$R7" == "0"
     MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
     ExecShell "open" "${URLSystemRequirements}"
     Quit
   ${EndIf}
 
-!ifdef HAVE_64BIT_BUILD
-  SetRegView 64
-!endif
+  ${If} ${RunningX64}
+    StrCpy $INSTDIR "${DefaultInstDir64bit}"
+  ${Else}
+    StrCpy $INSTDIR "${DefaultInstDir32bit}"
+  ${EndIf}
 
   ; Require elevation if the user can elevate
   ${ElevateUAC}
 
 ; The commands inside this ifndef are needed prior to NSIS 3.0a2 and can be
 ; removed after we require NSIS 3.0a2 or greater.
 !ifndef NSIS_PACKEDVERSION
   ${If} ${AtLeastWinVista}
     System::Call 'user32::SetProcessDPIAware()'
   ${EndIf}
 !endif
 
+  ; If we have any existing installation, use its location as the default
+  ; path for this install, even if it's not the same architecture.
+  SetRegView 32
   SetShellVarContext all ; Set SHCTX to HKLM
   ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
   ${If} "$R9" == "false"
+  ${AndIf} ${RunningX64}
+    SetRegView 64
+    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
+  ${EndIf}
+
+  ${If} "$R9" == "false"
     SetShellVarContext current ; Set SHCTX to HKCU
     ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
 
     ${If} ${RunningX64}
       ; In HKCU there is no WOW64 redirection, which means we may have gotten
-      ; the path to a 32-bit install even though we're 64-bit, or vice-versa.
+      ; the path to a 32-bit install even though we're 64-bit.
       ; In that case, just use the default path instead of offering an upgrade.
       ; But only do that override if the existing install is in Program Files,
       ; because that's the only place we can be sure is specific
       ; to either 32 or 64 bit applications.
       ; The WordFind syntax below searches for the first occurence of the
       ; "delimiter" (the Program Files path) in the install path and returns
       ; anything that appears before that. If nothing appears before that,
-      ; then the install is under Program Files (32 or 64).
-!ifdef HAVE_64BIT_BUILD
+      ; then the install is under Program Files.
       ${WordFind} $R9 $PROGRAMFILES32 "+1{" $0
-!else
-      ${WordFind} $R9 $PROGRAMFILES64 "+1{" $0
-!endif
       ${If} $0 == ""
         StrCpy $R9 "false"
       ${EndIf}
     ${EndIf}
   ${EndIf}
 
+  StrCpy $PreviousInstallDir ""
+  StrCpy $PreviousInstallArch ""
   ${If} "$R9" != "false"
-    StrCpy $INSTDIR "$R9"
+    ; Don't override the default install path with an existing installation
+    ; of a different architecture.
+    System::Call "*(i)p.r0"
+    StrCpy $1 "$R9\${FileMainEXE}"
+    System::Call "Kernel32::GetBinaryTypeW(w r1, p r0)i"
+    System::Call "*$0(i.r2)"
+    System::Free $0
+
+    ${If} $2 == "6" ; 6 == SCS_64BIT_BINARY
+    ${AndIf} ${RunningX64}
+      StrCpy $PreviousInstallDir "$R9"
+      StrCpy $PreviousInstallArch "64"
+      StrCpy $INSTDIR "$PreviousInstallDir"
+    ${ElseIf} $2 == "0" ; 0 == SCS_32BIT_BINARY
+    ${AndIfNot} ${RunningX64}
+      StrCpy $PreviousInstallDir "$R9"
+      StrCpy $PreviousInstallArch "32"
+      StrCpy $INSTDIR "$PreviousInstallDir"
+    ${EndIf}
   ${EndIf}
 
   ; Used to determine if the default installation directory was used.
   StrCpy $InitialInstallDir "$INSTDIR"
 
   ClearErrors
   WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
                    "Write Test"
@@ -417,26 +447,29 @@ Function .onInit
   ; when a page is displayed.
   StrCpy $IntroPhaseSeconds "0"
   StrCpy $OptionsPhaseSeconds "0"
   StrCpy $EndPreInstallPhaseTickCount "0"
   StrCpy $EndInstallPhaseTickCount "0"
   StrCpy $InitialInstallRequirementsCode ""
   StrCpy $IsDownloadFinished ""
   StrCpy $FirefoxLaunchCode "0"
-  StrCpy $CheckboxShortcutOnBar "1"
-  StrCpy $CheckboxShortcutInStartMenu "1"
-  StrCpy $CheckboxShortcutOnDesktop "1"
+  StrCpy $CheckboxShortcuts "1"
   StrCpy $CheckboxSendPing "1"
 !ifdef MOZ_MAINTENANCE_SERVICE
   StrCpy $CheckboxInstallMaintSvc "1"
 !else
   StrCpy $CheckboxInstallMaintSvc "0"
 !endif
   StrCpy $WasOptionsButtonClicked "0"
+  ${If} ${RunningX64}
+    StrCpy $DroplistArch "$(VERSION_64BIT)"
+  ${Else}
+    StrCpy $DroplistArch "$(VERSION_32BIT)"
+  ${EndIf}
 
   StrCpy $0 ""
 !ifdef FONT_FILE1
   ${If} ${FileExists} "$FONTS\${FONT_FILE1}"
     StrCpy $0 "${FONT_NAME1}"
   ${EndIf}
 !endif
 
@@ -570,21 +603,21 @@ Function SendPing
     ; Get the seconds elapsed from the end of the pre-installation check phase
     ; to the completion of the installation phase.
     ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
 
     ; Get the seconds elapsed from the end of the installation phase to the
     ; completion of all phases.
     ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
 
-!ifdef HAVE_64BIT_BUILD
-    StrCpy $R0 "1"
-!else
-    StrCpy $R0 "0"
-!endif
+    ${If} $DroplistArch == "$(VERSION_64BIT)"
+      StrCpy $R0 "1"
+    ${Else}
+      StrCpy $R0 "0"
+    ${EndIf}
 
     ${If} ${RunningX64}
       StrCpy $R1 "1"
     ${Else}
       StrCpy $R1 "0"
     ${EndIf}
 
     ; Though these values are sometimes incorrect due to bug 444664 it happens
@@ -898,80 +931,39 @@ Function createOptions
   StrCpy $ExistingTopDir ""
 
   nsDialogs::Create /NOUNLOAD 1018
   Pop $Dialog
   ; Since the text color for controls is set in this Dialog the foreground and
   ; background colors of the Dialog must also be hardcoded.
   SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
 
-  ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 18u ${OPTIONS_ITEM_WIDTH_DU} \
-                     12u "$(CREATE_SHORTCUTS)"
-  Pop $0
-  SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-  SendMessage $0 ${WM_SETFONT} $FontNormal 0
-
-  ${If} ${AtLeastWin7}
-    StrCpy $0 "$(ADD_SC_TASKBAR)"
-  ${Else}
-    StrCpy $0 "$(ADD_SC_QUICKLAUNCHBAR)"
-  ${EndIf}
-  ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 38u \
-                        ${OPTIONS_SUBITEM_WIDTH_DU} 12u "$0"
-  Pop $CheckboxShortcutOnBar
-  ; The uxtheme must be disabled on checkboxes in order to override the system
-  ; font color.
-  System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnBar, w " ", w " ")'
-  SetCtlColors $CheckboxShortcutOnBar ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-  SendMessage $CheckboxShortcutOnBar ${WM_SETFONT} $FontNormal 0
-  ${NSD_Check} $CheckboxShortcutOnBar
-
-  ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 54u ${OPTIONS_SUBITEM_WIDTH_DU} \
-                        12u "$(ADD_CheckboxShortcutInStartMenu)"
-  Pop $CheckboxShortcutInStartMenu
-  ; The uxtheme must be disabled on checkboxes in order to override the system
-  ; font color.
-  System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutInStartMenu, w " ", w " ")'
-  SetCtlColors $CheckboxShortcutInStartMenu ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-  SendMessage $CheckboxShortcutInStartMenu ${WM_SETFONT} $FontNormal 0
-  ${NSD_Check} $CheckboxShortcutInStartMenu
-
-  ${NSD_CreateCheckbox} ${OPTIONS_SUBITEM_EDGE_DU} 70u ${OPTIONS_SUBITEM_WIDTH_DU} \
-                        12u "$(ADD_CheckboxShortcutOnDesktop)"
-  Pop $CheckboxShortcutOnDesktop
-  ; The uxtheme must be disabled on checkboxes in order to override the system
-  ; font color.
-  System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcutOnDesktop, w " ", w " ")'
-  SetCtlColors $CheckboxShortcutOnDesktop ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-  SendMessage $CheckboxShortcutOnDesktop ${WM_SETFONT} $FontNormal 0
-  ${NSD_Check} $CheckboxShortcutOnDesktop
-
-  ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 100u ${OPTIONS_ITEM_WIDTH_DU} \
+  ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 25u ${OPTIONS_ITEM_WIDTH_DU} \
                      12u "$(DEST_FOLDER)"
   Pop $0
   SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $0 ${WM_SETFONT} $FontNormal 0
 
-  ${NSD_CreateDirRequest} ${OPTIONS_SUBITEM_EDGE_DU} 116u 159u 14u "$INSTDIR"
+  ${NSD_CreateDirRequest} ${OPTIONS_SUBITEM_EDGE_DU} 41u 159u 14u "$INSTDIR"
   Pop $DirRequest
   SetCtlColors $DirRequest ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $DirRequest ${WM_SETFONT} $FontNormal 0
   System::Call shlwapi::SHAutoComplete(i $DirRequest, i ${SHACF_FILESYSTEM})
   ${NSD_OnChange} $DirRequest OnChange_DirRequest
 
 !ifdef ${AB_CD}_rtl
   ; Remove the RTL styling from the directory request text box
   ${RemoveStyle} $DirRequest ${SS_RIGHT}
   ${RemoveExStyle} $DirRequest ${WS_EX_RIGHT}
   ${RemoveExStyle} $DirRequest ${WS_EX_RTLREADING}
   ${NSD_AddStyle} $DirRequest ${SS_LEFT}
   ${NSD_AddExStyle} $DirRequest ${WS_EX_LTRREADING}|${WS_EX_LEFT}
 !endif
 
-  ${NSD_CreateBrowseButton} 280u 116u 50u 14u "$(BROWSE_BUTTON)"
+  ${NSD_CreateBrowseButton} 280u 41u 50u 14u "$(BROWSE_BUTTON)"
   Pop $ButtonBrowse
   SetCtlColors $ButtonBrowse "" ${COMMON_BKGRD_COLOR}
   ${NSD_OnClick} $ButtonBrowse OnClick_ButtonBrowse
 
   ; Get the number of pixels from the left of the Dialog to the right side of
   ; the "Space Required:" and "Space Available:" labels prior to setting RTL so
   ; the correct position of the controls can be set by NSIS for RTL locales.
 
@@ -982,99 +974,184 @@ Function createOptions
   ${If} $1 > $3
     StrCpy $ControlHeightPX "$1"
   ${Else}
     StrCpy $ControlHeightPX "$3"
   ${EndIf}
 
   IntOp $0 $0 + 8 ; Add padding to the control's width
   ; Make both controls the same width as the widest control
-  ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 134u $0 $ControlHeightPX "$(SPACE_REQUIRED)"
+  ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 59u $0 $ControlHeightPX "$(SPACE_REQUIRED)"
   Pop $5
   SetCtlColors $5 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
   SendMessage $5 ${WM_SETFONT} $FontItalic 0
 
   IntOp $2 $2 + 8 ; Add padding to the control's width
-  ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 145u $2 $ControlHeightPX "$(SPACE_AVAILABLE)"
+  ${NSD_CreateLabelCenter} ${OPTIONS_SUBITEM_EDGE_DU} 70u $2 $ControlHeightPX "$(SPACE_AVAILABLE)"
   Pop $6
   SetCtlColors $6 ${COMMON_TEXT_COLOR_FADED} ${COMMON_BKGRD_COLOR}
   SendMessage $6 ${WM_SETFONT} $FontItalic 0
 
   ; Use the widest label for aligning the labels next to them
   ${If} $0 > $2
     StrCpy $6 "$5"
   ${EndIf}
   FindWindow $1 "#32770" "" $HWNDPARENT
   ${GetDlgItemEndPX} $6 $ControlRightPX
 
   IntOp $ControlRightPX $ControlRightPX + 6
 
-  ${NSD_CreateLabel} $ControlRightPX 134u 100% $ControlHeightPX \
+  ${NSD_CreateLabel} $ControlRightPX 59u 100% $ControlHeightPX \
                      "${APPROXIMATE_REQUIRED_SPACE_MB} $(MEGA)$(BYTE)"
   Pop $7
   SetCtlColors $7 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $7 ${WM_SETFONT} $FontNormal 0
 
   ; Create the free space label with an empty string and update it by calling
   ; UpdateFreeSpaceLabel
-  ${NSD_CreateLabel} $ControlRightPX 145u 100% $ControlHeightPX " "
+  ${NSD_CreateLabel} $ControlRightPX 70u 100% $ControlHeightPX " "
   Pop $LabelFreeSpace
   SetCtlColors $LabelFreeSpace ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $LabelFreeSpace ${WM_SETFONT} $FontNormal 0
 
   Call UpdateFreeSpaceLabel
 
-  ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 168u ${OPTIONS_SUBITEM_WIDTH_DU} \
+  ${If} ${AtLeastWin7}
+    StrCpy $0 "$(ADD_SC_DESKTOP_TASKBAR)"
+  ${Else}
+    StrCpy $0 "$(ADD_SC_DESKTOP_QUICKLAUNCHBAR)"
+  ${EndIf}
+  ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 100u \
+                        ${OPTIONS_ITEM_WIDTH_DU} 12u "$0"
+  Pop $CheckboxShortcuts
+  ; The uxtheme must be disabled on checkboxes in order to override the system
+  ; font color.
+  System::Call 'uxtheme::SetWindowTheme(i $CheckboxShortcuts, w " ", w " ")'
+  SetCtlColors $CheckboxShortcuts ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
+  SendMessage $CheckboxShortcuts ${WM_SETFONT} $FontNormal 0
+  ${NSD_Check} $CheckboxShortcuts
+
+  ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 116u ${OPTIONS_SUBITEM_WIDTH_DU} \
                         12u "$(SEND_PING)"
   Pop $CheckboxSendPing
   ; The uxtheme must be disabled on checkboxes in order to override the system
   ; font color.
   System::Call 'uxtheme::SetWindowTheme(i $CheckboxSendPing, w " ", w " ")'
   SetCtlColors $CheckboxSendPing ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
   SendMessage $CheckboxSendPing ${WM_SETFONT} $FontNormal 0
   ${NSD_Check} $CheckboxSendPing
 
 !ifdef MOZ_MAINTENANCE_SERVICE
+  StrCpy $CheckboxInstallMaintSvc "0"
   ; We can only install the maintenance service if the user is an admin.
   Call IsUserAdmin
   Pop $0
 
   ; Only show the maintenance service checkbox if we're on XP SP3 or higher;
   ;  we don't ever want to install it on XP without at least SP3 installed.
   ${If} $0 == "true"
   ${AndIf} ${IsWinXP}
   ${AndIf} ${AtMostServicePack} 2
     StrCpy $0 "false"
   ${EndIf}
 
-  ; Only show the maintenance service checkbox if we have write access to HKLM
-  ClearErrors
-  WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
-                   "Write Test"
-  ${If} ${Errors}
-  ${OrIf} $0 != "true"
-    StrCpy $CheckboxInstallMaintSvc "0"
-  ${Else}
+  ${If} $0 == "true"
+    ; Only show the maintenance service checkbox if we have write access to HKLM
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
-    ; Read the registry instead of using ServicesHelper::IsInstalled so the
-    ; plugin isn't included in the stub installer to lessen its size.
     ClearErrors
-    ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\services\MozillaMaintenance" "ImagePath"
-    ${If} ${Errors}
-      ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 184u ${OPTIONS_ITEM_WIDTH_DU} \
-                            12u "$(INSTALL_MAINT_SERVICE)"
-      Pop $CheckboxInstallMaintSvc
-      System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
-      SetCtlColors $CheckboxInstallMaintSvc ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
-      SendMessage $CheckboxInstallMaintSvc ${WM_SETFONT} $FontNormal 0
-      ${NSD_Check} $CheckboxInstallMaintSvc
+    WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
+                     "Write Test"
+    ${IfNot} ${Errors}
+      DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
+      ; Read the registry instead of using ServicesHelper::IsInstalled so the
+      ; plugin isn't included in the stub installer to lessen its size.
+      ClearErrors
+      ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\services\MozillaMaintenance" "ImagePath"
+      ${If} ${Errors}
+        ${NSD_CreateCheckbox} ${OPTIONS_ITEM_EDGE_DU} 132u ${OPTIONS_ITEM_WIDTH_DU} \
+                              12u "$(INSTALL_MAINT_SERVICE)"
+        Pop $CheckboxInstallMaintSvc
+        System::Call 'uxtheme::SetWindowTheme(i $CheckboxInstallMaintSvc, w " ", w " ")'
+        SetCtlColors $CheckboxInstallMaintSvc ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
+        SendMessage $CheckboxInstallMaintSvc ${WM_SETFONT} $FontNormal 0
+        ${NSD_Check} $CheckboxInstallMaintSvc
+      ${EndIf}
     ${EndIf}
   ${EndIf}
 !endif
 
+  ${If} ${RunningX64}
+    ; Get the exact pixel width we're going to need for this label.
+    ; The label string has a keyboard accelerator, which is an '&' that's in
+    ; the string but is not rendered, and GetTextExtent doesn't account for
+    ; those, so remove them first. Also handle any escaped &'s ("&&").
+    StrCpy $R0 "$(ARCH_DROPLIST_LABEL)"
+    StrCpy $R1 ""
+    ${Do}
+      ${StrTok} $R2 $R0 "&" 0 0
+      StrCpy $R1 "$R1$R2"
+      StrLen $R3 $R2
+      IntOp $R3 $R3 + 1
+      StrCpy $R0 $R0 "" $R3
+      StrCpy $R4 $R0 1
+      ${If} $R4 == "&"
+        StrCpy $R1 "$R1&"
+        StrCpy $R0 $R0 "" 1
+      ${EndIf}
+    ${LoopUntil} $R0 == ""
+
+    ${GetTextExtent} $R1 $FontNormal $R0 $R1
+    ${If} $CheckboxInstallMaintSvc == "0"
+      ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 134u $R0 $R1 "$(ARCH_DROPLIST_LABEL)"
+    ${Else}
+      ${NSD_CreateLabel} ${OPTIONS_ITEM_EDGE_DU} 154u $R0 $R1 "$(ARCH_DROPLIST_LABEL)"
+    ${EndIf}
+    Pop $0
+    SetCtlColors $0 ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
+    SendMessage $0 ${WM_SETFONT} $FontNormal 0
+
+    ; Set the dropdown list size to the same as the larger of the two options.
+    ${GetTextExtent} "$(VERSION_32BIT)" $FontNormal $R0 $R1
+    ${GetTextExtent} "$(VERSION_64BIT)" $FontNormal $R2 $R3
+    ${If} $R0 < $R2
+      StrCpy $R0 $R2
+    ${EndIf}
+    ${If} $R1 < $R3
+      StrCpy $R3 $R1
+    ${EndIf}
+    ; Add enough width for the dropdown button. How wide the button is depends
+    ; on he system display scaling setting, which we cannot easily determine,
+    ; so just use a value that works fine for a setting of 200% and adds a
+    ; little too much padding for settings below that.
+    IntOp $R0 $R0 + 56
+
+    ; Put the droplist right after the label, with some padding.
+    ${GetDlgItemEndPX} $0 $ControlRightPX
+    IntOp $ControlRightPX $ControlRightPX + 4
+    ${If} $CheckboxInstallMaintSvc == "0"
+      ${NSD_CreateDropList} $ControlRightPX 132u $R0 $R3 ""
+    ${Else}
+      ${NSD_CreateDropList} $ControlRightPX 152u $R0 $R3 ""
+    ${EndIf}
+    Pop $DroplistArch
+    ${NSD_CB_AddString} $DroplistArch "$(VERSION_32BIT)"
+    ${NSD_CB_AddString} $DroplistArch "$(VERSION_64BIT)"
+    ${NSD_OnChange} $DroplistArch OnChange_DroplistArch
+    ; The uxtheme must be disabled in order to override the system colors.
+    System::Call 'uxtheme::SetWindowTheme(i $DroplistArch, w " ", w " ")'
+    SetCtlColors $DroplistArch ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
+    SendMessage $DroplistArch ${WM_SETFONT} $FontNormal 0
+
+    ${If} ${RunningX64}
+      ${NSD_CB_SelectString} $DroplistArch "$(VERSION_64BIT)"
+    ${Else}
+      ${NSD_CB_SelectString} $DroplistArch "$(VERSION_32BIT)"
+    ${EndIf}
+  ${EndIf}
+
   GetDlgItem $0 $HWNDPARENT 1 ; Install button
   ${If} ${FileExists} "$INSTDIR\${FileMainEXE}"
     SendMessage $0 ${WM_SETTEXT} 0 "STR:$(UPGRADE_BUTTON)"
   ${Else}
     SendMessage $0 ${WM_SETTEXT} 0 "STR:$(INSTALL_BUTTON)"
   ${EndIf}
   ${NSD_SetFocus} $0
 
@@ -1132,19 +1209,18 @@ Function leaveOptions
   Pop $0
   ${GetSecondsElapsed} "$StartOptionsPhaseTickCount" "$0" $OptionsPhaseSeconds
   ; It is possible for this value to be 0 if the user clicks fast enough so
   ; increment the value by 1 if it is 0.
   ${If} $OptionsPhaseSeconds == 0
     IntOp $OptionsPhaseSeconds $OptionsPhaseSeconds + 1
   ${EndIf}
 
-  ${NSD_GetState} $CheckboxShortcutOnBar $CheckboxShortcutOnBar
-  ${NSD_GetState} $CheckboxShortcutInStartMenu $CheckboxShortcutInStartMenu
-  ${NSD_GetState} $CheckboxShortcutOnDesktop $CheckboxShortcutOnDesktop
+  ${NSD_GetState} $CheckboxShortcuts $CheckboxShortcuts
+  ${NSD_GetText} $DroplistArch $DroplistArch
   ${NSD_GetState} $CheckboxSendPing $CheckboxSendPing
 !ifdef MOZ_MAINTENANCE_SERVICE
   ${NSD_GetState} $CheckboxInstallMaintSvc $CheckboxInstallMaintSvc
 !endif
 
 FunctionEnd
 
 Function createInstall
@@ -1329,18 +1405,25 @@ Function createInstall
   ${NSD_FreeImage} $0
   ${NSD_FreeImage} $HwndBitmapBlurb1
   ${NSD_FreeImage} $HwndBitmapBlurb2
   ${NSD_FreeImage} $HWndBitmapBlurb3
 FunctionEnd
 
 Function StartDownload
   ${NSD_KillTimer} StartDownload
-  InetBgDL::Get "${URLStubDownload}${URLStubDownloadAppend}" "$PLUGINSDIR\download.exe" \
-                /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+  ${If} $DroplistArch == "$(VERSION_64BIT)"
+    InetBgDL::Get "${URLStubDownload64}${URLStubDownloadAppend}" \
+                  "$PLUGINSDIR\download.exe" \
+                  /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+  ${Else}
+    InetBgDL::Get "${URLStubDownload32}${URLStubDownloadAppend}" \
+                  "$PLUGINSDIR\download.exe" \
+                  /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
+  ${EndIf}
   StrCpy $4 ""
   ${NSD_CreateTimer} OnDownload ${DownloadIntervalMS}
   ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
     RmDir /r "$INSTDIR\${TO_BE_DELETED}"
   ${EndIf}
 FunctionEnd
 
 Function SetProgressBars
@@ -1551,33 +1634,28 @@ Function OnDownload
 
       ; Instead of extracting the files we use the downloaded installer to
       ; install in case it needs to perform operations that the stub doesn't
       ; know about.
       WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
       ; Don't create the QuickLaunch or Taskbar shortcut from the launched installer
       WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false"
 
-      ; Either avoid or force adding a taskbar pin based on the checkbox value:
-      ${If} $CheckboxShortcutOnBar == 0
+      ; Always create a start menu shortcut, so the user always has some way
+      ; to access the application.
+      WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
+
+      ; Either avoid or force adding a taskbar pin and desktop shortcut
+      ; based on the checkbox value.
+      ${If} $CheckboxShortcuts == 0
         WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
+        WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
       ${Else}
         WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
-      ${EndIf}
-
-      ${If} $CheckboxShortcutOnDesktop == 1
         WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
-      ${Else}
-        WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
-      ${EndIf}
-
-      ${If} $CheckboxShortcutInStartMenu == 1
-        WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
-      ${Else}
-        WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "false"
       ${EndIf}
 
 !ifdef MOZ_MAINTENANCE_SERVICE
       ${If} $CheckboxInstallMaintSvc == 1
         WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "true"
       ${Else}
         WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
       ${EndIf}
@@ -1744,17 +1822,17 @@ Function FinishInstall
     ${If} ${Errors} ; Not elevated
       Call ExecSetAsDefaultAppUser
     ${Else} ; Elevated - execute the function in the unelevated process
       GetFunctionAddress $0 ExecSetAsDefaultAppUser
       UAC::ExecCodeSegment $0
     ${EndIf}
   ${EndIf}
 
-  ${If} $CheckboxShortcutOnBar == 1
+  ${If} $CheckboxShortcuts == 1
     ${If} ${AtMostWinVista}
       ClearErrors
       ${GetParameters} $0
       ClearErrors
       ${GetOptions} "$0" "/UAC:" $0
       ${If} ${Errors}
         Call AddQuickLaunchShortcut
       ${Else}
@@ -1880,16 +1958,40 @@ Function OnClick_ButtonBrowse
   ${EndIf}
 
   ${If} $0 != ""
     StrCpy $INSTDIR "$0"
     System::Call 'user32::SetWindowTextW(i $DirRequest, w "$INSTDIR")'
   ${EndIf}
 FunctionEnd
 
+Function OnChange_DroplistArch
+  ; When the user changes the 32/64-bit setting, change the default install path
+  ; to use the correct version of Program Files. But only do that if the user
+  ; hasn't selected their own install path yet, and if we didn't select our
+  ; default as the location of an existing install.
+  ${If} $INSTDIR == $InitialInstallDir
+    ${NSD_GetText} $DroplistArch $0
+    ${If} $0 == "$(VERSION_32BIT)"
+      ${If} $PreviousInstallArch == 32
+        StrCpy $InitialInstallDir $PreviousInstallDir
+      ${Else}
+        StrCpy $InitialInstallDir "${DefaultInstDir32bit}"
+      ${EndIf}
+    ${Else}
+      ${If} $PreviousInstallArch == 64
+        StrCpy $InitialInstallDir $PreviousInstallDir
+      ${Else}
+        StrCpy $InitialInstallDir "${DefaultInstDir64bit}"
+      ${EndIf}
+    ${EndIf}
+    ${NSD_SetText} $DirRequest $InitialInstallDir
+  ${EndIf}
+FunctionEnd
+
 Function CheckSpace
   ${If} "$ExistingTopDir" != ""
     StrLen $0 "$ExistingTopDir"
     StrLen $1 "$INSTDIR"
     ${If} $0 <= $1
       StrCpy $2 "$INSTDIR" $3
       ${If} "$2" == "$ExistingTopDir"
         Return
@@ -1944,16 +2046,17 @@ Function CanWrite
         Return
       ${EndIf}
     ${Loop}
   ${EndUnless}
 
   GetTempFileName $2 "$0"
   Delete $2
   CreateDirectory "$2"
+
   ${If} ${FileExists} "$2"
     ${If} ${FileExists} "$INSTDIR"
       GetTempFileName $3 "$INSTDIR"
     ${Else}
       GetTempFileName $3 "$2"
     ${EndIf}
     ${If} ${FileExists} "$3"
       Delete "$3"
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -193,8 +193,18 @@ disableContainersAlertTitle=Close All Co
 disableContainersMsg=If you disable Container Tabs now, #S container tab will be closed. Are you sure you want to disable Container Tabs?;If you disable Container Tabs now, #S container tabs will be closed. Are you sure you want to disable Container Tabs?
 
 # LOCALIZATION NOTE (disableContainersOkButton): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #S is the number of container tabs
 disableContainersOkButton=Close #S Container Tab;Close #S Container Tabs
 
 disableContainersButton2=Keep enabled
+
+removeContainerAlertTitle=Remove This Container?
+
+# LOCALIZATION NOTE (removeContainerMsg): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #S is the number of container tabs
+removeContainerMsg=If you remove this Container now, #S container tab will be closed. Are you sure you want to remove this Container?;If you remove this Container now, #S container tabs will be closed. Are you sure you want to remove this Container?
+
+removeContainerOkButton=Remove this Container
+removeContainerButton2=Don’t remove this Container
--- a/browser/locales/en-US/installer/nsisstrings.properties
+++ b/browser/locales/en-US/installer/nsisstrings.properties
@@ -36,21 +36,21 @@ WARN_MANUALLY_CLOSE_APP_LAUNCH=$BrandSho
 ERROR_DOWNLOAD=Your download was interrupted.\n\nPlease click the OK button to continue.
 
 INSTALL_BUTTON=&Install
 UPGRADE_BUTTON=&Upgrade
 CANCEL_BUTTON=Cancel
 OPTIONS_BUTTON=&Options
 
 MAKE_DEFAULT=&Make $BrandShortName my default browser
-CREATE_SHORTCUTS=Create Shortcuts for $BrandShortName:
-ADD_SC_TASKBAR=On my &Task bar
-ADD_SC_QUICKLAUNCHBAR=On my &Quick Launch bar
-ADD_CheckboxShortcutInStartMenu=In my &Start Menu Programs Folder
-ADD_CheckboxShortcutOnDesktop=On my &Desktop
+ADD_SC_DESKTOP_TASKBAR=&Create desktop and taskbar shortcuts for $BrandShortName
+ADD_SC_DESKTOP_QUICKLAUNCHBAR=&Create desktop and quick launch shortcuts for $BrandShortName
+VERSION_32BIT=32-bit $BrandShortName
+VERSION_64BIT=64-bit $BrandShortName
+ARCH_DROPLIST_LABEL=&Version to install
 SPACE_REQUIRED=Space Required:
 SPACE_AVAILABLE=Space Available:
 ONE_MOMENT_INSTALL=One moment, $BrandShortName will launch as soon as the install is complete…
 ONE_MOMENT_UPGRADE=One moment, $BrandShortName will launch as soon as the upgrade is complete…
 INSTALL_MAINT_SERVICE=&Install the $BrandShortName background update service
 SEND_PING=S&end information about this installation to Mozilla
 BROWSE_BUTTON=B&rowse…
 DEST_FOLDER=Destination Folder
--- a/browser/themes/shared/autocomplete.inc.css
+++ b/browser/themes/shared/autocomplete.inc.css
@@ -7,25 +7,42 @@
 #PopupAutoComplete > richlistbox > richlistitem {
   height: 20px;
   min-height: 20px;
   border: 0;
   border-radius: 0;
   padding: 0px 1px 0px 1px;
 }
 
+#PopupAutoComplete > richlistbox > richlistitem > .ac-site-icon {
+  margin-inline-start: 4px;
+  margin-inline-end: 0;
+}
+
 #PopupAutoComplete > richlistbox > richlistitem > .ac-title {
   font: icon;
-  margin-inline-start: 6px;
+  margin-inline-start: 4px;
 }
 
 #PopupAutoComplete > richlistbox {
   padding: 0;
 }
 
+
+/* Login form autocompletion */
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon {
+  display: initial;
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#login);
+}
+
+#PopupAutoComplete > richlistbox > richlistitem[originaltype="login"] > .ac-site-icon[selected] {
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#login-highlighted);
+}
+
+
 /* Insecure field warning */
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] {
   background-color: #F6F6F6; /* Bug 1319176 */
 }
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype="insecureWarning"] > .ac-site-icon {
   list-style-image: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon);
 }
--- a/browser/themes/shared/notification-icons.svg
+++ b/browser/themes/shared/notification-icons.svg
@@ -16,16 +16,21 @@
     }
     .blocked:target ~ #strikeout {
       display: block;
     }
     .blocked {
       clip-path: url(#clip);
     }
 
+    #login-highlighted {
+      fill: HighlightText;
+      fill-opacity: 1;
+    }
+
     #plugin-blocked,
     #plugin-blocked:target ~ #strikeout {
       fill: #d92215;
       fill-opacity: 1;
     }
 
     #camera-sharing,
     #microphone-sharing,
@@ -68,16 +73,17 @@
   <use id="geo-linux-blocked" class="blocked" xlink:href="#geo-linux-icon" />
   <use id="geo-linux-detailed" xlink:href="#geo-linux-detailed-icon" />
   <use id="geo-windows" xlink:href="#geo-windows-icon" />
   <use id="geo-windows-blocked" class="blocked" xlink:href="#geo-windows-icon" />
   <use id="geo-windows-detailed" xlink:href="#geo-windows-detailed-icon" />
   <use id="indexedDB" xlink:href="#indexedDB-icon" />
   <use id="indexedDB-blocked" class="blocked" xlink:href="#indexedDB-icon" />
   <use id="login" xlink:href="#login-icon" />
+  <use id="login-highlighted" class="highlighted" xlink:href="#login-icon" />
   <use id="login-detailed" xlink:href="#login-detailed-icon" />
   <use id="microphone" xlink:href="#microphone-icon" />
   <use id="microphone-sharing" xlink:href="#microphone-icon"/>
   <use id="microphone-blocked" class="blocked" xlink:href="#microphone-icon" />
   <use id="microphone-detailed" xlink:href="#microphone-detailed-icon" />
   <use id="plugin" xlink:href="#plugin-icon" />
   <use id="plugin-blocked" class="blocked" xlink:href="#plugin-icon" />
   <use id="popup" xlink:href="#popup-icon" />
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1101,17 +1101,21 @@ toolbar[brighttext] #close-button {
        (-moz-os-version: windows-win7) {
   #window-controls {
     margin-inline-start: 4px;
   }
 
   #minimize-button,
   #restore-button,
   #close-button {
-    list-style-image: url("chrome://global/skin/icons/windowControls.png");
+    /* Important to ensure this applies even on toolbar[brighttext] */
+    list-style-image: url("chrome://global/skin/icons/windowControls.png") !important;
+    /* Also override background color to a avoid hover background styling
+     * leaking through around the image. */
+    background-color: transparent !important;
     padding: 0;
   }
 
   #minimize-button {
     -moz-image-region: rect(0, 16px, 16px, 0);
   }
 
   #minimize-button:hover {
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -407,37 +407,39 @@ bool typeIsRefPtr(QualType Q) {
   if (name == "RefPtr" || name == "nsCOMPtr") {
     return true;
   }
   return false;
 }
 
 // The method defined in clang for ignoring implicit nodes doesn't work with
 // some AST trees. To get around this, we define our own implementation of
-// IgnoreImplicit.
-const Stmt *IgnoreImplicit(const Stmt *s) {
+// IgnoreTrivials.
+const Stmt *IgnoreTrivials(const Stmt *s) {
   while (true) {
     if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
       s = ewc->getSubExpr();
     } else if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
       s = mte->GetTemporaryExpr();
     } else if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
       s = bte->getSubExpr();
     } else if (auto *ice = dyn_cast<ImplicitCastExpr>(s)) {
       s = ice->getSubExpr();
+    } else if (auto *pe = dyn_cast<ParenExpr>(s)) {
+      s = pe->getSubExpr();
     } else {
       break;
     }
   }
 
   return s;
 }
 
-const Expr *IgnoreImplicit(const Expr *e) {
-  return cast<Expr>(IgnoreImplicit(static_cast<const Stmt *>(e)));
+const Expr *IgnoreTrivials(const Expr *e) {
+  return cast<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e)));
 }
 }
 
 class CustomTypeAnnotation {
   enum ReasonKind {
     RK_None,
     RK_Direct,
     RK_ArrayElement,
@@ -900,16 +902,18 @@ AST_MATCHER(CXXRecordDecl, needsMemMovab
 /// This matcher will select classes which require all members to be memmovable
 AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
   return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members");
 }
 
 AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
   const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
   return
+      // Skip constructors in system headers
+      !ASTIsInSystemHeader(Declaration->getASTContext(), *Declaration) &&
       // Skip ignored namespaces and paths
       !isInIgnoredNamespaceForImplicitCtor(Declaration) &&
       !isIgnoredPathForImplicitCtor(Declaration) &&
       // We only want Converting constructors
       Declaration->isConvertingConstructor(false) &&
       // We don't want copy of move constructors, as those are allowed to be
       // implicit
       !Declaration->isCopyOrMoveConstructor() &&
@@ -1913,33 +1917,33 @@ void DiagnosticsMatcher::KungFuDeathGrip
     return;
   }
 
   // Not interested in parameters.
   if (isa<ImplicitParamDecl>(D) || isa<ParmVarDecl>(D)) {
     return;
   }
 
-  const Expr *E = IgnoreImplicit(D->getInit());
+  const Expr *E = IgnoreTrivials(D->getInit());
   const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E);
   if (CE && CE->getNumArgs() == 0) {
     // We don't report an error when we construct and don't use a nsCOMPtr /
     // nsRefPtr with no arguments. We don't report it because the error is not
     // related to the current check. In the future it may be reported through a
     // more generic mechanism.
     return;
   }
 
   // We don't want to look at the single argument conversion constructors
   // which are inbetween the declaration and the actual object which we are
   // assigning into the nsCOMPtr/RefPtr. To do this, we repeatedly
-  // IgnoreImplicit, then look at the expression. If it is one of these
+  // IgnoreTrivials, then look at the expression. If it is one of these
   // conversion constructors, we ignore it and continue to dig.
   while ((CE = dyn_cast<CXXConstructExpr>(E)) && CE->getNumArgs() == 1) {
-    E = IgnoreImplicit(CE->getArg(0));
+    E = IgnoreTrivials(CE->getArg(0));
   }
 
   // We allow taking a kungFuDeathGrip of `this` because it cannot change
   // beneath us, so calling directly through `this` is OK. This is the same
   // for local variable declarations.
   //
   // We also don't complain about unused RefPtrs which are constructed from
   // the return value of a new expression, as these are required in order to
--- a/build/clang-plugin/tests/TestKungFuDeathGrip.cpp
+++ b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp
@@ -30,34 +30,36 @@ public:
 class Type {
 public:
   static nsCOMPtr<Type> someStaticCOMPtr;
 
   void f(nsCOMPtr<Type> ignoredArgument, Type *param) {
     nsCOMPtr<Type> never_referenced;
     nsCOMPtr<Type> kfdg_t1(this);
     nsCOMPtr<Type> kfdg_t2 = this;
+    nsCOMPtr<Type> kfdg_t3 = (this);
 
     nsCOMPtr<Type> kfdg_m1(p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}}
     nsCOMPtr<Type> kfdg_m2 = p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}}
     nsCOMPtr<Type> kfdg_m3(p);
     kfdg_m3.mPtr->f(nullptr, nullptr);
     nsCOMPtr<Type> kfdg_m4 = p;
     kfdg_m4.mPtr->f(nullptr, nullptr);
 
     nsCOMPtr<Type> kfdg_a1((already_AddRefed<Type>()));
     nsCOMPtr<Type> kfdg_a2 = already_AddRefed<Type>();
 
     nsCOMPtr<Type> kfdg_p1(param);
     nsCOMPtr<Type> kfdg_p2 = param;
 
 
     RefPtr<Type> never_referenced2;
-    RefPtr<Type> kfdg_t3(this);
-    RefPtr<Type> kfdg_t4 = this;
+    RefPtr<Type> kfdg_t4(this);
+    RefPtr<Type> kfdg_t5 = this;
+    RefPtr<Type> kfdg_t6 = (this);
 
     RefPtr<Type> kfdg_m5(p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}}
     RefPtr<Type> kfdg_m6 = p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}}
     RefPtr<Type> kfdg_m7(p);
     kfdg_m7.mPtr->f(nullptr, nullptr);
     RefPtr<Type> kfdg_m8 = p;
     kfdg_m8.mPtr->f(nullptr, nullptr);
 
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -330,18 +330,21 @@ def lib_path(target, vc_path, windows_sd
     sdk_target = {
         'x86': 'x86',
         'x86_64': 'x64',
         'arm': 'arm',
     }.get(target.cpu)
 
     atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target)
     if not os.path.isdir(atlmfc_dir):
-        die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). '
-            'Please install them.' % vc_path)
+        # For Visual Studio 2017
+        atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', sdk_target)
+        if not os.path.isdir(atlmfc_dir):
+            die('Cannot find the ATL/MFC libraries in the Visual C++ directory '
+                '(%s). Please install them.' % vc_path)
 
 
     libs = []
     lib_env = os.environ.get('LIB')
     if lib_env:
         libs.append(lib_env)
     libs.extend((
         os.path.join(vc_path, 'lib', *vc_target),
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -321,19 +321,22 @@ nsScriptSecurityManager::GetChannelResul
             if (!principalToInherit) {
               principalToInherit = loadInfo->TriggeringPrincipal();
             }
             principalToInherit.forget(aPrincipal);
             return NS_OK;
         }
 
         nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
-        if (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
-            securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
-            securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+        // The data: inheritance flags should only apply to the initial load,
+        // not to loads that it might have redirected to.
+        if (loadInfo->RedirectChain().IsEmpty() &&
+            (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
+             securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
+             securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
 
             nsCOMPtr<nsIURI> uri;
             nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
             NS_ENSURE_SUCCESS(rv, rv); 
             nsCOMPtr<nsIPrincipal> principalToInherit = loadInfo->PrincipalToInherit();
             if (!principalToInherit) {
               principalToInherit = loadInfo->TriggeringPrincipal();
             }
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -82,17 +82,17 @@ included_inclnames_to_ignore = set([
     'shellmoduleloader.out.h',  # generated in $OBJDIR
     'unicode/timezone.h',       # ICU
     'unicode/ucal.h',           # ICU
     'unicode/uclean.h',         # ICU
     'unicode/ucol.h',           # ICU
     'unicode/udat.h',           # ICU
     'unicode/udatpg.h',         # ICU
     'unicode/uenum.h',          # ICU
-    'unicode/unorm.h',          # ICU
+    'unicode/unorm2.h',         # ICU
     'unicode/unum.h',           # ICU
     'unicode/unumsys.h',        # ICU
     'unicode/ustring.h',        # ICU
     'unicode/utypes.h',         # ICU
     'vtune/VTuneWrapper.h'      # VTune
 ])
 
 # These files have additional constraints on where they are #included, so we
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -5,17 +5,17 @@
 "use strict";
 
 const Services = require("Services");
 const { Task } = require("devtools/shared/task");
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
 const { updateGrids } = require("./actions/grids");
-const App = createFactory(require("./components/app"));
+const App = createFactory(require("./components/App"));
 const Store = require("./store");
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
   new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 function LayoutView(inspector, window) {
   this.document = window.document;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13780,65 +13780,70 @@ class OnLinkClickEvent : public Runnable
 {
 public:
   OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
                    nsIURI* aURI,
                    const char16_t* aTargetSpec,
                    const nsAString& aFileName,
                    nsIInputStream* aPostDataStream,
                    nsIInputStream* aHeadersDataStream,
+                   bool aNoOpenerImplied,
                    bool aIsTrusted);
 
   NS_IMETHOD Run() override
   {
     nsAutoPopupStatePusher popupStatePusher(mPopupState);
 
     // We need to set up an AutoJSAPI here for the following reason: When we do
     // OnLinkClickSync we'll eventually end up in nsGlobalWindow::OpenInternal
     // which only does popup blocking if !LegacyIsCallerChromeOrNativeCode().
     // So we need to fake things so that we don't look like native code as far
     // as LegacyIsCallerNativeCode() is concerned.
     AutoJSAPI jsapi;
     if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
       mHandler->OnLinkClickSync(mContent, mURI,
                                 mTargetSpec.get(), mFileName,
                                 mPostDataStream, mHeadersDataStream,
+                                mNoOpenerImplied,
                                 nullptr, nullptr);
     }
     return NS_OK;
   }
 
 private:
   RefPtr<nsDocShell> mHandler;
   nsCOMPtr<nsIURI> mURI;
   nsString mTargetSpec;
   nsString mFileName;
   nsCOMPtr<nsIInputStream> mPostDataStream;
   nsCOMPtr<nsIInputStream> mHeadersDataStream;
   nsCOMPtr<nsIContent> mContent;
   PopupControlState mPopupState;
+  bool mNoOpenerImplied;
   bool mIsTrusted;
 };
 
 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler,
                                    nsIContent* aContent,
                                    nsIURI* aURI,
                                    const char16_t* aTargetSpec,
                                    const nsAString& aFileName,
                                    nsIInputStream* aPostDataStream,
                                    nsIInputStream* aHeadersDataStream,
+                                   bool aNoOpenerImplied,
                                    bool aIsTrusted)
   : mHandler(aHandler)
   , mURI(aURI)
   , mTargetSpec(aTargetSpec)
   , mFileName(aFileName)
   , mPostDataStream(aPostDataStream)
   , mHeadersDataStream(aHeadersDataStream)
   , mContent(aContent)
   , mPopupState(mHandler->mScriptGlobal->GetPopupControlState())
+  , mNoOpenerImplied(aNoOpenerImplied)
   , mIsTrusted(aIsTrusted)
 {
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const char16_t* aTargetSpec,
@@ -13866,40 +13871,46 @@ nsDocShell::OnLinkClick(nsIContent* aCon
   if (aContent->IsEditable()) {
     return NS_OK;
   }
 
   nsresult rv = NS_ERROR_FAILURE;
   nsAutoString target;
 
   nsCOMPtr<nsIWebBrowserChrome3> browserChrome3 = do_GetInterface(mTreeOwner);
+  bool noOpenerImplied = false;
   if (browserChrome3) {
     nsCOMPtr<nsIDOMNode> linkNode = do_QueryInterface(aContent);
     nsAutoString oldTarget(aTargetSpec);
     rv = browserChrome3->OnBeforeLinkTraversal(oldTarget, aURI,
                                                linkNode, mIsAppTab, target);
+    if (!oldTarget.Equals(target)) {
+      noOpenerImplied = true;
+    }
   }
 
   if (NS_FAILED(rv)) {
     target = aTargetSpec;
   }
 
   nsCOMPtr<nsIRunnable> ev =
     new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
-                         aPostDataStream, aHeadersDataStream, aIsTrusted);
+                         aPostDataStream, aHeadersDataStream, noOpenerImplied,
+                         aIsTrusted);
   return NS_DispatchToCurrentThread(ev);
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent* aContent,
                             nsIURI* aURI,
                             const char16_t* aTargetSpec,
                             const nsAString& aFileName,
                             nsIInputStream* aPostDataStream,
                             nsIInputStream* aHeadersDataStream,
+                            bool aNoOpenerImplied,
                             nsIDocShell** aDocShell,
                             nsIRequest** aRequest)
 {
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nullptr;
   }
   if (aRequest) {
@@ -13955,16 +13966,19 @@ nsDocShell::OnLinkClickSync(nsIContent* 
                  INTERNAL_LOAD_FLAGS_NO_OPENER;
         // We now have all the flags we could possibly have, so just stop.
         break;
       }
       if (token.LowerCaseEqualsLiteral("noopener")) {
         flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
       }
     }
+    if (aNoOpenerImplied) {
+      flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
+    }
   }
 
   // Get the owner document of the link that was clicked, this will be
   // the document that the link is in, or the last document that the
   // link was in. From that document, we'll get the URI to use as the
   // referer, since the current URI in this docshell may be a
   // new document that we're in the process of loading.
   nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -202,16 +202,17 @@ public:
                          nsIInputStream* aHeadersDataStream,
                          bool aIsTrusted) override;
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
                              nsIInputStream* aHeadersDataStream = 0,
+                             bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0) override;
   NS_IMETHOD OnOverLink(nsIContent* aContent,
                         nsIURI* aURI,
                         const char16_t* aTargetSpec) override;
   NS_IMETHOD OnLeaveLink() override;
 
   nsDocShellInfoLoadType ConvertLoadTypeToDocShellLoadInfo(uint32_t aLoadType);
--- a/docshell/base/nsILinkHandler.h
+++ b/docshell/base/nsILinkHandler.h
@@ -54,25 +54,27 @@ public:
    *
    * @param aContent the content for the frame that generated the trigger
    * @param aURI a URI obect that defines the destination for the link
    * @param aTargetSpec indicates where the link is targeted (may be an empty
    *        string)
    * @param aFileName non-null when the link should be downloaded as the given file
    * @param aPostDataStream the POST data to send
    * @param aHeadersDataStream ???
+   * @param aNoOpenerImplied if the link implies "noopener"
    * @param aDocShell (out-param) the DocShell that the request was opened on
    * @param aRequest the request that was opened
    */
   NS_IMETHOD OnLinkClickSync(nsIContent* aContent,
                              nsIURI* aURI,
                              const char16_t* aTargetSpec,
                              const nsAString& aFileName,
                              nsIInputStream* aPostDataStream = 0,
                              nsIInputStream* aHeadersDataStream = 0,
+                             bool aNoOpenerImplied = false,
                              nsIDocShell** aDocShell = 0,
                              nsIRequest** aRequest = 0) = 0;
 
   /**
    * Process a mouse-over a link.
    *
    * @param aContent the linked content.
    * @param aURI an URI object that defines the destination for the link
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -81,16 +81,21 @@ FindAnimationsForCompositor(const nsIFra
       effects->PropertiesForAnimationsLevel().HasProperty(aProperty)) {
     return false;
   }
 
   if (aFrame->RefusedAsyncAnimation()) {
     return false;
   }
 
+  if (aFrame->StyleContext()->StyleSource().IsServoComputedValues()) {
+    NS_ERROR("stylo: cannot handle compositor-driven animations yet");
+    return false;
+  }
+
   // The animation cascade will almost always be up-to-date by this point
   // but there are some cases such as when we are restoring the refresh driver
   // from test control after seeking where it might not be the case.
   //
   // Those cases are probably not important but just to be safe, let's make
   // sure the cascade is up to date since if it *is* up to date, this is
   // basically a no-op.
   Maybe<NonOwningAnimationTarget> pseudoElement =
@@ -198,19 +203,21 @@ EffectCompositor::RequestRestyle(dom::El
     if (!hasPendingRestyle) {
       PostRestyleForAnimation(aElement, aPseudoType, aCascadeLevel);
     }
     elementsToRestyle.Put(key, true);
   }
 
   if (aRestyleType == RestyleType::Layer) {
     // Prompt layers to re-sync their animations.
-    MOZ_ASSERT(mPresContext->RestyleManager()->IsGecko(),
-               "stylo: Servo-backed style system should not be using "
+    if (mPresContext->RestyleManager()->IsServo()) {
+      NS_ERROR("stylo: Servo-backed style system should not be using "
                "EffectCompositor");
+      return;
+    }
     mPresContext->RestyleManager()->AsGecko()->IncrementAnimationGeneration();
     EffectSet* effectSet =
       EffectSet::GetEffectSet(aElement, aPseudoType);
     if (effectSet) {
       effectSet->UpdateAnimationGeneration(mPresContext);
     }
   }
 }
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -165,21 +165,22 @@ DOMIntersectionObserver::Unobserve(Eleme
 }
 
 bool
 DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
 {
     if (!mObservationTargets.Contains(&aTarget)) {
         return false;
     }
-    if (mObservationTargets.Count() == 1) {
+
+    mObservationTargets.RemoveEntry(&aTarget);
+    if (mObservationTargets.Count() == 0) {
         Disconnect();
         return false;
     }
-    mObservationTargets.RemoveEntry(&aTarget);
     return true;
 }
 
 void
 DOMIntersectionObserver::Connect()
 {
   if (mConnected) {
     return;
@@ -190,26 +191,27 @@ DOMIntersectionObserver::Connect()
 }
 
 void
 DOMIntersectionObserver::Disconnect()
 {
   if (!mConnected) {
     return;
   }
+
+  mConnected = false;
   for (auto iter = mObservationTargets.Iter(); !iter.Done(); iter.Next()) {
     Element* target = iter.Get()->GetKey();
     target->UnregisterIntersectionObserver(this);
   }
   mObservationTargets.Clear();
   if (mOwner) {
     nsIDocument* document = mOwner->GetExtantDoc();
     document->RemoveIntersectionObserver(this);
   }
-  mConnected = false;
 }
 
 void
 DOMIntersectionObserver::TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal)
 {
   aRetVal.SwapElements(mQueuedEntries);
   mQueuedEntries.Clear();
 }
@@ -273,17 +275,20 @@ DOMIntersectionObserver::Update(nsIDocum
     }
   } else {
     nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell();
     if (presShell) {
       rootFrame = presShell->GetRootScrollFrame();
       if (rootFrame) {
         nsPresContext* presContext = rootFrame->PresContext();
         while (!presContext->IsRootContentDocument()) {
-          presContext = rootFrame->PresContext()->GetParentPresContext();
+          presContext = presContext->GetParentPresContext();
+          if (!presContext) {
+            break;
+          }
           rootFrame = presContext->PresShell()->GetRootScrollFrame();
         }
         root = rootFrame->GetContent()->AsElement();
         nsIScrollableFrame* scrollFrame = do_QueryFrame(rootFrame);
         rootRect = scrollFrame->GetScrollPortRect();
       }
     }
   }
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -231,60 +231,20 @@ UnwrapObject(JSObject* obj, U& value, pr
 template <prototypes::ID PrototypeID, class T, typename U>
 MOZ_ALWAYS_INLINE nsresult
 UnwrapObject(JSObject* obj, U& value)
 {
   return UnwrapObject<T>(obj, value, PrototypeID,
                          PrototypeTraits<PrototypeID>::Depth);
 }
 
-inline bool
-IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj,
-                  bool* notDateOrRegExp)
-{
-  MOZ_ASSERT(obj);
-
-  js::ESClass cls;
-  if (!js::GetBuiltinClass(cx, obj, &cls)) {
-    return false;
-  }
-
-  *notDateOrRegExp = cls != js::ESClass::Date && cls != js::ESClass::RegExp;
-  return true;
-}
-
 MOZ_ALWAYS_INLINE bool
-IsObjectValueConvertibleToDictionary(JSContext* cx,
-                                     JS::Handle<JS::Value> objVal,
-                                     bool* convertible)
+IsConvertibleToDictionary(JS::Handle<JS::Value> val)
 {
-  JS::Rooted<JSObject*> obj(cx, &objVal.toObject());
-  return IsNotDateOrRegExp(cx, obj, convertible);
-}
-
-MOZ_ALWAYS_INLINE bool
-IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val,
-                          bool* convertible)
-{
-  if (val.isNullOrUndefined()) {
-    *convertible = true;
-    return true;
-  }
-  if (!val.isObject()) {
-    *convertible = false;
-    return true;
-  }
-  return IsObjectValueConvertibleToDictionary(cx, val, convertible);
-}
-
-MOZ_ALWAYS_INLINE bool
-IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj,
-                                 bool* convertible)
-{
-  return IsNotDateOrRegExp(cx, obj, convertible);
+  return val.isNullOrUndefined() || val.isObject();
 }
 
 // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
 // constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
 // The end of the prototype objects should be the start of the interface
 // objects, and the end of the interface objects should be the start of the
 // named properties objects.
 static_assert((size_t)constructors::id::_ID_Start ==
@@ -2304,16 +2264,32 @@ IdEquals(jsid id, const char* string)
 
 inline bool
 AddStringToIDVector(JSContext* cx, JS::AutoIdVector& vector, const char* name)
 {
   return vector.growBy(1) &&
          AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(), name);
 }
 
+// We use one constructor JSNative to represent all DOM interface objects (so
+// we can easily detect when we need to wrap them in an Xray wrapper). We store
+// the real JSNative in the mNative member of a JSNativeHolder in the
+// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
+// specific interface object. We also store the NativeProperties in the
+// JSNativeHolder.
+// Note that some interface objects are not yet a JSFunction but a normal
+// JSObject with a DOMJSClass, those do not use these slots.
+
+enum {
+  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
+};
+
+bool
+Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
+
 // Implementation of the bits that XrayWrapper needs
 
 /**
  * This resolves operations, attributes and constants of the interfaces for obj.
  *
  * wrapper is the Xray JS object.
  * obj is the target object of the Xray, a binding's instance object or a
  *     interface or interface prototype object.
@@ -2377,16 +2353,19 @@ XrayGetNativeProto(JSContext* cx, JS::Ha
     const DOMJSClass* domClass = GetDOMClass(obj);
     if (domClass) {
       ProtoHandleGetter protoGetter = domClass->mGetProto;
       if (protoGetter) {
         protop.set(protoGetter(cx));
       } else {
         protop.set(JS::GetRealmObjectPrototype(cx));
       }
+    } else if (JS_ObjectIsFunction(cx, obj)) {
+      MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
+      protop.set(JS::GetRealmFunctionPrototype(cx));
     } else {
       const js::Class* clasp = js::GetObjectClass(obj);
       MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
       ProtoGetter protoGetter =
         DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
       protop.set(protoGetter(cx));
     }
   }
@@ -2443,49 +2422,33 @@ GetCachedSlotStorageObject(JSContext* cx
 }
 
 extern NativePropertyHooks sEmptyNativePropertyHooks;
 
 extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
 
 extern const js::ObjectOps sInterfaceObjectClassObjectOps;
 
-// We use one constructor JSNative to represent all DOM interface objects (so
-// we can easily detect when we need to wrap them in an Xray wrapper). We store
-// the real JSNative in the mNative member of a JSNativeHolder in the
-// CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT slot of the JSFunction object for a
-// specific interface object. We also store the NativeProperties in the
-// JSNativeHolder.
-// Note that some interface objects are not yet a JSFunction but a normal
-// JSObject with a DOMJSClass, those do not use these slots.
-
-enum {
-  CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0
-};
-
-bool
-Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
-
 inline bool
 UseDOMXray(JSObject* obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) ||
          JS_IsNativeFunction(obj, Constructor) ||
          IsDOMIfaceAndProtoClass(clasp);
 }
 
 #ifdef DEBUG
 inline bool
 HasConstructor(JSObject* obj)
 {
   return JS_IsNativeFunction(obj, Constructor) ||
          js::GetObjectClass(obj)->getConstruct();
 }
- #endif
+#endif
 
 // Helpers for creating a const version of a type.
 template<typename T>
 const T& Constify(T& arg)
 {
   return arg;
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -5778,40 +5778,34 @@ def getJSToNativeConversionInfo(type, de
             }
             """,
             dictLoc=dictLoc,
             val=val,
             desc=firstCap(sourceDescription),
             exceptionCode=exceptionCode)
 
         if failureCode is not None:
-            if isDefinitelyObject:
-                dictionaryTest = "IsObjectValueConvertibleToDictionary"
-            else:
-                dictionaryTest = "IsConvertibleToDictionary"
-
-            template = fill("""
-                { // scope for isConvertible
-                  bool isConvertible;
-                  if (!${testConvertible}(cx, ${val}, &isConvertible)) {
-                    $*{exceptionCode}
-                  }
-                  if (!isConvertible) {
-                    $*{failureCode}
-                  }
-
-                  $*{conversionCode}
-                }
-
-                """,
-                testConvertible=dictionaryTest,
-                val=val,
-                exceptionCode=exceptionCode,
-                failureCode=failureCode,
-                conversionCode=conversionCode)
+            # This means we're part of an overload or union conversion, and
+            # should simply skip stuff if our value is not convertible to
+            # dictionary, instead of trying and throwing.  If we're either
+            # isDefinitelyObject or isNullOrUndefined then we're convertible to
+            # dictionary and don't need to check here.
+            if isDefinitelyObject or isNullOrUndefined:
+                template = conversionCode
+            else:
+                template = fill(
+                    """
+                    if (!IsConvertibleToDictionary(${val})) {
+                      $*{failureCode}
+                    }
+                    $*{conversionCode}
+                    """,
+                    val=val,
+                    failureCode=failureCode,
+                    conversionCode=conversionCode)
         else:
             template = conversionCode
 
         if type.nullable():
             declType = CGTemplatedType("Nullable", declType)
             template = CGIfElseWrapper("${val}.isNullOrUndefined()",
                                        CGGeneric("${declName}.SetNull();\n"),
                                        CGGeneric(template)).define()
@@ -8025,22 +8019,22 @@ class CGMethodCall(CGThing):
                 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
                 caseBody.append(CGGeneric("}\n"))
 
             # Now check for distinguishingArg being various kinds of objects.
             # The spec says to check for the following things in order:
             # 1)  A platform object that's not a platform array object, being
             #     passed to an interface or "object" arg.
             # 2)  A Date object being passed to a Date or "object" arg.
-            # 3)  A RegExp object being passed to a RegExp or "object" arg.
-            # 4)  A callable object being passed to a callback or "object" arg.
-            # 5)  An iterable object being passed to a sequence arg.
-            # 6)  Any non-Date and non-RegExp object being passed to a
-            #     array or callback interface or dictionary or
-            #     "object" arg.
+            #     XXXbz This is actually gone from the spec now, but we still
+            #     have some APIs using Date.
+            # 3)  A callable object being passed to a callback or "object" arg.
+            # 4)  An iterable object being passed to a sequence arg.
+            # 5)  Any object being passed to a array or callback interface or
+            #     dictionary or "object" arg.
 
             # First grab all the overloads that have a non-callback interface
             # (which includes typed arrays and arraybuffers) at the
             # distinguishing index.  We can also include the ones that have an
             # "object" here, since if those are present no other object-typed
             # argument will be.
             objectSigs = [
                 s for s in possibleSignatures
@@ -12586,24 +12580,18 @@ class CGDictionary(CGThing):
                   return false;
                 }
 
                 """,
                 dictName=self.makeClassName(self.dictionary.parent))
         else:
             body += dedent(
                 """
-                { // scope for isConvertible
-                  bool isConvertible;
-                  if (!IsConvertibleToDictionary(cx, val, &isConvertible)) {
-                    return false;
-                  }
-                  if (!isConvertible) {
-                    return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
-                  }
+                if (!IsConvertibleToDictionary(val)) {
+                  return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
                 }
 
                 """)
 
         memberInits = [self.getMemberConversion(m).define()
                        for m in self.memberInfo]
         if memberInits:
             body += fill(
--- a/dom/bindings/parser/tests/test_distinguishability.py
+++ b/dom/bindings/parser/tests/test_distinguishability.py
@@ -158,17 +158,18 @@ def WebIDLTest(parser, harness):
                  "CallbackInterface?", "CallbackInterface2",
                  "object", "Callback", "Callback2", "optional Dict",
                  "optional Dict2", "sequence<long>", "sequence<short>",
                  "MozMap<object>", "MozMap<Dict>", "MozMap<long>",
                  "Date", "Date?", "any",
                  "Promise<any>", "Promise<any>?",
                  "USVString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer",
                  "Uint8Array", "Uint16Array" ]
-    # When we can parse Date and RegExp, we need to add them here.
+    # When we can parse Date, we need to add it here.
+    # XXXbz we can, and should really do that...
 
     # Try to categorize things a bit to keep list lengths down
     def allBut(list1, list2):
         return [a for a in list1 if a not in list2 and
                 (a != "any" and a != "Promise<any>" and a != "Promise<any>?")]
     numerics = [ "long", "short", "long?", "short?" ]
     booleans = [ "boolean", "boolean?" ]
     primitives = numerics + booleans
--- a/dom/bindings/test/chrome.ini
+++ b/dom/bindings/test/chrome.ini
@@ -14,8 +14,9 @@ support-files =
 support-files =
   file_focuser.html
   file_fullScreenPropertyAccessor.html
 skip-if = e10s # prerendering doesn't work in e10s yet
 [test_kill_longrunning_prerendered_content.xul]
 skip-if = e10s # prerendering doesn't work in e10s yet
 [test_bug1123516_maplikesetlikechrome.xul]
 skip-if = debug == false
+[test_bug1287912.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_bug1287912.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1287912
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1287912</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287912">Mozilla Bug 1287912</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="t" src="http://example.org/tests/dom/bindings/test/"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function test()
+{
+  var win = document.getElementById("t").contentWindow;
+  is(Object.getPrototypeOf(win.Image), win.Function.prototype, "The __proto__ of a named constructor is Function.prototype");
+  is(win.Image.prototype, win.HTMLImageElement.prototype, "The prototype property of a named constructor is the interface prototype object");
+  is(win.HTMLImageElement['foo'], undefined, "Should not have a property named foo on the HTMLImageElement interface object");
+  is(win.Image['foo'], undefined, "Should not have a property named foo on the Image named constructor");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/bindings/test/test_exception_messages.html
+++ b/dom/bindings/test/test_exception_messages.html
@@ -39,19 +39,19 @@ https://bugzilla.mozilla.org/show_bug.cg
         "Value being assigned to TreeWalker.currentNode does not implement interface Node.",
         "wrong interface setter call" ],
       [ 'document.createElement("canvas").getContext("2d").fill("bogus")',
         "Argument 1 of CanvasRenderingContext2D.fill 'bogus' is not a valid value for enumeration CanvasWindingRule.",
         "bogus enum value" ],
       [ 'document.createTreeWalker(document, 0xFFFFFFFF, { acceptNode: 5 }).nextNode()',
         "Property 'acceptNode' is not callable.",
         "non-callable callback interface operation property" ],
-      [ '(new TextDecoder).decode(new Uint8Array(), new RegExp())',
+      [ '(new TextDecoder).decode(new Uint8Array(), 5)',
         "Argument 2 of TextDecoder.decode can't be converted to a dictionary.",
-        "regexp passed for a dictionary" ],
+        "primitive passed for a dictionary" ],
       [ 'URL.createObjectURL(null, null)',
         "Argument 1 is not valid for any of the 2-argument overloads of URL.createObjectURL.",
         "overload resolution failure" ],
       [ 'document.createElement("select").add({})',
         "Argument 1 of HTMLSelectElement.add could not be converted to any of: HTMLOptionElement, HTMLOptGroupElement.",
         "invalid value passed for union" ],
       [ 'document.createElement("canvas").getContext("2d").createLinearGradient(0, 1, 0, 1).addColorStop(NaN, "")',
         "Argument 1 of CanvasGradient.addColorStop is not a finite floating-point value.",
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -116,31 +116,31 @@ public:
     template<typename T>
     void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLsizei depth, GLint border,
                               const T& anySrc, GLuint viewElemOffset = 0,
                               GLuint viewElemLengthOverride = 0)
     {
         const char funcName[] = "compressedTexImage3D";
         const uint8_t funcDims = 3;
-        const TexImageSourceAdapter src(anySrc, viewElemOffset, viewElemLengthOverride);
+        const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
         CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
                            height, depth, border, src);
     }
 
     template<typename T>
     void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLint zOffset, GLsizei width, GLsizei height,
                                  GLsizei depth, GLenum unpackFormat, const T& anySrc,
                                  GLuint viewElemOffset = 0,
                                  GLuint viewElemLengthOverride = 0)
     {
         const char funcName[] = "compressedTexSubImage3D";
         const uint8_t funcDims = 3;
-        const TexImageSourceAdapter src(anySrc, viewElemOffset, viewElemLengthOverride);
+        const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
         CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src);
     }
 
     ////////////////////////////////////
 
     void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                            GLint zOffset, GLint x, GLint y, GLsizei width,
@@ -154,27 +154,27 @@ public:
 
     ////////////////////////////////////
 
     template<typename T>
     void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const T& anySrc, ErrorResult& out_error)
     {
-        const TexImageSourceAdapter src(anySrc, &out_error);
+        const TexImageSourceAdapter src(&anySrc, &out_error);
         TexImage3D(target, level, internalFormat, width, height, depth, border,
                    unpackFormat, unpackType, src);
     }
 
     void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const dom::ArrayBufferView& view,
                     GLuint viewElemOffset, ErrorResult&)
     {
-        const TexImageSourceAdapter src(view, viewElemOffset);
+        const TexImageSourceAdapter src(&view, viewElemOffset);
         TexImage3D(target, level, internalFormat, width, height, depth, border,
                    unpackFormat, unpackType, src);
     }
 
 protected:
     void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const TexImageSource& src)
@@ -189,17 +189,17 @@ protected:
 
 public:
     template<typename T>
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType, const T& anySrc,
                        ErrorResult& out_error)
     {
-        const TexImageSourceAdapter src(anySrc, &out_error);
+        const TexImageSourceAdapter src(&anySrc, &out_error);
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, width, height, depth,
                       unpackFormat, unpackType, src);
     }
 
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType,
                        const dom::Nullable<dom::ArrayBufferView>& maybeSrcView,
@@ -207,17 +207,17 @@ public:
     {
         if (IsContextLost())
             return;
 
         if (!ValidateNonNull("texSubImage3D", maybeSrcView))
             return;
         const auto& srcView = maybeSrcView.Value();
 
-        const TexImageSourceAdapter src(srcView, srcElemOffset);
+        const TexImageSourceAdapter src(&srcView, srcElemOffset);
         TexSubImage3D(target, level, xOffset, yOffset, zOffset, width, height, depth,
                       unpackFormat, unpackType, src);
     }
 
 protected:
     void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLint zOffset, GLsizei width, GLsizei height, GLsizei depth,
                        GLenum unpackFormat, GLenum unpackType, const TexImageSource& src)
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -26,25 +26,16 @@ WebGL2Context::CopyBufferSubData(GLenum 
     const auto& readBuffer = ValidateBufferSelection(funcName, readTarget);
     if (!readBuffer)
         return;
 
     const auto& writeBuffer = ValidateBufferSelection(funcName, writeTarget);
     if (!writeBuffer)
         return;
 
-    if (readBuffer->mNumActiveTFOs ||
-        writeBuffer->mNumActiveTFOs)
-    {
-        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                              " object.",
-                              funcName);
-        return;
-    }
-
     if (!ValidateNonNegative(funcName, "readOffset", readOffset) ||
         !ValidateNonNegative(funcName, "writeOffset", writeOffset) ||
         !ValidateNonNegative(funcName, "size", size))
     {
         return;
     }
 
     const auto fnValidateOffsetSize = [&](const char* info, GLintptr offset,
@@ -124,31 +115,16 @@ WebGL2Context::GetBufferSubData(GLenum t
     if (!buffer)
         return;
 
     if (!buffer->ValidateRange(funcName, srcByteOffset, byteLen))
         return;
 
     ////
 
-    if (buffer->mNumActiveTFOs) {
-        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                              " object.",
-                              funcName);
-        return;
-    }
-
-    if (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER &&
-        mBoundTransformFeedback->mIsActive)
-    {
-        ErrorInvalidOperation("%s: Currently bound transform feedback is active.",
-                              funcName);
-        return;
-    }
-
     if (!CheckedInt<GLsizeiptr>(byteLen).isValid()) {
         ErrorOutOfMemory("%s: Size too large.", funcName);
         return;
     }
     const GLsizeiptr glByteLen(byteLen);
 
     ////
 
--- a/dom/canvas/WebGLBuffer.cpp
+++ b/dom/canvas/WebGLBuffer.cpp
@@ -13,25 +13,22 @@
 namespace mozilla {
 
 WebGLBuffer::WebGLBuffer(WebGLContext* webgl, GLuint buf)
     : WebGLContextBoundObject(webgl)
     , mGLName(buf)
     , mContent(Kind::Undefined)
     , mUsage(LOCAL_GL_STATIC_DRAW)
     , mByteLength(0)
-    , mNumActiveTFOs(0)
-    , mBoundForTF(false)
 {
     mContext->mBuffers.insertBack(this);
 }
 
 WebGLBuffer::~WebGLBuffer()
 {
-    MOZ_ASSERT(!mNumActiveTFOs);
     DeleteOnce();
 }
 
 void
 WebGLBuffer::SetContentAfterBind(GLenum target)
 {
     if (mContent != Kind::Undefined)
         return;
@@ -106,23 +103,16 @@ WebGLBuffer::BufferData(GLenum target, s
     // Careful: data.Length() could conceivably be any uint32_t, but GLsizeiptr
     // is like intptr_t.
     if (!CheckedInt<GLsizeiptr>(size).isValid())
         return mContext->ErrorOutOfMemory("%s: bad size", funcName);
 
     if (!ValidateBufferUsageEnum(mContext, funcName, usage))
         return;
 
-    if (mNumActiveTFOs) {
-        mContext->ErrorInvalidOperation("%s: Buffer is bound to an active transform"
-                                        " feedback object.",
-                                        funcName);
-        return;
-    }
-
     const auto& gl = mContext->gl;
     gl->MakeCurrent();
     const ScopedLazyBind lazyBind(gl, target, this);
     mContext->InvalidateBufferFetching();
 
 #ifdef XP_MACOSX
     // bug 790879
     if (gl->WorkAroundDriverBugs() &&
@@ -215,25 +205,16 @@ bool
 WebGLBuffer::IsElementArrayUsedWithMultipleTypes() const
 {
     return mCache->BeenUsedWithMultipleTypes();
 }
 
 bool
 WebGLBuffer::ValidateCanBindToTarget(const char* funcName, GLenum target)
 {
-    const bool wouldBeTF = (target == LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER);
-    if (mWebGLRefCnt && wouldBeTF != mBoundForTF) {
-        mContext->ErrorInvalidOperation("%s: Buffers cannot be simultaneously bound to "
-                                        " transform feedback and bound elsewhere.",
-                                        funcName);
-        return false;
-    }
-    mBoundForTF = wouldBeTF;
-
     /* https://www.khronos.org/registry/webgl/specs/latest/2.0/#5.1
      *
      * In the WebGL 2 API, buffers have their WebGL buffer type
      * initially set to undefined. Calling bindBuffer, bindBufferRange
      * or bindBufferBase with the target argument set to any buffer
      * binding point except COPY_READ_BUFFER or COPY_WRITE_BUFFER will
      * then set the WebGL buffer type of the buffer being bound
      * according to the table above.
--- a/dom/canvas/WebGLBuffer.h
+++ b/dom/canvas/WebGLBuffer.h
@@ -74,15 +74,13 @@ public:
 
 protected:
     ~WebGLBuffer();
 
     Kind mContent;
     GLenum mUsage;
     size_t mByteLength;
     UniquePtr<WebGLElementArrayCache> mCache;
-    size_t mNumActiveTFOs;
-    bool mBoundForTF;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_BUFFER_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -115,16 +115,17 @@ WebGLContextOptions::WebGLContextOptions
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mBufferFetchingIsVerified(false)
     , mBufferFetchingHasPerVertex(false)
     , mMaxFetchedVertices(0)
     , mMaxFetchedInstances(0)
     , mLayerIsMirror(false)
     , mBypassShaderValidation(false)
+    , mBuffersForUB_Dirty(true)
     , mContextLossHandler(this)
     , mNeedsFakeNoAlpha(false)
     , mNeedsFakeNoDepth(false)
     , mNeedsFakeNoStencil(false)
     , mNeedsEmulatedLoneDepthStencil(false)
 {
     mGeneration = 0;
     mInvalidated = false;
@@ -257,16 +258,17 @@ WebGLContext::DestroyResourcesAndContext
     mBoundTransformFeedback = nullptr;
     mDefaultTransformFeedback = nullptr;
 
     mQuerySlot_SamplesPassed = nullptr;
     mQuerySlot_TFPrimsWritten = nullptr;
     mQuerySlot_TimeElapsed = nullptr;
 
     mIndexedUniformBufferBindings.clear();
+    OnUBIndexedBindingsChanged();
 
     //////
 
     ClearLinkedList(mBuffers);
     ClearLinkedList(mFramebuffers);
     ClearLinkedList(mPrograms);
     ClearLinkedList(mQueries);
     ClearLinkedList(mRenderbuffers);
@@ -2565,16 +2567,52 @@ WebGLContext::ValidateArrayBufferView(co
         elemCount = elemCountOverride;
     }
 
     *out_bytes = bytes + (elemOffset * elemSize);
     *out_byteLen = elemCount * elemSize;
     return true;
 }
 
+////
+
+const decltype(WebGLContext::mBuffersForUB)&
+WebGLContext::BuffersForUB() const
+{
+    if (mBuffersForUB_Dirty) {
+        mBuffersForUB.clear();
+        for (const auto& cur : mIndexedUniformBufferBindings) {
+            if (cur.mBufferBinding) {
+                mBuffersForUB.insert(cur.mBufferBinding.get());
+            }
+        }
+        mBuffersForUB_Dirty = false;
+    }
+    return mBuffersForUB;
+}
+
+////
+
+bool
+WebGLContext::ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer)
+{
+    if (!mBoundTransformFeedback)
+        return true;
+
+    const auto& buffersForTF = mBoundTransformFeedback->BuffersForTF();
+    if (buffersForTF.count(buffer)) {
+        ErrorInvalidOperation("%s: Specified WebGLBuffer is currently bound for transform"
+                              " feedback.",
+                              funcName);
+        return false;
+    }
+
+    return true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags)
 {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -215,50 +215,50 @@ protected:
         memset(this, 0, sizeof(*this));
     }
 };
 
 ////
 
 struct TexImageSourceAdapter final : public TexImageSource
 {
-    TexImageSourceAdapter(const dom::Nullable<dom::ArrayBufferView>& maybeView,
+    TexImageSourceAdapter(const dom::Nullable<dom::ArrayBufferView>* maybeView,
                           ErrorResult*)
     {
-        if (!maybeView.IsNull()) {
-            mView = &(maybeView.Value());
+        if (!maybeView->IsNull()) {
+            mView = &(maybeView->Value());
         }
     }
 
-    TexImageSourceAdapter(const dom::ArrayBufferView& view, ErrorResult*) {
-        mView = &view;
+    TexImageSourceAdapter(const dom::ArrayBufferView* view, ErrorResult*) {
+        mView = view;
     }
 
-    TexImageSourceAdapter(const dom::ArrayBufferView& view, GLuint viewElemOffset,
+    TexImageSourceAdapter(const dom::ArrayBufferView* view, GLuint viewElemOffset,
                           GLuint viewElemLengthOverride = 0)
     {
-        mView = &view;
+        mView = view;
         mViewElemOffset = viewElemOffset;
         mViewElemLengthOverride = viewElemLengthOverride;
     }
 
-    TexImageSourceAdapter(WebGLsizeiptr pboOffset, GLuint ignored1, GLuint ignored2 = 0) {
-        mPboOffset = &pboOffset;
+    TexImageSourceAdapter(const WebGLsizeiptr* pboOffset, GLuint ignored1, GLuint ignored2 = 0) {
+        mPboOffset = pboOffset;
     }
 
-    TexImageSourceAdapter(WebGLsizeiptr pboOffset, ErrorResult* ignored) {
-        mPboOffset = &pboOffset;
+    TexImageSourceAdapter(const WebGLsizeiptr* pboOffset, ErrorResult* ignored) {
+        mPboOffset = pboOffset;
     }
 
-    TexImageSourceAdapter(const dom::ImageData& imageData, ErrorResult*) {
-        mImageData = &imageData;
+    TexImageSourceAdapter(const dom::ImageData* imageData, ErrorResult*) {
+        mImageData = imageData;
     }
 
-    TexImageSourceAdapter(const dom::Element& domElem, ErrorResult* const out_error) {
-        mDomElem = &domElem;
+    TexImageSourceAdapter(const dom::Element* domElem, ErrorResult* const out_error) {
+        mDomElem = domElem;
         mOut_error = out_error;
     }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 
 class WebGLContext
     : public nsIDOMWebGLRenderingContext
@@ -1021,32 +1021,32 @@ public:
     void CompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat,
                               GLsizei width, GLsizei height, GLint border,
                               const T& anySrc, GLuint viewElemOffset = 0,
                               GLuint viewElemLengthOverride = 0)
     {
         const char funcName[] = "compressedTexImage2D";
         const uint8_t funcDims = 2;
         const GLsizei depth = 1;
-        const TexImageSourceAdapter src(anySrc, viewElemOffset, viewElemLengthOverride);
+        const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
         CompressedTexImage(funcName, funcDims, target, level, internalFormat, width,
                            height, depth, border, src);
     }
 
     template<typename T>
     void CompressedTexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                                  GLsizei width, GLsizei height, GLenum unpackFormat,
                                  const T& anySrc, GLuint viewElemOffset = 0,
                                  GLuint viewElemLengthOverride = 0)
     {
         const char funcName[] = "compressedTexSubImage2D";
         const uint8_t funcDims = 2;
         const GLint zOffset = 0;
         const GLsizei depth = 1;
-        const TexImageSourceAdapter src(anySrc, viewElemOffset, viewElemLengthOverride);
+        const TexImageSourceAdapter src(&anySrc, viewElemOffset, viewElemLengthOverride);
         CompressedTexSubImage(funcName, funcDims, target, level, xOffset, yOffset,
                               zOffset, width, height, depth, unpackFormat, src);
     }
 
 protected:
     void CompressedTexImage(const char* funcName, uint8_t funcDims, GLenum target,
                             GLint level, GLenum internalFormat, GLsizei width,
                             GLsizei height, GLsizei depth, GLint border,
@@ -1110,27 +1110,27 @@ public:
 
     ////
 
     template<typename T>
     void TexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLint border, GLenum unpackFormat, GLenum unpackType,
                     const T& anySrc, ErrorResult& out_error)
     {
-        const TexImageSourceAdapter src(anySrc, &out_error);
+        const TexImageSourceAdapter src(&anySrc, &out_error);
         TexImage2D(target, level, internalFormat, width, height, border, unpackFormat,
                    unpackType, src);
     }
 
     void TexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLint border, GLenum unpackFormat, GLenum unpackType,
                     const dom::ArrayBufferView& view, GLuint viewElemOffset,
                     ErrorResult&)
     {
-        const TexImageSourceAdapter src(view, viewElemOffset);
+        const TexImageSourceAdapter src(&view, viewElemOffset);
         TexImage2D(target, level, internalFormat, width, height, border, unpackFormat,
                    unpackType, src);
     }
 
 protected:
     void TexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width,
                     GLsizei height, GLint border, GLenum unpackFormat,
                     GLenum unpackType, const TexImageSource& src)
@@ -1150,27 +1150,27 @@ protected:
     ////
 
 public:
     template<typename T>
     void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLsizei width, GLsizei height, GLenum unpackFormat,
                        GLenum unpackType, const T& anySrc, ErrorResult& out_error)
     {
-        const TexImageSourceAdapter src(anySrc, &out_error);
+        const TexImageSourceAdapter src(&anySrc, &out_error);
         TexSubImage2D(target, level, xOffset, yOffset, width, height, unpackFormat,
                       unpackType, src);
     }
 
     void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLsizei width, GLsizei height, GLenum unpackFormat,
                        GLenum unpackType, const dom::ArrayBufferView& view,
                        GLuint viewElemOffset, ErrorResult&)
     {
-        const TexImageSourceAdapter src(view, viewElemOffset);
+        const TexImageSourceAdapter src(&view, viewElemOffset);
         TexSubImage2D(target, level, xOffset, yOffset, width, height, unpackFormat,
                       unpackType, src);
     }
 
 protected:
     void TexSubImage2D(GLenum target, GLint level, GLint xOffset, GLint yOffset,
                        GLsizei width, GLsizei height, GLenum unpackFormat,
                        GLenum unpackType, const TexImageSource& src)
@@ -1575,32 +1575,36 @@ protected:
                               uint32_t* const out_width, uint32_t* const out_height);
 
     bool HasDrawBuffers() const {
         return IsWebGL2() ||
                IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
     }
 
     WebGLRefPtr<WebGLBuffer>* ValidateBufferSlot(const char* funcName, GLenum target);
+public:
     WebGLBuffer* ValidateBufferSelection(const char* funcName, GLenum target);
+protected:
     IndexedBufferBinding* ValidateIndexedBufferSlot(const char* funcName, GLenum target,
                                                     GLuint index);
 
     bool ValidateIndexedBufferBinding(const char* funcName, GLenum target, GLuint index,
                                       WebGLRefPtr<WebGLBuffer>** const out_genericBinding,
                                       IndexedBufferBinding** const out_indexedBinding);
 
     bool ValidateNonNegative(const char* funcName, const char* argName, int64_t val) {
         if (MOZ_UNLIKELY(val < 0)) {
             ErrorInvalidValue("%s: `%s` must be non-negative.", funcName, argName);
             return false;
         }
         return true;
     }
 
+    bool ValidateForNonTransformFeedback(const char* funcName, WebGLBuffer* buffer);
+
 public:
     template<typename T>
     bool ValidateNonNull(const char* funcName, const dom::Nullable<T>& maybe) {
         if (maybe.IsNull()) {
             ErrorInvalidValue("%s: `null` is invalid.", funcName);
             return false;
         }
         return true;
@@ -1749,16 +1753,27 @@ protected:
     UniquePtr<FakeBlackTexture> mFakeBlack_3D_0001;
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0000;
     UniquePtr<FakeBlackTexture> mFakeBlack_2D_Array_0001;
 
     bool BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack);
 
     ////////////////////////////////////
 
+private:
+    mutable bool mBuffersForUB_Dirty;
+    mutable std::set<const WebGLBuffer*> mBuffersForUB;
+
+public:
+    void OnUBIndexedBindingsChanged() const { mBuffersForUB_Dirty = true; }
+    const decltype(mBuffersForUB)& BuffersForUB() const;
+
+    ////////////////////////////////////
+
+protected:
     // Generic Vertex Attributes
     UniquePtr<GLenum[]> mVertexAttribType;
     GLfloat mVertexAttrib0Vector[4];
     GLfloat mFakeVertexAttrib0BufferObjectVector[4];
     size_t mFakeVertexAttrib0BufferObjectSize;
     GLuint mFakeVertexAttrib0BufferObject;
     WebGLVertexAttrib0Status mFakeVertexAttrib0BufferStatus;
 
--- a/dom/canvas/WebGLContextBuffers.cpp
+++ b/dom/canvas/WebGLContextBuffers.cpp
@@ -70,16 +70,19 @@ WebGLContext::ValidateBufferSelection(co
         return nullptr;
     const auto& buffer = *slot;
 
     if (!buffer) {
         ErrorInvalidOperation("%s: Buffer for `target` is null.", funcName);
         return nullptr;
     }
 
+    if (!ValidateForNonTransformFeedback(funcName, buffer.get()))
+        return nullptr;
+
     return buffer.get();
 }
 
 IndexedBufferBinding*
 WebGLContext::ValidateIndexedBufferSlot(const char* funcName, GLenum target, GLuint index)
 {
     decltype(mIndexedUniformBufferBindings)* bindings;
     const char* maxIndexEnum;
@@ -207,16 +210,25 @@ WebGLContext::BindBufferBase(GLenum targ
     *genericBinding = buffer;
     indexedBinding->mBufferBinding = buffer;
     indexedBinding->mRangeStart = 0;
     indexedBinding->mRangeSize = 0;
 
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
+
+    switch (target) {
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+        mBoundTransformFeedback->OnIndexedBindingsChanged();
+        break;
+    case LOCAL_GL_UNIFORM:
+        OnUBIndexedBindingsChanged();
+        break;
+    }
 }
 
 void
 WebGLContext::BindBufferRange(GLenum target, GLuint index, WebGLBuffer* buffer,
                               WebGLintptr offset, WebGLsizeiptr size)
 {
     const char funcName[] = "bindBufferRange";
     if (IsContextLost())
@@ -291,16 +303,25 @@ WebGLContext::BindBufferRange(GLenum tar
     *genericBinding = buffer;
     indexedBinding->mBufferBinding = buffer;
     indexedBinding->mRangeStart = offset;
     indexedBinding->mRangeSize = size;
 
     if (buffer) {
         buffer->SetContentAfterBind(target);
     }
+
+    switch (target) {
+    case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER:
+        mBoundTransformFeedback->OnIndexedBindingsChanged();
+        break;
+    case LOCAL_GL_UNIFORM:
+        OnUBIndexedBindingsChanged();
+        break;
+    }
 }
 
 ////////////////////////////////////////
 
 void
 WebGLContext::BufferDataImpl(GLenum target, size_t dataLen, const uint8_t* data,
                              GLenum usage)
 {
@@ -378,23 +399,16 @@ WebGLContext::BufferSubDataImpl(GLenum t
 
     if (!ValidateNonNegative(funcName, "byteOffset", dstByteOffset))
         return;
 
     const auto& buffer = ValidateBufferSelection(funcName, target);
     if (!buffer)
         return;
 
-    if (buffer->mNumActiveTFOs) {
-        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                              " object.",
-                              "bufferSubData");
-        return;
-    }
-
     if (!buffer->ValidateRange(funcName, dstByteOffset, dataLen))
         return;
 
     if (!CheckedInt<GLintptr>(dataLen).isValid()) {
         ErrorOutOfMemory("%s: Size too large.", funcName);
         return;
     }
     const GLintptr glDataLen(dataLen);
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -14,16 +14,18 @@
 #include "WebGLFramebuffer.h"
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
+#include <algorithm>
+
 namespace mozilla {
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
 ////////////////////////////////////////
 
 class ScopedResolveTexturesForDraw
@@ -295,16 +297,26 @@ WebGLContext::DrawArrays_check(const cha
         return false;
     }
 
     return true;
 }
 
 ////////////////////////////////////////
 
+template<typename T>
+static bool
+DoSetsIntersect(const std::set<T>& a, const std::set<T>& b)
+{
+    std::vector<T> intersection;
+    std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
+                          std::back_inserter(intersection));
+    return bool(intersection.size());
+}
+
 class ScopedDrawHelper final
 {
     WebGLContext* const mWebGL;
     bool mDidFake;
 
 public:
     ScopedDrawHelper(WebGLContext* webgl, const char* funcName, uint32_t firstVertex,
                      uint32_t vertCount, uint32_t instanceCount, bool* const out_error)
@@ -338,17 +350,17 @@ public:
             *out_error = true;
             return;
         }
         mDidFake = true;
 
         ////
         // Check UBO sizes.
 
-        const auto& linkInfo = webgl->mActiveProgramLinkInfo;
+        const auto& linkInfo = mWebGL->mActiveProgramLinkInfo;
         for (const auto& cur : linkInfo->uniformBlocks) {
             const auto& dataSize = cur->mDataSize;
             const auto& binding = cur->mBinding;
             if (!binding) {
                 mWebGL->ErrorInvalidOperation("%s: Buffer for uniform block is null.",
                                               funcName);
                 *out_error = true;
                 return;
@@ -361,16 +373,32 @@ public:
                                               funcName);
                 *out_error = true;
                 return;
             }
         }
 
         ////
 
+        const auto& tfo = mWebGL->mBoundTransformFeedback;
+        if (tfo) {
+            const auto& buffersForTF = tfo->BuffersForTF();
+            const auto& buffersForUB = mWebGL->BuffersForUB();
+            if (DoSetsIntersect(buffersForTF, buffersForUB)) {
+                mWebGL->ErrorInvalidOperation("%s: At least one WebGLBuffer is bound for"
+                                              " both transform feedback and as a uniform"
+                                              " buffer.",
+                                              funcName);
+                *out_error = true;
+                return;
+            }
+        }
+
+        ////
+
         mWebGL->RunContextLossTimer();
     }
 
     ~ScopedDrawHelper() {
         if (mDidFake) {
             mWebGL->UndoFakeVertexAttrib0();
         }
     }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1457,60 +1457,52 @@ WebGLContext::ReadPixels(GLint x, GLint 
 
     ReadPixelsImpl(x, y, width, height, format, type, bytes, byteLen);
 }
 
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format,
                          GLenum type, WebGLsizeiptr offset, ErrorResult& out_error)
 {
+    const char funcName[] = "readPixels";
     if (!ReadPixels_SharedPrecheck(&out_error))
         return;
 
-    if (!mBoundPixelPackBuffer) {
-        ErrorInvalidOperation("readPixels: PIXEL_PACK_BUFFER must not be null.");
+    const auto& buffer = ValidateBufferSelection(funcName, LOCAL_GL_PIXEL_PACK_BUFFER);
+    if (!buffer)
         return;
-    }
-
-    if (mBoundPixelPackBuffer->mNumActiveTFOs) {
-        ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                              " object.",
-                              "readPixels");
-        return;
-    }
 
     //////
 
-    if (offset < 0) {
-        ErrorInvalidValue("readPixels: offset must not be negative.");
+    if (!ValidateNonNegative(funcName, "offset", offset))
         return;
-    }
 
     {
         const auto bytesPerType = webgl::BytesPerPixel({LOCAL_GL_RED, type});
 
         if (offset % bytesPerType != 0) {
-            ErrorInvalidOperation("readPixels: `offset` must be divisible by the size"
-                                  " a `type` in bytes.");
+            ErrorInvalidOperation("%s: `offset` must be divisible by the size of `type`"
+                                  " in bytes.",
+                                  funcName);
             return;
         }
     }
 
     //////
 
-    const auto bytesAvailable = mBoundPixelPackBuffer->ByteLength();
+    const auto bytesAvailable = buffer->ByteLength();
     const auto checkedBytesAfterOffset = CheckedUint32(bytesAvailable) - offset;
 
     uint32_t bytesAfterOffset = 0;
     if (checkedBytesAfterOffset.isValid()) {
         bytesAfterOffset = checkedBytesAfterOffset.value();
     }
 
     gl->MakeCurrent();
-    const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, mBoundPixelPackBuffer);
+    const ScopedLazyBind lazyBind(gl, LOCAL_GL_PIXEL_PACK_BUFFER, buffer);
 
     ReadPixelsImpl(x, y, width, height, format, type, (void*)offset, bytesAfterOffset);
 }
 
 static bool
 ValidateReadPixelsFormatAndType(const webgl::FormatInfo* srcFormat,
                                 const webgl::PackingInfo& pi, gl::GLContext* gl,
                                 WebGLContext* webgl)
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -1439,17 +1439,17 @@ WebGLProgram::TransformFeedbackVaryings(
     case LOCAL_GL_INTERLEAVED_ATTRIBS:
         break;
 
     case LOCAL_GL_SEPARATE_ATTRIBS:
         {
             GLuint maxAttribs = 0;
             gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS,
                              &maxAttribs);
-            if (varyings.Length() >= maxAttribs) {
+            if (varyings.Length() > maxAttribs) {
                 mContext->ErrorInvalidValue("%s: Length of `varyings` exceeds %s.",
                                             funcName,
                                             "TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS");
                 return;
             }
         }
         break;
 
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -180,24 +180,29 @@ FromView(WebGLContext* webgl, const char
         }
     }
     return MakeUnique<webgl::TexUnpackBytes>(webgl, target, width, height, depth,
                                              isClientData, bytes, availByteCount);
 }
 
 static UniquePtr<webgl::TexUnpackBytes>
 FromPboOffset(WebGLContext* webgl, const char* funcName, TexImageTarget target,
-              uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset,
-              size_t availBufferBytes)
+              uint32_t width, uint32_t height, uint32_t depth, WebGLsizeiptr pboOffset)
 {
     if (pboOffset < 0) {
         webgl->ErrorInvalidValue("%s: offset cannot be negative.", funcName);
         return nullptr;
     }
 
+    const auto& buffer = webgl->ValidateBufferSelection(funcName,
+                                                        LOCAL_GL_PIXEL_UNPACK_BUFFER);
+    if (!buffer)
+        return nullptr;
+
+    size_t availBufferBytes = buffer->ByteLength();
     if (size_t(pboOffset) > availBufferBytes) {
         webgl->ErrorInvalidOperation("%s: Offset is passed end of buffer.", funcName);
         return nullptr;
     }
     availBufferBytes -= pboOffset;
 
     const bool isClientData = false;
     const auto ptr = (const uint8_t*)pboOffset;
@@ -361,35 +366,22 @@ WebGLContext::From(const char* funcName,
     uint32_t width, height, depth;
     if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
                          &height, &depth))
     {
         return nullptr;
     }
 
     if (src.mPboOffset) {
-        if (!mBoundPixelUnpackBuffer) {
-            ErrorInvalidOperation("%s: PACK_BUFFER must be non-null.", funcName);
-            return nullptr;
-        }
-
-        if (mBoundPixelUnpackBuffer->mNumActiveTFOs) {
-            ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                                  " object.",
-                                  funcName);
-            return nullptr;
-        }
-
-        const auto& availBytes = mBoundPixelUnpackBuffer->ByteLength();
         return FromPboOffset(this, funcName, target, width, height, depth,
-                             *(src.mPboOffset), availBytes);
+                             *(src.mPboOffset));
     }
 
     if (mBoundPixelUnpackBuffer) {
-        ErrorInvalidOperation("%s: PACK_BUFFER must be null.", funcName);
+        ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
         return nullptr;
     }
 
     if (src.mImageData) {
         return FromImageData(this, funcName, target, width, height, depth,
                              *(src.mImageData), scopedArr);
     }
 
@@ -1365,35 +1357,22 @@ WebGLContext::FromCompressed(const char*
     uint32_t width, height, depth;
     if (!ValidateExtents(this, funcName, rawWidth, rawHeight, rawDepth, border, &width,
                          &height, &depth))
     {
         return nullptr;
     }
 
     if (src.mPboOffset) {
-        if (!mBoundPixelUnpackBuffer) {
-            ErrorInvalidOperation("%s: PACK_BUFFER must be non-null.", funcName);
-            return nullptr;
-        }
-
-        if (mBoundPixelUnpackBuffer->mNumActiveTFOs) {
-            ErrorInvalidOperation("%s: Buffer is bound to an active transform feedback"
-                                  " object.",
-                                  funcName);
-            return nullptr;
-        }
-
-        const auto& availBytes = mBoundPixelUnpackBuffer->ByteLength();
         return FromPboOffset(this, funcName, target, width, height, depth,
-                             *(src.mPboOffset), availBytes);
+                             *(src.mPboOffset));
     }
 
     if (mBoundPixelUnpackBuffer) {
-        ErrorInvalidOperation("%s: PACK_BUFFER must be null.", funcName);
+        ErrorInvalidOperation("%s: PIXEL_UNPACK_BUFFER must be null.", funcName);
         return nullptr;
     }
 
     return FromView(this, funcName, target, width, height, depth, src.mView,
                     src.mViewElemOffset, src.mViewElemLengthOverride);
 }
 
 void
--- a/dom/canvas/WebGLTransformFeedback.cpp
+++ b/dom/canvas/WebGLTransformFeedback.cpp
@@ -12,16 +12,17 @@
 namespace mozilla {
 
 WebGLTransformFeedback::WebGLTransformFeedback(WebGLContext* webgl, GLuint tf)
     : WebGLContextBoundObject(webgl)
     , mGLName(tf)
     , mIndexedBindings(webgl->mGLMaxTransformFeedbackSeparateAttribs)
     , mIsPaused(false)
     , mIsActive(false)
+    , mBuffersForTF_Dirty(true)
 {
     mContext->mTransformFeedbacks.insertBack(this);
 }
 
 WebGLTransformFeedback::~WebGLTransformFeedback()
 {
     DeleteOnce();
 }
@@ -31,16 +32,38 @@ WebGLTransformFeedback::Delete()
 {
     if (mGLName) {
         mContext->MakeContextCurrent();
         mContext->gl->fDeleteTransformFeedbacks(1, &mGLName);
     }
     removeFrom(mContext->mTransformFeedbacks);
 }
 
+////
+
+const decltype(WebGLTransformFeedback::mBuffersForTF)&
+WebGLTransformFeedback::BuffersForTF() const
+{
+    // The generic bind point cannot incur undefined read/writes because otherwise it
+    // would be impossible to read back from this. The spec implies that readback from
+    // the TRANSFORM_FEEDBACK target is possible, just not simultaneously with being
+    // "bound or in use for transform feedback".
+    // Therefore, only the indexed bindings of the TFO count.
+    if (mBuffersForTF_Dirty) {
+        mBuffersForTF.clear();
+        for (const auto& cur : mIndexedBindings) {
+            if (cur.mBufferBinding) {
+                mBuffersForTF.insert(cur.mBufferBinding.get());
+            }
+        }
+        mBuffersForTF_Dirty = false;
+    }
+    return mBuffersForTF;
+}
+
 ////////////////////////////////////////
 
 void
 WebGLTransformFeedback::BeginTransformFeedback(GLenum primMode)
 {
     const char funcName[] = "beginTransformFeedback";
 
     if (mIsActive)
@@ -102,23 +125,16 @@ WebGLTransformFeedback::BeginTransformFe
 
     mActive_Program = prog;
     mActive_PrimMode = primMode;
     mActive_VertPosition = 0;
     mActive_VertCapacity = minVertCapacity;
 
     ////
 
-    for (const auto& cur : mIndexedBindings) {
-        const auto& buffer = cur.mBufferBinding;
-        if (buffer) {
-            buffer->mNumActiveTFOs++;
-        }
-    }
-
     mActive_Program->mNumActiveTFOs++;
 }
 
 
 void
 WebGLTransformFeedback::EndTransformFeedback()
 {
     const char funcName[] = "endTransformFeedback";
@@ -134,23 +150,16 @@ WebGLTransformFeedback::EndTransformFeed
 
     ////
 
     mIsActive = false;
     mIsPaused = false;
 
     ////
 
-    for (const auto& cur : mIndexedBindings) {
-        const auto& buffer = cur.mBufferBinding;
-        if (buffer) {
-            buffer->mNumActiveTFOs--;
-        }
-    }
-
     mActive_Program->mNumActiveTFOs--;
 }
 
 void
 WebGLTransformFeedback::PauseTransformFeedback()
 {
     const char funcName[] = "pauseTransformFeedback";
 
--- a/dom/canvas/WebGLTransformFeedback.h
+++ b/dom/canvas/WebGLTransformFeedback.h
@@ -32,29 +32,37 @@ private:
     bool mIsPaused;
     bool mIsActive;
     // Not in state tables:
     WebGLRefPtr<WebGLProgram> mActive_Program;
     MOZ_INIT_OUTSIDE_CTOR GLenum mActive_PrimMode;
     MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertPosition;
     MOZ_INIT_OUTSIDE_CTOR size_t mActive_VertCapacity;
 
+    mutable bool mBuffersForTF_Dirty;
+    mutable std::set<const WebGLBuffer*> mBuffersForTF;
+
 public:
     WebGLTransformFeedback(WebGLContext* webgl, GLuint tf);
 private:
     ~WebGLTransformFeedback();
 
 public:
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTransformFeedback)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTransformFeedback)
 
     void Delete();
     WebGLContext* GetParentObject() const { return mContext; }
     virtual JSObject* WrapObject(JSContext*, JS::Handle<JSObject*>) override;
 
+    ////
+
+    void OnIndexedBindingsChanged() const { mBuffersForTF_Dirty = true; }
+    const decltype(mBuffersForTF)& BuffersForTF() const;
+
     // GL Funcs
     void BeginTransformFeedback(GLenum primMode);
     void EndTransformFeedback();
     void PauseTransformFeedback();
     void ResumeTransformFeedback();
 };
 
 } // namespace mozilla
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/bound-buffer-size-change-test.html
@@ -56,16 +56,18 @@ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_
 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
     "Calling bindBufferBase on a buffer where no storage is allocated should succeed.");
 shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer1");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "0");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "0");
 
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
 debug("");
 debug("bindBufferBase with UNIFORM_BUFFER target");
@@ -92,21 +94,23 @@ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK
 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
     "Calling bindBufferRange on a buffer where no storage is allocated should succeed.");
 shouldBe("gl.getParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 4, gl.STATIC_DRAW);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 gl.bufferData(gl.TRANSFORM_FEEDBACK_BUFFER, 12, gl.STATIC_DRAW);
+wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buffer3");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_SIZE, 0)", "8");
 shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_START, 0)", "4");
 
 wtu.glErrorShouldBe(gl, gl.NO_ERROR);
 
 debug("");
 debug("bindBufferRange with UNIFORM_BUFFER target");
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-type-restrictions.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/buffers/buffer-type-restrictions.html
@@ -83,26 +83,27 @@ var testBindingFn = function(firstBindFn
   bind(secondBindFn, secondTarget, null);
 
   var messagePrefix = "Binding buffer first with " + firstBindFn + " to gl." + firstTargetStr
     + " and then binding buffer with " + secondBindFn + " to gl." + secondTargetStr + " should ";
   if (firstTarget == secondTarget || noElementArrayVsOtherDataConflicts(firstTarget, secondTarget))
     wtu.glErrorShouldBe(gl, gl.NO_ERROR, messagePrefix + "WORK");
   else
     wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, messagePrefix + "FAIL");
-
+  /*
   if ((firstTarget == gl.TRANSFORM_FEEDBACK_BUFFER && secondTarget != gl.TRANSFORM_FEEDBACK_BUFFER) ||
       (firstTarget != gl.TRANSFORM_FEEDBACK_BUFFER && secondTarget == gl.TRANSFORM_FEEDBACK_BUFFER)) {
     bind(firstBindFn, firstTarget, buffer);
     bind(secondBindFn, secondTarget, buffer);
 
     var message = "Binding buffer first with " + firstBindFn + " to gl." + firstTargetStr
     + " and simultaneously binding buffer with " + secondBindFn + " to gl." + secondTargetStr + " should FAIL";
     wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, message);
   }
+  */
 }
 
 var testBinding = function(firstTarget, secondTarget) {
   for (var i = 0; i < bindFunctions.length; i++)
     if (i == 0 || firstTarget == gl.UNIFORM_BUFFER || firstTarget == gl.TRANSFORM_FEEDBACK_BUFFER)
       for (var j = 0; j < bindFunctions.length; j++)
         if (j == 0 || secondTarget == gl.UNIFORM_BUFFER || secondTarget == gl.TRANSFORM_FEEDBACK_BUFFER)
           testBindingFn(bindFunctions[i], bindFunctions[j], firstTarget, secondTarget);
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -139,16 +139,22 @@ public:
   SetContentFilters(const nsAString& aFilters);
 
   FileSystemBase*
   GetFileSystem(ErrorResult& aRv);
 
   bool
   ClonableToDifferentThreadOrProcess() const;
 
+  nsIFile*
+  GetInternalNsIFile() const
+  {
+    return mFile;
+  }
+
 private:
   Directory(nsISupports* aParent,
             nsIFile* aFile,
             FileSystemBase* aFileSystem = nullptr);
   ~Directory();
 
   /*
    * Convert relative DOM path to the absolute real path.
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -824,17 +824,17 @@ HTMLFormElement::SubmitSubmission(HTMLFo
     nsCOMPtr<nsIInputStream> postDataStream;
     rv = aFormSubmission->GetEncodedSubmission(actionURI,
                                                getter_AddRefs(postDataStream));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
 
     rv = linkHandler->OnLinkClickSync(this, actionURI,
                                       target.get(),
                                       NullString(),
-                                      postDataStream, nullptr,
+                                      postDataStream, nullptr, false,
                                       getter_AddRefs(docShell),
                                       getter_AddRefs(mSubmittingRequest));
     NS_ENSURE_SUBMIT_SUCCESS(rv);
   }
 
   // Even if the submit succeeds, it's possible for there to be no docshell
   // or request; for example, if it's to a named anchor within the same page
   // the submit will not really do anything.
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -480,38 +480,47 @@ namespace {
 
 /**
  * This may return nullptr if the DOM File's implementation of
  * File::mozFullPathInternal does not successfully return a non-empty
  * string that is a valid path. This can happen on Firefox OS, for example,
  * where the file picker can create Blobs.
  */
 static already_AddRefed<nsIFile>
-DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData)
-{
-  nsString path;
-
+LastUsedDirectory(const OwningFileOrDirectory& aData)
+{
   if (aData.IsFile()) {
-    ErrorResult rv;
-    aData.GetAsFile()->GetMozFullPathInternal(path, rv);
-    if (rv.Failed() || path.IsEmpty()) {
-      rv.SuppressException();
+    nsAutoString path;
+    ErrorResult error;
+    aData.GetAsFile()->GetMozFullPathInternal(path, error);
+    if (error.Failed() || path.IsEmpty()) {
+      error.SuppressException();
+      return nullptr;
+    }
+
+    nsCOMPtr<nsIFile> localFile;
+    nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
+                                        getter_AddRefs(localFile));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
       return nullptr;
     }
-  } else {
-    MOZ_ASSERT(aData.IsDirectory());
-    aData.GetAsDirectory()->GetFullRealPath(path);
-  }
-
-  nsCOMPtr<nsIFile> localFile;
-  nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
-                                      getter_AddRefs(localFile));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
+
+    nsCOMPtr<nsIFile> parentFile;
+    rv = localFile->GetParent(getter_AddRefs(parentFile));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    return parentFile.forget();
+  }
+
+  MOZ_ASSERT(aData.IsDirectory());
+
+  nsCOMPtr<nsIFile> localFile = aData.GetAsDirectory()->GetInternalNsIFile();
+  MOZ_ASSERT(localFile);
 
   return localFile.forget();
 }
 
 void
 GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
                           nsAString& aName)
 {
@@ -611,22 +620,19 @@ HTMLInputElement::nsFilePickerShownCallb
     }
   }
 
   if (newFilesOrDirectories.IsEmpty()) {
     return NS_OK;
   }
 
   // Store the last used directory using the content pref service:
-  nsCOMPtr<nsIFile> file =
-    DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]);
-
-  if (file) {
-    nsCOMPtr<nsIFile> lastUsedDir;
-    file->GetParent(getter_AddRefs(lastUsedDir));
+  nsCOMPtr<nsIFile> lastUsedDir = LastUsedDirectory(newFilesOrDirectories[0]);
+
+  if (lastUsedDir) {
     HTMLInputElement::gUploadLastDir->StoreLastUsedDirectory(
       mInput->OwnerDoc(), lastUsedDir);
   }
 
   // The text control frame (if there is one) isn't going to send a change
   // event because it will think this is done by a script.
   // So, we can safely send one by ourself.
   mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
@@ -989,36 +995,32 @@ HTMLInputElement::InitFilePicker(FilePic
   // cycles adding them for FILE_PICKER_DIRECTORY.
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::accept) &&
       aType != FILE_PICKER_DIRECTORY) {
     SetFilePickerFiltersFromAccept(filePicker);
   } else {
     filePicker->AppendFilters(nsIFilePicker::filterAll);
   }
 
-  // Set default directry and filename
+  // Set default directory and filename
   nsAutoString defaultName;
 
   const nsTArray<OwningFileOrDirectory>& oldFiles =
     GetFilesOrDirectoriesInternal();
 
   nsCOMPtr<nsIFilePickerShownCallback> callback =
     new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
 
   if (!oldFiles.IsEmpty() &&
       aType != FILE_PICKER_DIRECTORY) {
-    nsString path;
-
-    nsCOMPtr<nsIFile> localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]);
-    if (localFile) {
-      nsCOMPtr<nsIFile> parentFile;
-      nsresult rv = localFile->GetParent(getter_AddRefs(parentFile));
-      if (NS_SUCCEEDED(rv)) {
-        filePicker->SetDisplayDirectory(parentFile);
-      }
+    nsAutoString path;
+
+    nsCOMPtr<nsIFile> parentFile = LastUsedDirectory(oldFiles[0]);
+    if (parentFile) {
+      filePicker->SetDisplayDirectory(parentFile);
     }
 
     // Unfortunately nsIFilePicker doesn't allow multiple files to be
     // default-selected, so only select something by default if exactly
     // one file was selected before.
     if (oldFiles.Length() == 1) {
       nsAutoString leafName;
       GetDOMFileOrDirectoryName(oldFiles[0], leafName);
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -595,20 +595,16 @@ GMPCapability::Supports(const nsTArray<G
         // file, but uses Windows Media Foundation to decode. That's not present
         // on Windows XP, and on some Vista, Windows N, and KN variants without
         // certain services packs.
         if (tag.Equals(kEMEKeySystemClearkey)) {
           if (capabilities.mAPIName.EqualsLiteral(GMP_API_VIDEO_DECODER)) {
             if (!WMFDecoderModule::HasH264()) {
               continue;
             }
-          } else if (capabilities.mAPIName.EqualsLiteral(GMP_API_AUDIO_DECODER)) {
-            if (!WMFDecoderModule::HasAAC()) {
-              continue;
-            }
           }
         }
 #endif
         return true;
       }
     }
   }
   return false;
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -316,16 +316,22 @@ nsSMILAnimationController::DoSample(bool
   }
   if (mRunningSample) {
     NS_ERROR("Shouldn't be recursively sampling");
     return;
   }
 
   bool isStyleFlushNeeded = mResampleNeeded;
   mResampleNeeded = false;
+
+  if (mDocument->IsStyledByServo()) {
+    NS_ERROR("stylo: SMIL animations not supported yet");
+    return;
+  }
+
   // Set running sample flag -- do this before flushing styles so that when we
   // flush styles we don't end up requesting extra samples
   AutoRestore<bool> autoRestoreRunningSample(mRunningSample);
   mRunningSample = true;
 
   // STEP 1: Bring model up to date
   // (i)  Rewind elements where necessary
   // (ii) Run milestone samples
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -7,16 +7,17 @@
 #define _MOZILLA_GFX_2D_H
 
 #include "Types.h"
 #include "Point.h"
 #include "Rect.h"
 #include "Matrix.h"
 #include "Quaternion.h"
 #include "UserData.h"
+#include <vector>
 
 // GenericRefCountedBase allows us to hold on to refcounted objects of any type
 // (contrary to RefCounted<T> which requires knowing the type T) and, in particular,
 // without having a dependency on that type. This is used for DrawTargetSkia
 // to be able to hold on to a GLContext.
 #include "mozilla/GenericRefCounted.h"
 
 // This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
@@ -625,16 +626,20 @@ public:
 };
 
 struct Glyph
 {
   uint32_t mIndex;
   Point mPosition;
 };
 
+static inline bool operator==(const Glyph& aOne, const Glyph& aOther) {
+  return aOne.mIndex == aOther.mIndex && aOne.mPosition == aOther.mPosition;
+}
+
 /** This class functions as a glyph buffer that can be drawn to a DrawTarget.
  * @todo XXX - This should probably contain the guts of gfxTextRun in the future as
  * roc suggested. But for now it's a simple container for a glyph vector.
  */
 struct GlyphBuffer
 {
   const Glyph *mGlyphs; //!< A pointer to a buffer of glyphs. Managed by the caller.
   uint32_t mNumGlyphs;  //!< Number of glyphs mGlyphs points to.
@@ -659,17 +664,17 @@ struct GlyphMetrics
   // Height of the glyph's black box.
   Float mHeight;
 };
 
 /** This class is an abstraction of a backend/platform specific font object
  * at a particular size. It is passed into text drawing calls to describe
  * the font used for the drawing call.
  */
-class ScaledFont : public RefCounted<ScaledFont>
+class ScaledFont : public external::AtomicRefCounted<ScaledFont>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)
   virtual ~ScaledFont() {}
 
   typedef void (*FontFileDataOutput)(const uint8_t *aData, uint32_t aLength, uint32_t aIndex, Float aGlyphSize, void *aBaton);
   typedef void (*FontDescriptorOutput)(const uint8_t *aData, uint32_t aLength, Float aFontSize, void *aBaton);
 
@@ -773,16 +778,17 @@ public:
   virtual ~DrawTarget() {}
 
   virtual bool IsValid() const { return true; };
   virtual DrawTargetType GetType() const = 0;
 
   virtual BackendType GetBackendType() const = 0;
 
   virtual bool IsRecording() const { return false; }
+  virtual bool IsCaptureDT() const { return false; }
 
   /**
    * Returns a SourceSurface which is a snapshot of the current contents of the DrawTarget.
    * Multiple calls to Snapshot() without any drawing operations in between will
    * normally return the same SourceSurface object.
    */
   virtual already_AddRefed<SourceSurface> Snapshot() = 0;
   virtual IntSize GetSize() = 0;
@@ -1267,16 +1273,27 @@ protected:
   bool mTransformDirty : 1;
   bool mPermitSubpixelAA : 1;
 
   SurfaceFormat mFormat;
 };
 
 class DrawTargetCapture : public DrawTarget
 {
+public:
+  virtual bool IsCaptureDT() const { return true; }
+
+  /**
+   * Returns true if the recording only contains FillGlyph calls with
+   * a single font and color. Returns the list of Glyphs along with
+   * the font and color as outparams if so.
+   */
+  virtual bool ContainsOnlyColoredGlyphs(RefPtr<ScaledFont>& aScaledFont,
+                                         Color& aColor,
+                                         std::vector<Glyph>& aGlyphs) = 0;
 };
 
 class DrawEventRecorder : public RefCounted<DrawEventRecorder>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
   virtual ~DrawEventRecorder() { }
 };
--- a/gfx/2d/DrawCommand.h
+++ b/gfx/2d/DrawCommand.h
@@ -41,24 +41,24 @@ class DrawingCommand
 {
 public:
   virtual ~DrawingCommand() {}
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aTransform = nullptr) const = 0;
 
   virtual bool GetAffectedRect(Rect& aDeviceRect, const Matrix& aTransform) const { return false; }
 
+  CommandType GetType() { return mType; }
+
 protected:
   explicit DrawingCommand(CommandType aType)
     : mType(aType)
   {
   }
 
-  CommandType GetType() { return mType; }
-
 private:
   CommandType mType;
 };
 
 class StoredPattern
 {
 public:
   explicit StoredPattern(const Pattern& aPattern)
@@ -413,16 +413,17 @@ private:
   StoredPattern mPattern;
   StrokeOptions mStrokeOptions;
   DrawOptions mOptions;
   std::vector<Float> mDashes;
 };
 
 class FillGlyphsCommand : public DrawingCommand
 {
+  friend class DrawTargetCaptureImpl;
 public:
   FillGlyphsCommand(ScaledFont* aFont,
                     const GlyphBuffer& aBuffer,
                     const Pattern& aPattern,
                     const DrawOptions& aOptions,
                     const GlyphRenderingOptions* aRenderingOptions)
     : DrawingCommand(CommandType::FILLGLYPHS)
     , mFont(aFont)
@@ -548,16 +549,17 @@ public:
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix*) const
   {
     aDT->PopClip();
   }
 };
 
 class SetTransformCommand : public DrawingCommand
 {
+  friend class DrawTargetCaptureImpl;
 public:
   explicit SetTransformCommand(const Matrix& aTransform)
     : DrawingCommand(CommandType::SETTRANSFORM)
     , mTransform(aTransform)
   {
   }
 
   virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix* aMatrix) const
--- a/gfx/2d/DrawTargetCapture.cpp
+++ b/gfx/2d/DrawTargetCapture.cpp
@@ -192,10 +192,68 @@ DrawTargetCaptureImpl::ReplayToDrawTarge
   uint8_t* current = start;
 
   while (current < start + mDrawCommandStorage.size()) {
     reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t))->ExecuteOnDT(aDT, &aTransform);
     current += *(uint32_t*)current;
   }
 }
 
+bool
+DrawTargetCaptureImpl::ContainsOnlyColoredGlyphs(RefPtr<ScaledFont>& aScaledFont,
+                                                 Color& aColor,
+                                                 std::vector<Glyph>& aGlyphs)
+{
+  uint8_t* start = &mDrawCommandStorage.front();
+  uint8_t* current = start;
+
+  while (current < start + mDrawCommandStorage.size()) {
+    DrawingCommand* command =
+      reinterpret_cast<DrawingCommand*>(current + sizeof(uint32_t));
+    current += *(uint32_t*)current;
+
+    if (command->GetType() != CommandType::FILLGLYPHS &&
+        command->GetType() != CommandType::SETTRANSFORM) {
+      return false;
+    }
+
+    if (command->GetType() == CommandType::SETTRANSFORM) {
+      SetTransformCommand* transform = static_cast<SetTransformCommand*>(command);
+      if (transform->mTransform != Matrix()) {
+        return false;
+      }
+      continue;
+    }
+
+    FillGlyphsCommand* fillGlyphs = static_cast<FillGlyphsCommand*>(command);
+    if (aScaledFont && fillGlyphs->mFont != aScaledFont) {
+      return false;
+    }
+    aScaledFont = fillGlyphs->mFont;
+
+    Pattern& pat = fillGlyphs->mPattern;
+
+    if (pat.GetType() != PatternType::COLOR) {
+      return false;
+    }
+
+    ColorPattern* colorPat = static_cast<ColorPattern*>(&pat);
+    if (aColor != Color() && colorPat->mColor != aColor) {
+      return false;
+    }
+    aColor = colorPat->mColor;
+
+    if (fillGlyphs->mOptions.mCompositionOp != CompositionOp::OP_OVER ||
+        fillGlyphs->mOptions.mAlpha != 1.0f) {
+      return false;
+    }
+
+    //TODO: Deal with AA on the DrawOptions, and the GlyphRenderingOptions
+
+    aGlyphs.insert(aGlyphs.end(),
+                   fillGlyphs->mGlyphs.begin(),
+                   fillGlyphs->mGlyphs.end());
+  }
+  return true;
+}
+
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/2d/DrawTargetCapture.h
+++ b/gfx/2d/DrawTargetCapture.h
@@ -22,16 +22,18 @@ public:
   DrawTargetCaptureImpl()
   {}
 
   bool Init(const IntSize& aSize, DrawTarget* aRefDT);
 
   virtual BackendType GetBackendType() const { return mRefDT->GetBackendType(); }
   virtual DrawTargetType GetType() const { return mRefDT->GetType(); }
 
+  virtual bool IsCaptureDT() const { return true; }
+
   virtual already_AddRefed<SourceSurface> Snapshot();
 
   virtual void DetachAllSnapshots();
 
   virtual IntSize GetSize() { return mSize; }
 
   virtual void Flush() {}
   virtual void DrawSurface(SourceSurface *aSurface,
@@ -131,16 +133,18 @@ public:
   }
   virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType)
   {
     return mRefDT->CreateFilter(aType);
   }
 
   void ReplayToDrawTarget(DrawTarget* aDT, const Matrix& aTransform);
 
+  bool ContainsOnlyColoredGlyphs(RefPtr<ScaledFont>& aScaledFont, Color& aColor, std::vector<Glyph>& aGlyphs);
+
 protected:
   ~DrawTargetCaptureImpl();
 
 private:
 
   // This storage system was used to minimize the amount of heap allocations
   // that are required while recording. It should be noted there's no
   // guarantees on the alignments of DrawingCommands allocated in this array.
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -20,16 +20,17 @@
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/gfx/Matrix.h"
 #include "mozilla/layers/AsyncDragMetrics.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsRect.h"
 #include "nsRegion.h"
+#include "mozilla/Array.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
 #pragma warning( disable : 4800 )
 #endif
 
 namespace mozilla {
@@ -1263,11 +1264,47 @@ struct ParamTraits<mozilla::layers::Asyn
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) &&
             ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) &&
             ReadParam(aMsg, aIter, &aResult->mScrollTrack) &&
             ReadParam(aMsg, aIter, &aResult->mDirection));
   }
 };
 
+template <>
+struct ParamTraits<mozilla::gfx::Glyph>
+{
+  typedef mozilla::gfx::Glyph paramType;
+  static void Write(Message* aMsg, const paramType& aParam) {
+    WriteParam(aMsg, aParam.mIndex);
+    WriteParam(aMsg, aParam.mPosition);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+    return (ReadParam(aMsg, aIter, &aResult->mIndex) &&
+            ReadParam(aMsg, aIter, &aResult->mPosition)
+      );
+  }
+};
+
+template<typename T, size_t Length>
+struct ParamTraits<mozilla::Array<T, Length>>
+{
+  typedef mozilla::Array<T, Length> paramType;
+  static void Write(Message* aMsg, const paramType& aParam) {
+    for (size_t i = 0; i < Length; i++) {
+      WriteParam(aMsg, aParam[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
+    for (size_t i = 0; i < Length; i++) {
+      if (!ReadParam(aMsg, aIter, &aResult[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+};
+
 } /* namespace IPC */
 
 #endif /* __GFXMESSAGEUTILS_H__ */
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -474,19 +474,19 @@ public:
   }
 
   Color mColor;
   IntRect mBounds;
 };
 
 static ImageHost* GetImageHost(Layer* aLayer)
 {
-  LayerComposite* composite = aLayer->AsLayerComposite();
-  if (composite) {
-    return static_cast<ImageHost*>(composite->GetCompositableHost());
+  HostLayer* compositor = aLayer->AsHostLayer();
+  if (compositor) {
+    return static_cast<ImageHost*>(compositor->GetCompositableHost());
   }
   return nullptr;
 }
 
 struct ImageLayerProperties : public LayerPropertiesBase
 {
   explicit ImageLayerProperties(ImageLayer* aImage, bool aIsMask)
     : LayerPropertiesBase(aImage)
@@ -604,16 +604,18 @@ CloneLayerTreePropertiesInternal(Layer* 
       return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
     case Layer::TYPE_IMAGE:
       return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
     case Layer::TYPE_CANVAS:
       return MakeUnique<CanvasLayerProperties>(static_cast<CanvasLayer*>(aRoot));
     case Layer::TYPE_READBACK:
     case Layer::TYPE_SHADOW:
     case Layer::TYPE_PAINTED:
+    case Layer::TYPE_TEXT:
+    case Layer::TYPE_BORDER:
       return MakeUnique<LayerPropertiesBase>(aRoot);
   }
 
   MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
   return MakeUnique<LayerPropertiesBase>(aRoot);
 }
 
 /* static */ UniquePtr<LayerProperties>
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -571,26 +571,26 @@ Layer::CanUseOpaqueSurface()
     parent->CanUseOpaqueSurface();
 }
 
 // NB: eventually these methods will be defined unconditionally, and
 // can be moved into Layers.h
 const Maybe<ParentLayerIntRect>&
 Layer::GetLocalClipRect()
 {
-  if (LayerComposite* shadow = AsLayerComposite()) {
+  if (HostLayer* shadow = AsHostLayer()) {
     return shadow->GetShadowClipRect();
   }
   return GetClipRect();
 }
 
 const LayerIntRegion&
 Layer::GetLocalVisibleRegion()
 {
-  if (LayerComposite* shadow = AsLayerComposite()) {
+  if (HostLayer* shadow = AsHostLayer()) {
     return shadow->GetShadowVisibleRegion();
   }
   return GetVisibleRegion();
 }
 
 Matrix4x4
 Layer::SnapTransformTranslation(const Matrix4x4& aTransform,
                                 Matrix* aResidualTransform)
@@ -777,17 +777,17 @@ Layer::CalculateScissorRect(const Render
     currentClip = aCurrentScissorRect;
   }
 
   if (!clipLayer->GetLocalClipRect()) {
     return currentClip;
   }
 
   if (GetLocalVisibleRegion().IsEmpty() &&
-      !(AsLayerComposite() && AsLayerComposite()->NeedToDrawCheckerboarding())) {
+      !(AsHostLayer() && AsHostLayer()->NeedToDrawCheckerboarding())) {
     // When our visible region is empty, our parent may not have created the
     // intermediate surface that we would require for correct clipping; however,
     // this does not matter since we are invisible.
     // Make sure we still compute a clip rect if we want to draw checkboarding
     // for this layer, since we want to do this even if the layer is invisible.
     return RenderTargetIntRect(currentClip.TopLeft(), RenderTargetIntSize(0, 0));
   }
 
@@ -882,17 +882,17 @@ const CSSTransformMatrix
 Layer::GetTransformTyped() const
 {
   return ViewAs<CSSTransformMatrix>(GetTransform());
 }
 
 Matrix4x4
 Layer::GetLocalTransform()
 {
-  if (LayerComposite* shadow = AsLayerComposite())
+  if (HostLayer* shadow = AsHostLayer())
     return shadow->GetShadowTransform();
   else
     return GetTransform();
 }
 
 const LayerToParentLayerMatrix4x4
 Layer::GetLocalTransformTyped()
 {
@@ -936,17 +936,17 @@ Layer::ApplyPendingUpdatesForThisTransac
     }
   }
 }
 
 float
 Layer::GetLocalOpacity()
 {
   float opacity = mOpacity;
-  if (LayerComposite* shadow = AsLayerComposite())
+  if (HostLayer* shadow = AsHostLayer())
     opacity = shadow->GetShadowOpacity();
   return std::min(std::max(opacity, 0.0f), 1.0f);
 }
 
 float
 Layer::GetEffectiveOpacity()
 {
   float opacity = GetLocalOpacity();
@@ -1656,17 +1656,17 @@ LayerManager::StopFrameTimeRecording(uin
 }
 
 void
 LayerManager::BeginTabSwitch()
 {
   mTabSwitchStart = TimeStamp::Now();
 }
 
-static void PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite);
+static void PrintInfo(std::stringstream& aStream, HostLayer* aLayerComposite);
 
 #ifdef MOZ_DUMP_PAINTING
 template <typename T>
 void WriteSnapshotToDumpFile_internal(T* aObj, DataSourceSurface* aSurf)
 {
   nsCString string(aObj->Name());
   string.Append('-');
   string.AppendInt((uint64_t)aObj);
@@ -1697,18 +1697,18 @@ void WriteSnapshotToDumpFile(Compositor*
 }
 #endif
 
 void
 Layer::Dump(std::stringstream& aStream, const char* aPrefix,
             bool aDumpHtml, bool aSorted)
 {
 #ifdef MOZ_DUMP_PAINTING
-  bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsLayerComposite() &&
-                               AsLayerComposite()->GetCompositableHost();
+  bool dumpCompositorTexture = gfxEnv::DumpCompositorTextures() && AsHostLayer() &&
+                               AsHostLayer()->GetCompositableHost();
   bool dumpClientTexture = gfxEnv::DumpPaint() && AsShadowableLayer() &&
                            AsShadowableLayer()->GetCompositableClient();
   nsCString layerId(Name());
   layerId.Append('-');
   layerId.AppendInt((uint64_t)this);
 #endif
   if (aDumpHtml) {
     aStream << nsPrintfCString(R"(<li><a id="%p" )", this).get();
@@ -1718,17 +1718,17 @@ Layer::Dump(std::stringstream& aStream, 
     }
 #endif
     aStream << ">";
   }
   DumpSelf(aStream, aPrefix);
 
 #ifdef MOZ_DUMP_PAINTING
   if (dumpCompositorTexture) {
-    AsLayerComposite()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
+    AsHostLayer()->GetCompositableHost()->Dump(aStream, aPrefix, aDumpHtml);
   } else if (dumpClientTexture) {
     if (aDumpHtml) {
       aStream << nsPrintfCString(R"(<script>array["%s"]=")", layerId.BeginReading()).get();
     }
     AsShadowableLayer()->GetCompositableClient()->Dump(aStream, aPrefix,
         aDumpHtml, TextureDumpMode::DoNotCompress);
     if (aDumpHtml) {
       aStream << R"(";</script>)";
@@ -1881,17 +1881,17 @@ Layer::LogSelf(const char* aPrefix)
 }
 
 void
 Layer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("%s%s (0x%p)", mManager->Name(), Name(), this).get();
 
-  layers::PrintInfo(aStream, AsLayerComposite());
+  layers::PrintInfo(aStream, AsHostLayer());
 
   if (mClipRect) {
     AppendToString(aStream, *mClipRect, " [clip=", "]");
   }
   if (mScrolledClip) {
     AppendToString(aStream, mScrolledClip->GetClipRect(), " [scrolled-clip=", "]");
   }
   if (1.0 != mPostXScale || 1.0 != mPostYScale) {
@@ -2025,17 +2025,17 @@ Layer::DumpPacket(layerscope::LayersPack
   // Add a new layer (UnknownLayer)
   using namespace layerscope;
   LayersPacket::Layer* layer = aPacket->add_layer();
   // Basic information
   layer->set_type(LayersPacket::Layer::UnknownLayer);
   layer->set_ptr(reinterpret_cast<uint64_t>(this));
   layer->set_parentptr(reinterpret_cast<uint64_t>(aParent));
   // Shadow
-  if (LayerComposite* lc = AsLayerComposite()) {
+  if (HostLayer* lc = AsHostLayer()) {
     LayersPacket::Layer::Shadow* s = layer->mutable_shadow();
     if (const Maybe<ParentLayerIntRect>& clipRect = lc->GetShadowClipRect()) {
       DumpRect(s->mutable_clip(), *clipRect);
     }
     if (!lc->GetShadowBaseTransform().IsIdentity()) {
       DumpTransform(s->mutable_transform(), lc->GetShadowBaseTransform());
     }
     if (!lc->GetShadowVisibleRegion().IsEmpty()) {
@@ -2195,16 +2195,45 @@ ColorLayer::DumpPacket(layerscope::Layer
   Layer::DumpPacket(aPacket, aParent);
   // Get this layer data
   using namespace layerscope;
   LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
   layer->set_type(LayersPacket::Layer::ColorLayer);
   layer->set_color(mColor.ToABGR());
 }
 
+void
+TextLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+  Layer::PrintInfo(aStream, aPrefix);
+  AppendToString(aStream, mBounds, " [bounds=", "]");
+}
+
+void
+TextLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+  Layer::DumpPacket(aPacket, aParent);
+  // Get this layer data
+  using namespace layerscope;
+  LayersPacket::Layer* layer = aPacket->mutable_layer(aPacket->layer_size()-1);
+  layer->set_type(LayersPacket::Layer::TextLayer);
+}
+
+void
+BorderLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+  Layer::PrintInfo(aStream, aPrefix);
+}
+
+void
+BorderLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
+{
+  Layer::DumpPacket(aPacket, aParent);
+}
+
 CanvasLayer::CanvasLayer(LayerManager* aManager, void* aImplData)
   : Layer(aManager, aImplData)
   , mPreTransCallback(nullptr)
   , mPreTransCallbackData(nullptr)
   , mPostTransCallback(nullptr)
   , mPostTransCallbackData(nullptr)
   , mSamplingFilter(gfx::SamplingFilter::GOOD)
   , mDirty(false)
@@ -2453,17 +2482,17 @@ LayerManager::GetPendingScrollInfoUpdate
 
 void
 LayerManager::ClearPendingScrollInfoUpdate()
 {
   mPendingScrollUpdates.clear();
 }
 
 void
-PrintInfo(std::stringstream& aStream, LayerComposite* aLayerComposite)
+PrintInfo(std::stringstream& aStream, HostLayer* aLayerComposite)
 {
   if (!aLayerComposite) {
     return;
   }
   if (const Maybe<ParentLayerIntRect>& clipRect = aLayerComposite->GetShadowClipRect()) {
     AppendToString(aStream, *clipRect, " [shadow-clip=", "]");
   }
   if (!aLayerComposite->GetShadowBaseTransform().IsIdentity()) {
@@ -2495,10 +2524,25 @@ SetAntialiasingFlags(Layer* aLayer, Draw
 }
 
 IntRect
 ToOutsideIntRect(const gfxRect &aRect)
 {
   return IntRect::RoundOut(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
+TextLayer::TextLayer(LayerManager* aManager, void* aImplData)
+  : Layer(aManager, aImplData)
+{}
+
+TextLayer::~TextLayer()
+{}
+
+void
+TextLayer::SetGlyphs(nsTArray<GlyphArray>&& aGlyphs)
+{
+  MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Glyphs", this));
+  mGlyphs = Move(aGlyphs);
+  Mutated();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -13,16 +13,17 @@
 #include "FrameMetrics.h"               // for FrameMetrics
 #include "Units.h"                      // for LayerMargin, LayerPoint, ParentLayerIntRect
 #include "gfxContext.h"
 #include "gfxTypes.h"
 #include "gfxPoint.h"                   // for gfxPoint
 #include "gfxRect.h"                    // for gfxRect
 #include "gfx2DGlue.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2, etc
+#include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/EventForwards.h"      // for nsPaintEvent
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/Poison.h"
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 #include "mozilla/TimeStamp.h"          // for TimeStamp, TimeDuration
 #include "mozilla/UniquePtr.h"          // for UniquePtr
@@ -80,28 +81,31 @@ class AsyncPanZoomController;
 class BasicLayerManager;
 class ClientLayerManager;
 class Layer;
 class LayerMetricsWrapper;
 class PaintedLayer;
 class ContainerLayer;
 class ImageLayer;
 class ColorLayer;
+class TextLayer;
 class CanvasLayer;
+class BorderLayer;
 class ReadbackLayer;
 class ReadbackProcessor;
 class RefLayer;
-class LayerComposite;
+class HostLayer;
 class ShadowableLayer;
 class ShadowLayerForwarder;
 class LayerManagerComposite;
 class SpecificLayerAttributes;
 class Compositor;
 class FrameUniformityData;
 class PersistentBufferProvider;
+class GlyphArray;
 
 namespace layerscope {
 class LayersPacket;
 } // namespace layerscope
 
 #define MOZ_LAYER_DECL_NAME(n, e)                              \
   virtual const char* Name() const override { return n; }  \
   virtual LayerType GetType() const override { return e; }
@@ -397,16 +401,26 @@ public:
   virtual already_AddRefed<ImageLayer> CreateImageLayer() = 0;
   /**
    * CONSTRUCTION PHASE ONLY
    * Create a ColorLayer for this manager's layer tree.
    */
   virtual already_AddRefed<ColorLayer> CreateColorLayer() = 0;
   /**
    * CONSTRUCTION PHASE ONLY
+   * Create a TextLayer for this manager's layer tree.
+   */
+  virtual already_AddRefed<TextLayer> CreateTextLayer() = 0;
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Create a BorderLayer for this manager's layer tree.
+   */
+  virtual already_AddRefed<BorderLayer> CreateBorderLayer() { return nullptr; }
+  /**
+   * CONSTRUCTION PHASE ONLY
    * Create a CanvasLayer for this manager's layer tree.
    */
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() = 0;
   /**
    * CONSTRUCTION PHASE ONLY
    * Create a ReadbackLayer for this manager's layer tree.
    */
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() { return nullptr; }
@@ -739,16 +753,18 @@ class Layer {
 
 public:
   // Keep these in alphabetical order
   enum LayerType {
     TYPE_CANVAS,
     TYPE_COLOR,
     TYPE_CONTAINER,
     TYPE_IMAGE,
+    TYPE_TEXT,
+    TYPE_BORDER,
     TYPE_READBACK,
     TYPE_REF,
     TYPE_SHADOW,
     TYPE_PAINTED
   };
 
   /**
    * Returns the LayerManager this Layer belongs to. Note that the layer
@@ -1518,20 +1534,32 @@ public:
 
    /**
     * Dynamic cast to a Color. Returns null if this is not a
     * ColorLayer.
     */
   virtual ColorLayer* AsColorLayer() { return nullptr; }
 
   /**
+    * Dynamic cast to a TextLayer. Returns null if this is not a
+    * TextLayer.
+    */
+  virtual TextLayer* AsTextLayer() { return nullptr; }
+
+  /**
+    * Dynamic cast to a Border. Returns null if this is not a
+    * ColorLayer.
+    */
+  virtual BorderLayer* AsBorderLayer() { return nullptr; }
+
+  /**
    * Dynamic cast to a LayerComposite.  Return null if this is not a
    * LayerComposite.  Can be used anytime.
    */
-  virtual LayerComposite* AsLayerComposite() { return nullptr; }
+  virtual HostLayer* AsHostLayer() { return nullptr; }
 
   /**
    * Dynamic cast to a ShadowableLayer.  Return null if this is not a
    * ShadowableLayer.  Can be used anytime.
    */
   virtual ShadowableLayer* AsShadowableLayer() { return nullptr; }
 
   // These getters can be used anytime.  They return the effective
@@ -1941,16 +1969,23 @@ public:
    * and ensure that the layer is invalidated whenever the residual changes.
    * When it's false, a change in the residual will not trigger invalidation
    * and GetResidualTranslation will return 0,0.
    * So when the residual is to be ignored, set this to false for better
    * performance.
    */
   void SetAllowResidualTranslation(bool aAllow) { mAllowResidualTranslation = aAllow; }
 
+  void SetValidRegion(const nsIntRegion& aRegion)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ValidRegion", this));
+    mValidRegion = aRegion;
+    Mutated();
+  }
+
   /**
    * Can be used anytime
    */
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
 
   virtual PaintedLayer* AsPaintedLayer() override { return this; }
 
   MOZ_LAYER_DECL_NAME("PaintedLayer", TYPE_PAINTED)
@@ -2311,16 +2346,138 @@ protected:
 
   virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
 
   gfx::IntRect mBounds;
   gfx::Color mColor;
 };
 
 /**
+ * A Layer which renders Glyphs.
+ */
+class TextLayer : public Layer {
+public:
+  virtual TextLayer* AsTextLayer() override { return this; }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
+   */
+  void SetBounds(const gfx::IntRect& aBounds)
+  {
+    if (!mBounds.IsEqualEdges(aBounds)) {
+      mBounds = aBounds;
+      Mutated();
+    }
+  }
+
+  const gfx::IntRect& GetBounds()
+  {
+    return mBounds;
+  }
+
+  void SetScaledFont(gfx::ScaledFont* aScaledFont) {
+    if (aScaledFont != mFont) {
+      mFont = aScaledFont;
+      Mutated();
+    }
+  }
+
+  gfx::ScaledFont* GetScaledFont() { return mFont; }
+
+  MOZ_LAYER_DECL_NAME("TextLayer", TYPE_TEXT)
+
+  virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+  {
+    gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+    mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+  }
+
+  virtual void SetGlyphs(nsTArray<GlyphArray>&& aGlyphs);
+protected:
+  TextLayer(LayerManager* aManager, void* aImplData);
+  ~TextLayer();
+
+  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+  virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+  gfx::IntRect mBounds;
+  nsTArray<GlyphArray> mGlyphs;
+  RefPtr<gfx::ScaledFont> mFont;
+};
+
+/**
+ * A Layer which renders a rounded rect.
+ */
+class BorderLayer : public Layer {
+public:
+  virtual BorderLayer* AsBorderLayer() override { return this; }
+
+  /**
+   * CONSTRUCTION PHASE ONLY
+   * Set the color of the layer.
+   */
+
+  // Colors of each side as in css::Side
+  virtual void SetColors(const BorderColors& aColors)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Colors", this));
+    PodCopy(&mColors[0], &aColors[0], 4);
+    Mutated();
+  }
+
+  virtual void SetRect(const LayerRect& aRect)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Rect", this));
+    mRect = aRect;
+    Mutated();
+  }
+
+  // Size of each rounded corner as in css::Corner, 0.0 means a
+  // rectangular corner.
+  virtual void SetCornerRadii(const BorderCorners& aCorners)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Corners", this));
+    PodCopy(&mCorners[0], &aCorners[0], 4);
+    Mutated();
+  }
+
+  virtual void SetWidths(const BorderWidths& aWidths)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Widths", this));
+    PodCopy(&mWidths[0], &aWidths[0], 4);
+    Mutated();
+  }
+
+  MOZ_LAYER_DECL_NAME("BorderLayer", TYPE_BORDER)
+
+  virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+  {
+    gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
+    mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
+    ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
+  }
+
+protected:
+  BorderLayer(LayerManager* aManager, void* aImplData)
+    : Layer(aManager, aImplData)
+  {}
+
+  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+  virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
+
+  BorderColors mColors;
+  LayerRect mRect;
+  BorderCorners mCorners;
+  BorderWidths mWidths;
+};
+
+/**
  * A Layer for HTML Canvas elements.  It's backed by either a
  * gfxASurface or a GLContext (for WebGL layers), and has some control
  * for intelligent updating from the source if necessary (for example,
  * if hardware compositing is not available, for reading from the GL
  * buffer into an image surface that we can layer composite.)
  *
  * After Initialize is called, the underlying canvas Surface/GLContext
  * must not be modified during a layer transaction.
@@ -2366,16 +2523,18 @@ public:
    * Initialize this CanvasLayer with the given data.  The data must
    * have either mSurface or mGLContext initialized (but not both), as
    * well as mSize.
    *
    * This must only be called once.
    */
   virtual void Initialize(const Data& aData) = 0;
 
+  void SetBounds(gfx::IntRect aBounds) { mBounds = aBounds; }
+
   /**
    * Check the data is owned by this layer is still valid for rendering
    */
   virtual bool IsDataValid(const Data& aData) { return true; }
 
   /**
    * Notify this CanvasLayer that the canvas surface contents have
    * changed (or will change) before the next transaction.
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -237,12 +237,16 @@ typedef gfx::Matrix4x4Typed<LayerPixel, 
 // AsyncTransformComponentMatrix), and we represent the product of all of them
 // as a CSSTransformLayer -> ParentLayer transform (aliased as
 // AsyncTransformMatrix). To create an AsyncTransformMatrix from component
 // matrices, a ViewAs operation is needed. A MultipleAsyncTransforms
 // PixelCastJustification is provided for this purpose.
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> AsyncTransformComponentMatrix;
 typedef gfx::Matrix4x4Typed<CSSTransformedLayerPixel, ParentLayerPixel> AsyncTransformMatrix;
 
+typedef Array<gfx::Color, 4> BorderColors;
+typedef Array<LayerSize, 4> BorderCorners;
+typedef Array<LayerCoord, 4> BorderWidths;
+
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_LAYERSTYPES_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/BasicBorderLayer.cpp
@@ -0,0 +1,83 @@
+/* -*- 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/. */
+
+#include "BasicLayersImpl.h"            // for FillRectWithMask, etc
+#include "Layers.h"                     // for ColorLayer, etc
+#include "BasicImplData.h"              // for BasicImplData
+#include "BasicLayers.h"                // for BasicLayerManager
+#include "gfxContext.h"                 // for gfxContext, etc
+#include "gfxRect.h"                    // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h"           // for operator new
+#include "nsCOMPtr.h"                   // for already_AddRefed
+#include "nsDebug.h"                    // for NS_ASSERTION
+#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
+#include "nsRect.h"                     // for mozilla::gfx::IntRect
+#include "nsRegion.h"                   // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicBorderLayer : public BorderLayer, public BasicImplData {
+public:
+  explicit BasicBorderLayer(BasicLayerManager* aLayerManager) :
+    BorderLayer(aLayerManager, static_cast<BasicImplData*>(this))
+  {
+    MOZ_COUNT_CTOR(BasicBorderLayer);
+  }
+
+protected:
+  virtual ~BasicBorderLayer()
+  {
+    MOZ_COUNT_DTOR(BasicBorderLayer);
+  }
+
+public:
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+  {
+    NS_ASSERTION(BasicManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    BorderLayer::SetVisibleRegion(aRegion);
+  }
+
+  virtual void Paint(DrawTarget* aDT,
+                     const gfx::Point& aDeviceOffset,
+                     Layer* aMaskLayer) override
+  {
+    if (IsHidden()) {
+      return;
+    }
+
+    // We currently assume that we never have rounded corners,
+    // and that all borders have the same width and color.
+
+    ColorPattern color(mColors[0]);
+    StrokeOptions strokeOptions(mWidths[0]);
+
+    Rect rect = mRect.ToUnknownRect();
+    rect.Deflate(mWidths[0] / 2.0);
+    aDT->StrokeRect(rect, color, strokeOptions);
+  }
+
+protected:
+  BasicLayerManager* BasicManager()
+  {
+    return static_cast<BasicLayerManager*>(mManager);
+  }
+};
+
+already_AddRefed<BorderLayer>
+BasicLayerManager::CreateBorderLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  RefPtr<BorderLayer> layer = new BasicBorderLayer(this);
+  return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -109,16 +109,18 @@ public:
 
   virtual void SetRoot(Layer* aLayer) override;
 
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+  virtual already_AddRefed<TextLayer> CreateTextLayer() override;
+  virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
   virtual ImageFactory *GetImageFactory();
 
   virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_BASIC; }
   virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("Basic"); }
 
   bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
 #ifdef DEBUG
new file mode 100644
--- /dev/null
+++ b/gfx/layers/basic/BasicTextLayer.cpp
@@ -0,0 +1,86 @@
+/* -*- 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/. */
+
+#include "BasicLayersImpl.h"            // for FillRectWithMask, etc
+#include "Layers.h"                     // for ColorLayer, etc
+#include "BasicImplData.h"              // for BasicImplData
+#include "BasicLayers.h"                // for BasicLayerManager
+#include "gfxContext.h"                 // for gfxContext, etc
+#include "gfxRect.h"                    // for gfxRect
+#include "gfx2DGlue.h"
+#include "mozilla/mozalloc.h"           // for operator new
+#include "nsCOMPtr.h"                   // for already_AddRefed
+#include "nsDebug.h"                    // for NS_ASSERTION
+#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
+#include "nsRect.h"                     // for mozilla::gfx::IntRect
+#include "nsRegion.h"                   // for nsIntRegion
+#include "mozilla/gfx/PathHelpers.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+class BasicTextLayer : public TextLayer, public BasicImplData {
+public:
+  explicit BasicTextLayer(BasicLayerManager* aLayerManager) :
+    TextLayer(aLayerManager, static_cast<BasicImplData*>(this))
+  {
+    MOZ_COUNT_CTOR(BasicTextLayer);
+  }
+
+protected:
+  virtual ~BasicTextLayer()
+  {
+    MOZ_COUNT_DTOR(BasicTextLayer);
+  }
+
+public:
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+  {
+    NS_ASSERTION(BasicManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    TextLayer::SetVisibleRegion(aRegion);
+  }
+
+  virtual void Paint(DrawTarget* aDT,
+                     const gfx::Point& aDeviceOffset,
+                     Layer* aMaskLayer) override
+  {
+    if (IsHidden() || !mFont) {
+      return;
+    }
+
+    Rect snapped(mBounds.x, mBounds.y, mBounds.width, mBounds.height);
+    MaybeSnapToDevicePixels(snapped, *aDT, true);
+
+    // We don't currently support subpixel-AA in TextLayers since we
+    // don't check if there's an opaque background color behind them.
+    // We should fix this before using them in production.
+    aDT->SetPermitSubpixelAA(false);
+
+    for (GlyphArray& g : mGlyphs) {
+      GlyphBuffer buffer = { g.glyphs().Elements(), (uint32_t)g.glyphs().Length() };
+      aDT->FillGlyphs(mFont, buffer, ColorPattern(g.color().value()));
+    }
+  }
+
+protected:
+  BasicLayerManager* BasicManager()
+  {
+    return static_cast<BasicLayerManager*>(mManager);
+  }
+};
+
+already_AddRefed<TextLayer>
+BasicLayerManager::CreateTextLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  RefPtr<TextLayer> layer = new BasicTextLayer(this);
+  return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/client/ClientBorderLayer.cpp
@@ -0,0 +1,79 @@
+/* -*- 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/. */
+
+#include "ClientLayerManager.h"         // for ClientLayerManager, etc
+#include "Layers.h"                     // for ColorLayer, etc
+#include "mozilla/layers/LayersMessages.h"  // for ColorLayerAttributes, etc
+#include "mozilla/mozalloc.h"           // for operator new
+#include "nsCOMPtr.h"                   // for already_AddRefed
+#include "nsDebug.h"                    // for NS_ASSERTION
+#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
+#include "nsRegion.h"                   // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientBorderLayer : public BorderLayer,
+                          public ClientLayer {
+public:
+  explicit ClientBorderLayer(ClientLayerManager* aLayerManager) :
+    BorderLayer(aLayerManager, static_cast<ClientLayer*>(this))
+  {
+    MOZ_COUNT_CTOR(ClientBorderLayer);
+  }
+
+protected:
+  virtual ~ClientBorderLayer()
+  {
+    MOZ_COUNT_DTOR(ClientBorderLayer);
+  }
+
+public:
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+  {
+    NS_ASSERTION(ClientManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    BorderLayer::SetVisibleRegion(aRegion);
+  }
+
+  virtual void RenderLayer()
+  {
+    RenderMaskLayers(this);
+  }
+
+  virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+  {
+    aAttrs = BorderLayerAttributes(mRect, mColors, mCorners, mWidths);
+  }
+
+  virtual Layer* AsLayer() { return this; }
+  virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+  virtual void Disconnect()
+  {
+    ClientLayer::Disconnect();
+  }
+
+protected:
+  ClientLayerManager* ClientManager()
+  {
+    return static_cast<ClientLayerManager*>(mManager);
+  }
+};
+
+already_AddRefed<BorderLayer>
+ClientLayerManager::CreateBorderLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  RefPtr<ClientBorderLayer> layer =
+    new ClientBorderLayer(this);
+  CREATE_SHADOW(Border);
+  return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/client/ClientCanvasLayer.cpp
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -14,16 +14,18 @@
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #include "gfxPrefs.h"                   // for WebGLForceLayersReadback
+#include "gfxUtils.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 
 namespace mozilla {
 namespace layers {
 
 ClientCanvasLayer::~ClientCanvasLayer()
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -82,16 +82,18 @@ public:
 
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override;
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+  virtual already_AddRefed<TextLayer> CreateTextLayer() override;
+  virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<RefLayer> CreateRefLayer() override;
 
   void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier);
   TextureFactoryIdentifier GetTextureFactoryIdentifier()
   {
     return AsShadowForwarder()->GetTextureFactoryIdentifier();
   }
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/client/ClientTextLayer.cpp
@@ -0,0 +1,91 @@
+/* -*- 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/. */
+
+#include "ClientLayerManager.h"         // for ClientLayerManager, etc
+#include "Layers.h"                     // for ColorLayer, etc
+#include "mozilla/layers/LayersMessages.h"  // for ColorLayerAttributes, etc
+#include "mozilla/mozalloc.h"           // for operator new
+#include "nsCOMPtr.h"                   // for already_AddRefed
+#include "nsDebug.h"                    // for NS_ASSERTION
+#include "nsISupportsImpl.h"            // for Layer::AddRef, etc
+#include "nsRegion.h"                   // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientTextLayer : public TextLayer,
+                        public ClientLayer {
+public:
+  explicit ClientTextLayer(ClientLayerManager* aLayerManager) :
+    TextLayer(aLayerManager, static_cast<ClientLayer*>(this)),
+    mSwapped(false)
+  {
+    MOZ_COUNT_CTOR(ClientTextLayer);
+  }
+
+protected:
+  virtual ~ClientTextLayer()
+  {
+    MOZ_COUNT_DTOR(ClientTextLayer);
+  }
+
+public:
+  virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+  {
+    NS_ASSERTION(ClientManager()->InConstruction(),
+                 "Can only set properties in construction phase");
+    TextLayer::SetVisibleRegion(aRegion);
+  }
+
+  virtual void RenderLayer()
+  {
+    RenderMaskLayers(this);
+  }
+
+  virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+  {
+    NS_ASSERTION(!mSwapped, "Trying to access glyph array after it's been swapped!");
+    aAttrs = TextLayerAttributes(GetBounds(), nsTArray<GlyphArray>(), uintptr_t(mFont.get()));
+    aAttrs.get_TextLayerAttributes().glyphs().SwapElements(mGlyphs);
+    mSwapped = true;
+  }
+
+  virtual void SetGlyphs(nsTArray<GlyphArray>&& aGlyphs)
+  {
+    TextLayer::SetGlyphs(Move(aGlyphs));
+    mSwapped = false;
+  }
+
+  virtual Layer* AsLayer() { return this; }
+  virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+  virtual void Disconnect()
+  {
+    ClientLayer::Disconnect();
+  }
+
+protected:
+  ClientLayerManager* ClientManager()
+  {
+    return static_cast<ClientLayerManager*>(mManager);
+  }
+
+  bool mSwapped;
+};
+
+already_AddRefed<TextLayer>
+ClientLayerManager::CreateTextLayer()
+{
+  NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+  RefPtr<ClientTextLayer> layer =
+    new ClientTextLayer(this);
+  CREATE_SHADOW(Text);
+  return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -64,17 +64,17 @@ IsSameDimension(dom::ScreenOrientationIn
 }
 
 static bool
 ContentMightReflowOnOrientationChange(const IntRect& rect)
 {
   return rect.width != rect.height;
 }
 
-AsyncCompositionManager::AsyncCompositionManager(LayerManagerComposite* aManager)
+AsyncCompositionManager::AsyncCompositionManager(HostLayerManager* aManager)
   : mLayerManager(aManager)
   , mIsFirstPaint(true)
   , mLayersUpdated(false)
   , mPaintSyncId(0)
   , mReadyForCompose(true)
 {
 }
 
@@ -200,45 +200,45 @@ AsyncCompositionManager::ComputeRotation
 }
 
 #ifdef DEBUG
 static void
 GetBaseTransform(Layer* aLayer, Matrix4x4* aTransform)
 {
   // Start with the animated transform if there is one
   *aTransform =
-    (aLayer->AsLayerComposite()->GetShadowTransformSetByAnimation()
+    (aLayer->AsHostLayer()->GetShadowTransformSetByAnimation()
         ? aLayer->GetLocalTransform()
         : aLayer->GetTransform());
 }
 #endif
 
 static void
 TransformClipRect(Layer* aLayer,
                   const ParentLayerToParentLayerMatrix4x4& aTransform)
 {
   MOZ_ASSERT(aTransform.Is2D());
-  const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+  const Maybe<ParentLayerIntRect>& clipRect = aLayer->AsHostLayer()->GetShadowClipRect();
   if (clipRect) {
     ParentLayerIntRect transformed = TransformBy(aTransform, *clipRect);
-    aLayer->AsLayerComposite()->SetShadowClipRect(Some(transformed));
+    aLayer->AsHostLayer()->SetShadowClipRect(Some(transformed));
   }
 }
 
 // Similar to TransformFixedClip(), but only transforms the fixed part of the
 // clip.
 static void
 TransformFixedClip(Layer* aLayer,
                    const ParentLayerToParentLayerMatrix4x4& aTransform,
                    AsyncCompositionManager::ClipParts& aClipParts)
 {
   MOZ_ASSERT(aTransform.Is2D());
   if (aClipParts.mFixedClip) {
     *aClipParts.mFixedClip = TransformBy(aTransform, *aClipParts.mFixedClip);
-    aLayer->AsLayerComposite()->SetShadowClipRect(aClipParts.Intersect());
+    aLayer->AsHostLayer()->SetShadowClipRect(aClipParts.Intersect());
   }
 }
 
 /**
  * Set the given transform as the shadow transform on the layer, assuming
  * that the given transform already has the pre- and post-scales applied.
  * That is, this function cancels out the pre- and post-scales from aTransform
  * before setting it as the shadow transform on the layer, so that when
@@ -251,17 +251,17 @@ SetShadowTransform(Layer* aLayer, LayerT
   if (ContainerLayer* c = aLayer->AsContainerLayer()) {
     aTransform.PreScale(1.0f / c->GetPreXScale(),
                         1.0f / c->GetPreYScale(),
                         1);
   }
   aTransform.PostScale(1.0f / aLayer->GetPostXScale(),
                        1.0f / aLayer->GetPostYScale(),
                        1);
-  aLayer->AsLayerComposite()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
+  aLayer->AsHostLayer()->SetShadowBaseTransform(aTransform.ToUnknownMatrix());
 }
 
 static void
 TranslateShadowLayer(Layer* aLayer,
                      const ParentLayerPoint& aTranslation,
                      bool aAdjustClipRect,
                      AsyncCompositionManager::ClipPartsCache* aClipPartsCache)
 {
@@ -272,17 +272,17 @@ TranslateShadowLayer(Layer* aLayer,
   // we don't have to worry about the adjustments compounding over successive
   // frames.
   LayerToParentLayerMatrix4x4 layerTransform = aLayer->GetLocalTransformTyped();
 
   // Apply the translation to the layer transform.
   layerTransform.PostTranslate(aTranslation);
 
   SetShadowTransform(aLayer, layerTransform);
-  aLayer->AsLayerComposite()->SetShadowTransformSetByAnimation(false);
+  aLayer->AsHostLayer()->SetShadowTransformSetByAnimation(false);
 
   if (aAdjustClipRect) {
     auto transform = ParentLayerToParentLayerMatrix4x4::Translation(aTranslation);
     // If we're passed a clip parts cache, only transform the fixed part of
     // the clip.
     if (aClipPartsCache) {
       auto iter = aClipPartsCache->find(aLayer);
       MOZ_ASSERT(iter != aClipPartsCache->end());
@@ -715,32 +715,32 @@ SampleAnimations(Layer* aLayer, TimeStam
           // interpolate the property
           Animatable interpolatedValue;
           SampleValue(portion, animation,
                       animData.mStartValues[segmentIndex],
                       animData.mEndValues[segmentIndex],
                       animData.mEndValues.LastElement(),
                       computedTiming.mCurrentIteration,
                       &interpolatedValue, layer);
-          LayerComposite* layerComposite = layer->AsLayerComposite();
+          HostLayer* layerCompositor = layer->AsHostLayer();
           switch (animation.property()) {
           case eCSSProperty_opacity:
           {
-            layerComposite->SetShadowOpacity(interpolatedValue.get_float());
-            layerComposite->SetShadowOpacitySetByAnimation(true);
+            layerCompositor->SetShadowOpacity(interpolatedValue.get_float());
+            layerCompositor->SetShadowOpacitySetByAnimation(true);
             break;
           }
           case eCSSProperty_transform:
           {
             Matrix4x4 matrix = interpolatedValue.get_ArrayOfTransformFunction()[0].get_TransformMatrix().value();
             if (ContainerLayer* c = layer->AsContainerLayer()) {
               matrix.PostScale(c->GetInheritedXScale(), c->GetInheritedYScale(), 1);
             }
-            layerComposite->SetShadowBaseTransform(matrix);
-            layerComposite->SetShadowTransformSetByAnimation(true);
+            layerCompositor->SetShadowBaseTransform(matrix);
+            layerCompositor->SetShadowTransformSetByAnimation(true);
             break;
           }
           default:
             NS_WARNING("Unhandled animated property");
           }
         }
       });
   return activeAnimations;
@@ -774,17 +774,17 @@ AsyncCompositionManager::RecordShadowTra
       aLayer,
       [this] (Layer* layer)
       {
         for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
           AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
           if (!apzc) {
             continue;
           }
-          gfx::Matrix4x4 shadowTransform = layer->AsLayerComposite()->GetShadowBaseTransform();
+          gfx::Matrix4x4 shadowTransform = layer->AsHostLayer()->GetShadowBaseTransform();
           if (!shadowTransform.Is2D()) {
             continue;
           }
 
           Matrix transform = shadowTransform.As2D();
           if (transform.IsTranslation() && !shadowTransform.IsIdentity()) {
             Point translation = transform.GetTranslation();
             mLayerTransformRecorder.RecordTransform(layer, translation);
@@ -800,44 +800,44 @@ AdjustForClip(const AsyncTransformCompon
   AsyncTransformComponentMatrix result = asyncTransform;
 
   // Container layers start at the origin, but they are clipped to where they
   // actually have content on the screen. The tree transform is meant to apply
   // to the clipped area. If the tree transform includes a scale component,
   // then applying it to container as-is will produce incorrect results. To
   // avoid this, translate the layer so that the clip rect starts at the origin,
   // apply the tree transform, and translate back.
-  if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsLayerComposite()->GetShadowClipRect()) {
+  if (const Maybe<ParentLayerIntRect>& shadowClipRect = aLayer->AsHostLayer()->GetShadowClipRect()) {
     if (shadowClipRect->TopLeft() != ParentLayerIntPoint()) {  // avoid a gratuitous change of basis
       result.ChangeBasis(shadowClipRect->x, shadowClipRect->y, 0);
     }
   }
   return result;
 }
 
 static void
 ExpandRootClipRect(Layer* aLayer, const ScreenMargin& aFixedLayerMargins)
 {
   // For Fennec we want to expand the root scrollable layer clip rect based on
   // the fixed position margins. In particular, we want this while the dynamic
   // toolbar is in the process of sliding offscreen and the area of the
   // LayerView visible to the user is larger than the viewport size that Gecko
   // knows about (and therefore larger than the clip rect). We could also just
   // clear the clip rect on aLayer entirely but this seems more precise.
-  Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+  Maybe<ParentLayerIntRect> rootClipRect = aLayer->AsHostLayer()->GetShadowClipRect();
   if (rootClipRect && aFixedLayerMargins != ScreenMargin()) {
 #ifndef MOZ_WIDGET_ANDROID
     // We should never enter here on anything other than Fennec, since
     // aFixedLayerMargins should be empty everywhere else.
     MOZ_ASSERT(false);
 #endif
     ParentLayerRect rect(rootClipRect.value());
     rect.Deflate(ViewAs<ParentLayerPixel>(aFixedLayerMargins,
       PixelCastJustification::ScreenIsParentLayerForRoot));
-    aLayer->AsLayerComposite()->SetShadowClipRect(Some(RoundedOut(rect)));
+    aLayer->AsHostLayer()->SetShadowClipRect(Some(RoundedOut(rect)));
   }
 }
 
 #ifdef MOZ_WIDGET_ANDROID
 static void
 MoveScrollbarForLayerMargin(Layer* aRoot, FrameMetrics::ViewID aRootScrollId,
                             const ScreenMargin& aFixedLayerMargins)
 {
@@ -1098,17 +1098,17 @@ AsyncCompositionManager::ApplyAsyncConte
         bool clipChanged = (hasAsyncTransform || clipDeferredFromChildren ||
                             layer->GetScrolledClipRect());
         if (clipChanged) {
           // Intersect the two clip parts and apply them to the layer.
           // During ApplyAsyncContentTransformTree on an ancestor layer,
           // AlignFixedAndStickyLayers may overwrite this with a new clip it
           // computes from the clip parts, but if that doesn't happen, this
           // is the layer's final clip rect.
-          layer->AsLayerComposite()->SetShadowClipRect(clipParts.Intersect());
+          layer->AsHostLayer()->SetShadowClipRect(clipParts.Intersect());
         }
 
         if (hasAsyncTransform) {
           // Apply the APZ transform on top of GetLocalTransform() here (rather than
           // GetTransform()) in case the OMTA code in SampleAnimations already set a
           // shadow transform; in that case we want to apply ours on top of that one
           // rather than clobber it.
           SetShadowTransform(layer,
@@ -1434,17 +1434,17 @@ AsyncCompositionManager::TransformShadow
     MOZ_ASSERT(aVsyncRate != TimeDuration::Forever());
     if (aVsyncRate != TimeDuration::Forever()) {
       nextFrame += aVsyncRate;
     }
 
     wantNextFrame |= SampleAPZAnimations(LayerMetricsWrapper(root), nextFrame);
   }
 
-  LayerComposite* rootComposite = root->AsLayerComposite();
+  HostLayer* rootComposite = root->AsHostLayer();
 
   gfx::Matrix4x4 trans = rootComposite->GetShadowBaseTransform();
   trans *= gfx::Matrix4x4::From2D(mWorldTransform);
   rootComposite->SetShadowBaseTransform(trans);
 
   if (gfxPrefs::CollectScrollTransforms()) {
     RecordShadowTransforms(root);
   }
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -65,17 +65,17 @@ struct AsyncTransform {
 class AsyncCompositionManager final
 {
   friend class AutoResolveRefLayers;
   ~AsyncCompositionManager();
 
 public:
   NS_INLINE_DECL_REFCOUNTING(AsyncCompositionManager)
 
-  explicit AsyncCompositionManager(LayerManagerComposite* aManager);
+  explicit AsyncCompositionManager(HostLayerManager* aManager);
 
   /**
    * This forces the is-first-paint flag to true. This is intended to
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
@@ -213,17 +213,17 @@ private:
   void DetachRefLayers();
 
   // Records the shadow transforms for the tree of layers rooted at the given layer
   void RecordShadowTransforms(Layer* aLayer);
 
   TargetConfig mTargetConfig;
   CSSRect mContentRect;
 
-  RefPtr<LayerManagerComposite> mLayerManager;
+  RefPtr<HostLayerManager> mLayerManager;
   // When this flag is set, the next composition will be the first for a
   // particular document (i.e. the document displayed on the screen will change).
   // This happens when loading a new page or switching tabs. We notify the
   // front-end (e.g. Java on Android) about this so that it take the new page
   // size and zoom into account when providing us with the next view transform.
   bool mIsFirstPaint;
 
   // This flag is set during a layers update, so that the first composition
--- a/gfx/layers/composite/CanvasLayerComposite.cpp
+++ b/gfx/layers/composite/CanvasLayerComposite.cpp
@@ -54,17 +54,17 @@ CanvasLayerComposite::SetCompositableHos
 
 Layer*
 CanvasLayerComposite::GetLayer()
 {
   return this;
 }
 
 void
-CanvasLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+CanvasLayerComposite::SetLayerManager(HostLayerManager* aManager)
 {
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mCompositableHost && mCompositor) {
     mCompositableHost->SetCompositor(mCompositor);
   }
 }
 
--- a/gfx/layers/composite/CanvasLayerComposite.h
+++ b/gfx/layers/composite/CanvasLayerComposite.h
@@ -43,30 +43,28 @@ public:
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual void Disconnect() override
   {
     Destroy();
   }
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+  virtual void SetLayerManager(HostLayerManager* aManager) override;
 
   virtual Layer* GetLayer() override;
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override;
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
-
-  void SetBounds(gfx::IntRect aBounds) { mBounds = aBounds; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   virtual const char* Name() const override { return "CanvasLayerComposite"; }
 
 protected:
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
 private:
   gfx::SamplingFilter GetSamplingFilter();
--- a/gfx/layers/composite/ColorLayerComposite.h
+++ b/gfx/layers/composite/ColorLayerComposite.h
@@ -34,32 +34,32 @@ protected:
     MOZ_COUNT_DTOR(ColorLayerComposite);
     Destroy();
   }
 
 public:
   // LayerComposite Implementation
   virtual Layer* GetLayer() override { return this; }
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override
+  virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
   }
 
   virtual void Destroy() override { mDestroyed = true; }
 
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
   virtual void CleanupResources() override {};
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   virtual const char* Name() const override { return "ColorLayerComposite"; }
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_ColorLayerComposite_H */
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -92,17 +92,17 @@ static void PrintUniformityInfo(Layer* a
   }
 
   // Don't want to print a log for smaller layers
   if (aLayer->GetLocalVisibleRegion().GetBounds().width < 300 ||
       aLayer->GetLocalVisibleRegion().GetBounds().height < 300) {
     return;
   }
 
-  Matrix4x4 transform = aLayer->AsLayerComposite()->GetShadowBaseTransform();
+  Matrix4x4 transform = aLayer->AsHostLayer()->GetShadowBaseTransform();
   if (!transform.Is2D()) {
     return;
   }
 
   Point translation = transform.As2D().GetTranslation();
   LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
   PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
 #endif
@@ -136,17 +136,17 @@ ContainerPrepare(ContainerT* aContainer,
 
   /**
    * Determine which layers to draw.
    */
   AutoTArray<Layer*, 12> children;
   aContainer->SortChildrenBy3DZOrder(children);
 
   for (uint32_t i = 0; i < children.Length(); i++) {
-    LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->ImplData());
+    LayerComposite* layerToRender = static_cast<LayerComposite*>(children.ElementAt(i)->AsHostLayer());
 
     RenderTargetIntRect clipRect = layerToRender->GetLayer()->
         CalculateScissorRect(aClipRect);
 
     if (layerToRender->GetLayer()->IsBackfaceHidden()) {
       continue;
     }
 
@@ -601,30 +601,30 @@ ContainerLayerComposite::~ContainerLayer
   }
 }
 
 void
 ContainerLayerComposite::Destroy()
 {
   if (!mDestroyed) {
     while (mFirstChild) {
-      static_cast<LayerComposite*>(GetFirstChild()->ImplData())->Destroy();
+      GetFirstChildComposite()->Destroy();
       RemoveChild(mFirstChild);
     }
     mDestroyed = true;
   }
 }
 
 LayerComposite*
 ContainerLayerComposite::GetFirstChildComposite()
 {
   if (!mFirstChild) {
     return nullptr;
    }
-  return static_cast<LayerComposite*>(mFirstChild->ImplData());
+  return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
 ContainerLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
 {
   ContainerRender(this, mCompositeManager, aClipRect);
 }
 
@@ -636,18 +636,17 @@ ContainerLayerComposite::Prepare(const R
 
 void
 ContainerLayerComposite::CleanupResources()
 {
   mLastIntermediateSurface = nullptr;
   mPrepared = nullptr;
 
   for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
-    LayerComposite* layerToCleanup = static_cast<LayerComposite*>(l->ImplData());
-    layerToCleanup->CleanupResources();
+    static_cast<LayerComposite*>(l->AsHostLayer())->CleanupResources();
   }
 }
 
 RefLayerComposite::RefLayerComposite(LayerManagerComposite* aManager)
   : RefLayer(aManager, nullptr)
   , LayerComposite(aManager)
 {
   mImplData = static_cast<LayerComposite*>(this);
@@ -666,17 +665,17 @@ RefLayerComposite::Destroy()
 }
 
 LayerComposite*
 RefLayerComposite::GetFirstChildComposite()
 {
   if (!mFirstChild) {
     return nullptr;
    }
-  return static_cast<LayerComposite*>(mFirstChild->ImplData());
+  return static_cast<LayerComposite*>(mFirstChild->AsHostLayer());
 }
 
 void
 RefLayerComposite::RenderLayer(const gfx::IntRect& aClipRect)
 {
   ContainerRender(this, mCompositeManager, aClipRect);
 }
 
--- a/gfx/layers/composite/ContainerLayerComposite.h
+++ b/gfx/layers/composite/ContainerLayerComposite.h
@@ -58,24 +58,24 @@ public:
 
 protected:
   ~ContainerLayerComposite();
 
 public:
   // LayerComposite Implementation
   virtual Layer* GetLayer() override { return this; }
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override
+  virtual void SetLayerManager(HostLayerManager* aManager) override
   {
     LayerComposite::SetLayerManager(aManager);
     mManager = aManager;
     mLastIntermediateSurface = nullptr;
 
     for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
-      LayerComposite* child = l->AsLayerComposite();
+      HostLayer* child = l->AsHostLayer();
       child->SetLayerManager(aManager);
     }
   }
 
   virtual void Destroy() override;
 
   LayerComposite* GetFirstChildComposite() override;
 
@@ -84,17 +84,17 @@ public:
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   // container layers don't use a compositable
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   // If the layer is marked as scale-to-resolution, add a post-scale
   // to the layer's transform equal to the pres shell resolution we're
   // scaling to. This cancels out the post scale of '1 / resolution'
   // added by Layout. TODO: It would be nice to get rid of both of these
@@ -168,17 +168,17 @@ public:
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void CleanupResources() override;
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   // ref layers don't use a compositable
   CompositableHost* GetCompositableHost() override { return nullptr; }
 
   virtual const char* Name() const override { return "RefLayerComposite"; }
   UniquePtr<PreparedData> mPrepared;
   RefPtr<CompositingRenderTarget> mLastIntermediateSurface;
 };
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -344,17 +344,17 @@ ImageHost::Composite(LayerComposite* aLa
     if (effect->mType == EffectTypes::NV12) {
       diagnosticFlags |= DiagnosticFlags::NV12;
     } else if (effect->mType == EffectTypes::YCBCR) {
       diagnosticFlags |= DiagnosticFlags::YCBCR;
     }
 
     if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
       if (mImageContainer) {
-        aLayer->GetLayerManager()->
+        static_cast<LayerManagerComposite*>(aLayer->GetLayerManager())->
             AppendImageCompositeNotification(ImageCompositeNotification(
                 mImageContainer, nullptr,
                 img->mTimeStamp, GetCompositor()->GetCompositionTime(),
                 img->mFrameID, img->mProducerID));
       }
       mLastFrameID = img->mFrameID;
       mLastProducerID = img->mProducerID;
     }
--- a/gfx/layers/composite/ImageLayerComposite.cpp
+++ b/gfx/layers/composite/ImageLayerComposite.cpp
@@ -75,17 +75,17 @@ ImageLayerComposite::GetRenderState()
 
 Layer*
 ImageLayerComposite::GetLayer()
 {
   return this;
 }
 
 void
-ImageLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+ImageLayerComposite::SetLayerManager(HostLayerManager* aManager)
 {
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mImageHost) {
     mImageHost->SetCompositor(mCompositor);
   }
 }
 
--- a/gfx/layers/composite/ImageLayerComposite.h
+++ b/gfx/layers/composite/ImageLayerComposite.h
@@ -38,29 +38,29 @@ public:
   virtual LayerRenderState GetRenderState() override;
 
   virtual void Disconnect() override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
   virtual Layer* GetLayer() override;
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+  virtual void SetLayerManager(HostLayerManager* aManager) override;
 
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
 
   virtual void ComputeEffectiveTransforms(const mozilla::gfx::Matrix4x4& aTransformToSurface) override;
 
   virtual void CleanupResources() override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   virtual const char* Name() const override { return "ImageLayerComposite"; }
 
   virtual bool IsOpaque() override;
 
   virtual nsIntRegion GetFullyRenderedRegion() override;
 
 protected:
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -105,31 +105,37 @@ LayerManagerComposite::ClearCachedResour
   }
 
   ClearSubtree(subtree);
   // FIXME [bjacob]
   // XXX the old LayerManagerOGL code had a mMaybeInvalidTree that it set to true here.
   // Do we need that?
 }
 
+HostLayerManager::HostLayerManager()
+  : mDebugOverlayWantsNextFrame(false)
+  , mWarningLevel(0.0f)
+  , mWindowOverlayChanged(false)
+  , mLastPaintTime(TimeDuration::Forever())
+  , mRenderStartTime(TimeStamp::Now())
+{}
+
+HostLayerManager::~HostLayerManager()
+{}
+
 /**
  * LayerManagerComposite
  */
 LayerManagerComposite::LayerManagerComposite(Compositor* aCompositor)
-: mWarningLevel(0.0f)
-, mUnusedApzTransformWarning(false)
+: mUnusedApzTransformWarning(false)
 , mDisabledApzWarning(false)
 , mCompositor(aCompositor)
 , mInTransaction(false)
 , mIsCompositorReady(false)
-, mDebugOverlayWantsNextFrame(false)
 , mGeometryChanged(true)
-, mWindowOverlayChanged(false)
-, mLastPaintTime(TimeDuration::Forever())
-, mRenderStartTime(TimeStamp::Now())
 {
   mTextRenderer = new TextRenderer(aCompositor);
   MOZ_ASSERT(aCompositor);
 }
 
 LayerManagerComposite::~LayerManagerComposite()
 {
   Destroy();
@@ -227,17 +233,17 @@ LayerManagerComposite::PostProcessLayers
   if (aLayer->Extend3DContext()) {
     // For layers participating 3D rendering context, their visible
     // region should be empty (invisible), so we pass through them
     // without doing anything.
 
     // Direct children of the establisher may have a clip, becaue the
     // item containing it; ex. of nsHTMLScrollFrame, may give it one.
     Maybe<ParentLayerIntRect> layerClip =
-      aLayer->AsLayerComposite()->GetShadowClipRect();
+      aLayer->AsHostLayer()->GetShadowClipRect();
     Maybe<ParentLayerIntRect> ancestorClipForChildren =
       IntersectMaybeRects(layerClip, aClipFromAncestors);
     MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
                "Only direct children of the establisher could have a clip");
 
     for (Layer* child = aLayer->GetLastChild();
          child;
          child = child->GetPrevSibling()) {
@@ -261,17 +267,17 @@ LayerManagerComposite::PostProcessLayers
       integerTranslation = Some(IntPoint::Truncate(transform2d.GetTranslation()));
       localOpaque = aOpaqueRegion;
       localOpaque.MoveBy(-*integerTranslation);
     }
   }
 
   // Compute a clip that's the combination of our layer clip with the clip
   // from our ancestors.
-  LayerComposite* composite = aLayer->AsLayerComposite();
+  LayerComposite* composite = static_cast<LayerComposite*>(aLayer->AsHostLayer());
   Maybe<ParentLayerIntRect> layerClip = composite->GetShadowClipRect();
   MOZ_ASSERT(!layerClip || !aLayer->Combines3DTransformWithAncestors(),
              "The layer with a clip should not participate "
              "a 3D rendering context");
   Maybe<ParentLayerIntRect> outsideClip =
     IntersectMaybeRects(layerClip, aClipFromAncestors);
 
   // Convert the combined clip into our pre-transform coordinate space, so
@@ -490,57 +496,16 @@ LayerManagerComposite::UpdateAndRender()
 
 already_AddRefed<DrawTarget>
 LayerManagerComposite::CreateOptimalMaskDrawTarget(const IntSize &aSize)
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
 
-already_AddRefed<PaintedLayer>
-LayerManagerComposite::CreatePaintedLayer()
-{
-  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
-                       "this should only be called on the drawing side");
-  RefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
-  return layer.forget();
-}
-
-already_AddRefed<ContainerLayer>
-LayerManagerComposite::CreateContainerLayer()
-{
-  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
-                       "this should only be called on the drawing side");
-  RefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
-  return layer.forget();
-}
-
-already_AddRefed<ImageLayer>
-LayerManagerComposite::CreateImageLayer()
-{
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
-}
-
-already_AddRefed<ColorLayer>
-LayerManagerComposite::CreateColorLayer()
-{
-  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
-                       "this should only be called on the drawing side");
-  RefPtr<ColorLayer> layer = new ColorLayerComposite(this);
-  return layer.forget();
-}
-
-already_AddRefed<CanvasLayer>
-LayerManagerComposite::CreateCanvasLayer()
-{
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
-}
-
 LayerComposite*
 LayerManagerComposite::RootLayer() const
 {
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
   }
 
@@ -828,18 +793,18 @@ LayerManagerComposite::PopGroupForLayerE
 
 // Used to clear the 'mLayerComposited' flag at the beginning of each Render().
 static void
 ClearLayerFlags(Layer* aLayer) {
   ForEachNode<ForwardIterator>(
       aLayer,
       [] (Layer* layer)
       {
-        if (layer->AsLayerComposite()) {
-          layer->AsLayerComposite()->SetLayerComposited(false);
+        if (layer->AsHostLayer()) {
+          static_cast<LayerComposite*>(layer->AsHostLayer())->SetLayerComposited(false);
         }
       });
 }
 
 void
 LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion, const nsIntRegion& aOpaqueRegion)
 {
   PROFILER_LABEL("LayerManagerComposite", "Render",
@@ -1151,74 +1116,180 @@ LayerManagerComposite::RenderToPresentat
 
   RootLayer()->Prepare(RenderTargetIntRect::FromUnknownRect(clipRect));
   RootLayer()->RenderLayer(clipRect);
 
   mCompositor->EndFrame();
 }
 #endif
 
-already_AddRefed<PaintedLayerComposite>
-LayerManagerComposite::CreatePaintedLayerComposite()
+class TextLayerComposite : public TextLayer,
+                           public LayerComposite
 {
-  if (mDestroyed) {
-    NS_WARNING("Call on destroyed layer manager");
-    return nullptr;
+public:
+  explicit TextLayerComposite(LayerManagerComposite *aManager)
+    : TextLayer(aManager, nullptr)
+    , LayerComposite(aManager)
+  {
+    MOZ_COUNT_CTOR(TextLayerComposite);
+    mImplData = static_cast<LayerComposite*>(this);
+  }
+
+protected:
+  ~TextLayerComposite()
+  {
+    MOZ_COUNT_DTOR(TextLayerComposite);
+    Destroy();
   }
-  return RefPtr<PaintedLayerComposite>(new PaintedLayerComposite(this)).forget();
-}
+
+public:
+  // LayerComposite Implementation
+  virtual Layer* GetLayer() override { return this; }
+
+  virtual void SetLayerManager(HostLayerManager* aManager) override
+  {
+    LayerComposite::SetLayerManager(aManager);
+    mManager = aManager;
+  }
+
+  virtual void Destroy() override { mDestroyed = true; }
+
+  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void CleanupResources() override {};
+
+  virtual void GenEffectChain(EffectChain& aEffect) override {}
+
+  CompositableHost* GetCompositableHost() override { return nullptr; }
+
+  virtual HostLayer* AsHostLayer() override { return this; }
+
+  virtual const char* Name() const override { return "TextLayerComposite"; }
+};
 
-already_AddRefed<ContainerLayerComposite>
-LayerManagerComposite::CreateContainerLayerComposite()
+class BorderLayerComposite : public BorderLayer,
+                             public LayerComposite
 {
-  if (mDestroyed) {
-    NS_WARNING("Call on destroyed layer manager");
-    return nullptr;
+public:
+  explicit BorderLayerComposite(LayerManagerComposite *aManager)
+    : BorderLayer(aManager, nullptr)
+    , LayerComposite(aManager)
+  {
+    MOZ_COUNT_CTOR(BorderLayerComposite);
+    mImplData = static_cast<LayerComposite*>(this);
+  }
+
+protected:
+  ~BorderLayerComposite()
+  {
+    MOZ_COUNT_DTOR(BorderLayerComposite);
+    Destroy();
   }
-  return RefPtr<ContainerLayerComposite>(new ContainerLayerComposite(this)).forget();
-}
+
+public:
+  // LayerComposite Implementation
+  virtual Layer* GetLayer() override { return this; }
+
+  virtual void SetLayerManager(HostLayerManager* aManager) override
+  {
+    LayerComposite::SetLayerManager(aManager);
+    mManager = aManager;
+  }
+
+  virtual void Destroy() override { mDestroyed = true; }
 
-already_AddRefed<ImageLayerComposite>
-LayerManagerComposite::CreateImageLayerComposite()
+  virtual void RenderLayer(const gfx::IntRect& aClipRect) override {}
+  virtual void CleanupResources() override {};
+
+  virtual void GenEffectChain(EffectChain& aEffect) override {}
+
+  CompositableHost* GetCompositableHost() override { return nullptr; }
+
+  virtual HostLayer* AsHostLayer() override { return this; }
+
+  virtual const char* Name() const override { return "BorderLayerComposite"; }
+};
+
+already_AddRefed<PaintedLayer>
+LayerManagerComposite::CreatePaintedLayer()
 {
   if (mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
   }
-  return RefPtr<ImageLayerComposite>(new ImageLayerComposite(this)).forget();
+  return RefPtr<PaintedLayer>(new PaintedLayerComposite(this)).forget();
+}
+
+already_AddRefed<ContainerLayer>
+LayerManagerComposite::CreateContainerLayer()
+{
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nullptr;
+  }
+  return RefPtr<ContainerLayer>(new ContainerLayerComposite(this)).forget();
 }
 
-already_AddRefed<ColorLayerComposite>
-LayerManagerComposite::CreateColorLayerComposite()
+already_AddRefed<ImageLayer>
+LayerManagerComposite::CreateImageLayer()
+{
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nullptr;
+  }
+  return RefPtr<ImageLayer>(new ImageLayerComposite(this)).forget();
+}
+
+already_AddRefed<ColorLayer>
+LayerManagerComposite::CreateColorLayer()
 {
   if (LayerManagerComposite::mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
   }
-  return RefPtr<ColorLayerComposite>(new ColorLayerComposite(this)).forget();
+  return RefPtr<ColorLayer>(new ColorLayerComposite(this)).forget();
 }
 
-already_AddRefed<CanvasLayerComposite>
-LayerManagerComposite::CreateCanvasLayerComposite()
+already_AddRefed<CanvasLayer>
+LayerManagerComposite::CreateCanvasLayer()
+{
+  if (LayerManagerComposite::mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nullptr;
+  }
+  return RefPtr<CanvasLayer>(new CanvasLayerComposite(this)).forget();
+}
+
+already_AddRefed<RefLayer>
+LayerManagerComposite::CreateRefLayer()
 {
   if (LayerManagerComposite::mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
   }
-  return RefPtr<CanvasLayerComposite>(new CanvasLayerComposite(this)).forget();
+  return RefPtr<RefLayer>(new RefLayerComposite(this)).forget();
 }
 
-already_AddRefed<RefLayerComposite>
-LayerManagerComposite::CreateRefLayerComposite()
+already_AddRefed<TextLayer>
+LayerManagerComposite::CreateTextLayer()
 {
   if (LayerManagerComposite::mDestroyed) {
     NS_WARNING("Call on destroyed layer manager");
     return nullptr;
   }
-  return RefPtr<RefLayerComposite>(new RefLayerComposite(this)).forget();
+  return RefPtr<TextLayer>(new TextLayerComposite(this)).forget();
+}
+
+already_AddRefed<BorderLayer>
+LayerManagerComposite::CreateBorderLayer()
+{
+  if (LayerManagerComposite::mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return nullptr;
+  }
+  return RefPtr<BorderLayer>(new BorderLayerComposite(this)).forget();
 }
 
 LayerManagerComposite::AutoAddMaskEffect::AutoAddMaskEffect(Layer* aMaskLayer,
                                                             EffectChain& aEffects)
   : mCompositable(nullptr), mFailed(false)
 {
   if (!aMaskLayer) {
     return;
@@ -1250,21 +1321,19 @@ void
 LayerManagerComposite::ChangeCompositor(Compositor* aNewCompositor)
 {
   mCompositor = aNewCompositor;
   mTextRenderer = new TextRenderer(aNewCompositor);
   mTwoPassTmpTarget = nullptr;
 }
 
 LayerComposite::LayerComposite(LayerManagerComposite *aManager)
-  : mCompositeManager(aManager)
+  : HostLayer(aManager)
+  , mCompositeManager(aManager)
   , mCompositor(aManager->GetCompositor())
-  , mShadowOpacity(1.0)
-  , mShadowTransformSetByAnimation(false)
-  , mShadowOpacitySetByAnimation(false)
   , mDestroyed(false)
   , mLayerComposited(false)
 { }
 
 LayerComposite::~LayerComposite()
 {
 }
 
@@ -1300,20 +1369,21 @@ void
 LayerManagerComposite::NotifyShadowTreeTransaction()
 {
   if (mFPS) {
     mFPS->NotifyShadowTreeTransaction();
   }
 }
 
 void
-LayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+LayerComposite::SetLayerManager(HostLayerManager* aManager)
 {
-  mCompositeManager = aManager;
-  mCompositor = aManager->GetCompositor();
+  HostLayer::SetLayerManager(aManager);
+  mCompositeManager = static_cast<LayerManagerComposite*>(aManager);
+  mCompositor = mCompositeManager->GetCompositor();
 }
 
 bool
 LayerManagerComposite::AsyncPanZoomEnabled() const
 {
   if (CompositorBridgeParent* bridge = mCompositor->GetCompositorBridgeParent()) {
     return bridge->AsyncPanZoomEnabled();
   }
@@ -1331,17 +1401,17 @@ LayerComposite::GetFullyRenderedRegion()
     shadowVisibleRegion.And(shadowVisibleRegion, tiled->GetValidRegion());
     return shadowVisibleRegion;
   } else {
     return GetShadowVisibleRegion().ToUnknownRegion();
   }
 }
 
 Matrix4x4
-LayerComposite::GetShadowTransform() {
+HostLayer::GetShadowTransform() {
   Matrix4x4 transform = mShadowTransform;
   Layer* layer = GetLayer();
 
   transform.PostScale(layer->GetPostXScale(), layer->GetPostYScale(), 1.0f);
   if (const ContainerLayer* c = layer->AsContainerLayer()) {
     transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f);
   }
 
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -60,17 +60,131 @@ class RefLayerComposite;
 class PaintedLayerComposite;
 class TextRenderer;
 class CompositingRenderTarget;
 struct FPSState;
 class PaintCounter;
 
 static const int kVisualWarningDuration = 150; // ms
 
-class LayerManagerComposite final : public LayerManager
+// An implementation of LayerManager that acts as a pair with ClientLayerManager
+// and is mirrored across IPDL. This gets managed/updated by LayerTransactionParent.
+class HostLayerManager : public LayerManager
+{
+public:
+  HostLayerManager();
+  ~HostLayerManager();
+
+  virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override
+  {
+    MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
+  }
+
+  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override
+  {
+    MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+    return false;
+  }
+
+  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+                              void* aCallbackData,
+                              EndTransactionFlags aFlags = END_DEFAULT) override
+  {
+    MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
+  }
+
+  virtual int32_t GetMaxTextureSize() const override
+  {
+    MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
+  }
+
+  virtual LayersBackend GetBackendType() override
+  {
+    MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+  }
+  virtual void GetBackendName(nsAString& name) override
+  {
+    MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
+  }
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() = 0;
+
+
+  virtual void ForcePresent() = 0;
+  virtual void AddInvalidRegion(const nsIntRegion& aRegion) = 0;
+  virtual void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
+                                                const Maybe<uint32_t>& aPresShellId) = 0;
+  virtual void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
+                                                const CSSIntRegion& aRegion) = 0;
+
+  virtual void NotifyShadowTreeTransaction() {}
+  virtual void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
+                                              const gfx::IntRect& aRect) = 0;
+  virtual Compositor* GetCompositor() const = 0;
+  virtual void EndTransaction(const TimeStamp& aTimeStamp,
+                              EndTransactionFlags aFlags = END_DEFAULT) = 0;
+  virtual void UpdateRenderBounds(const gfx::IntRect& aRect) {}
+
+  // Called by CompositorBridgeParent when a new compositor has been created due
+  // to a device reset. The layer manager must clear any cached resources
+  // attached to the old compositor, and make a best effort at ignoring
+  // layer or texture updates against the old compositor.
+  virtual void ChangeCompositor(Compositor* aNewCompositor) = 0;
+
+  void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+  {
+    aNotifications->AppendElements(Move(mImageCompositeNotifications));
+  }
+
+  /**
+   * LayerManagerComposite provides sophisticated debug overlays
+   * that can request a next frame.
+   */
+  bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
+  void SetDebugOverlayWantsNextFrame(bool aVal)
+  { mDebugOverlayWantsNextFrame = aVal; }
+
+  /**
+   * Add an on frame warning.
+   * @param severity ranges from 0 to 1. It's used to compute the warning color.
+   */
+  void VisualFrameWarning(float severity) {
+    mozilla::TimeStamp now = TimeStamp::Now();
+    if (mWarnTime.IsNull() ||
+        severity > mWarningLevel ||
+        mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) < now) {
+      mWarnTime = now;
+      mWarningLevel = severity;
+    }
+  }
+
+  // Indicate that we need to composite even if nothing in our layers has
+  // changed, so that the widget can draw something different in its window
+  // overlay.
+  void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
+
+
+  void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+
+protected:
+  bool mDebugOverlayWantsNextFrame;
+  nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
+  // Testing property. If hardware composer is supported, this will return
+  // true if the last frame was deemed 'too complicated' to be rendered.
+  float mWarningLevel;
+  mozilla::TimeStamp mWarnTime;
+
+  bool mWindowOverlayChanged;
+  RefPtr<PaintCounter> mPaintCounter;
+  TimeDuration mLastPaintTime;
+  TimeStamp mRenderStartTime;
+};
+
+// A layer manager implementation that uses the Compositor API
+// to render layers.
+class LayerManagerComposite final : public HostLayerManager
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::IntSize IntSize;
   typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
 
 public:
   explicit LayerManagerComposite(Compositor* aCompositor);
   ~LayerManagerComposite();
@@ -94,74 +208,46 @@ public:
   /**
    * LayerManager implementation.
    */
   virtual LayerManagerComposite* AsLayerManagerComposite() override
   {
     return this;
   }
 
-  void UpdateRenderBounds(const gfx::IntRect& aRect);
+  void UpdateRenderBounds(const gfx::IntRect& aRect) override;
 
   virtual bool BeginTransaction() override;
-  virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override
-  {
-    MOZ_CRASH("GFX: Use BeginTransactionWithDrawTarget");
-    return false;
-  }
   void BeginTransactionWithDrawTarget(gfx::DrawTarget* aTarget,
-                                      const gfx::IntRect& aRect);
-
-  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override
-  {
-    MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
-    return false;
-  }
+                                      const gfx::IntRect& aRect) override;
+  void EndTransaction(const TimeStamp& aTimeStamp,
+                      EndTransactionFlags aFlags = END_DEFAULT) override;
   virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
                               void* aCallbackData,
                               EndTransactionFlags aFlags = END_DEFAULT) override
   {
     MOZ_CRASH("GFX: Use EndTransaction(aTimeStamp)");
   }
-  void EndTransaction(const TimeStamp& aTimeStamp,
-                      EndTransactionFlags aFlags = END_DEFAULT);
 
   virtual void SetRoot(Layer* aLayer) override { mRoot = aLayer; }
 
   // XXX[nrc]: never called, we should move this logic to ClientLayerManager
   // (bug 946926).
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
 
-  virtual int32_t GetMaxTextureSize() const override
-  {
-    MOZ_CRASH("GFX: Call on compositor, not LayerManagerComposite");
-  }
-
   virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
 
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+  virtual already_AddRefed<TextLayer> CreateTextLayer() override;
+  virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
-  already_AddRefed<PaintedLayerComposite> CreatePaintedLayerComposite();
-  already_AddRefed<ContainerLayerComposite> CreateContainerLayerComposite();
-  already_AddRefed<ImageLayerComposite> CreateImageLayerComposite();
-  already_AddRefed<ColorLayerComposite> CreateColorLayerComposite();
-  already_AddRefed<CanvasLayerComposite> CreateCanvasLayerComposite();
-  already_AddRefed<RefLayerComposite> CreateRefLayerComposite();
-
-  virtual LayersBackend GetBackendType() override
-  {
-    MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
-  }
-  virtual void GetBackendName(nsAString& name) override
-  {
-    MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
-  }
+  virtual already_AddRefed<RefLayer> CreateRefLayer() override;
 
   virtual bool AreComponentAlphaLayersEnabled() override;
 
   virtual already_AddRefed<DrawTarget>
     CreateOptimalMaskDrawTarget(const IntSize &aSize) override;
 
   virtual const char* Name() const override { return ""; }
 
@@ -202,83 +288,61 @@ public:
   /**
    * returns true if PlatformAllocBuffer will return a buffer that supports
    * direct texturing
    */
   static bool SupportsDirectTexturing();
 
   static void PlatformSyncBeforeReplyUpdate();
 
-  void AddInvalidRegion(const nsIntRegion& aRegion)
+  void AddInvalidRegion(const nsIntRegion& aRegion) override
   {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
   }
 
   void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
-                                        const Maybe<uint32_t>& aPresShellId)
+                                        const Maybe<uint32_t>& aPresShellId) override
   {
     for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
       if (iter.Key().mLayersId == aLayersId &&
           (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
         iter.Remove();
       }
     }
   }
 
   void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                        const CSSIntRegion& aRegion)
+                                        const CSSIntRegion& aRegion) override
   {
     CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
     MOZ_ASSERT(regionForScrollFrame);
 
     *regionForScrollFrame = aRegion;
   }
 
   CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
   {
     return mVisibleRegions.Get(aGuid);
   }
 
-  Compositor* GetCompositor() const
+  Compositor* GetCompositor() const override
   {
     return mCompositor;
   }
 
   // Called by CompositorBridgeParent when a new compositor has been created due
   // to a device reset. The layer manager must clear any cached resources
   // attached to the old compositor, and make a best effort at ignoring
   // layer or texture updates against the old compositor.
-  void ChangeCompositor(Compositor* aNewCompositor);
+  void ChangeCompositor(Compositor* aNewCompositor) override;
 
-  /**
-   * LayerManagerComposite provides sophisticated debug overlays
-   * that can request a next frame.
-   */
-  bool DebugOverlayWantsNextFrame() { return mDebugOverlayWantsNextFrame; }
-  void SetDebugOverlayWantsNextFrame(bool aVal)
-  { mDebugOverlayWantsNextFrame = aVal; }
-
-  void NotifyShadowTreeTransaction();
+  void NotifyShadowTreeTransaction() override;
 
   TextRenderer* GetTextRenderer() { return mTextRenderer; }
 
-  /**
-   * Add an on frame warning.
-   * @param severity ranges from 0 to 1. It's used to compute the warning color.
-   */
-  void VisualFrameWarning(float severity) {
-    mozilla::TimeStamp now = TimeStamp::Now();
-    if (mWarnTime.IsNull() ||
-        severity > mWarningLevel ||
-        mWarnTime + TimeDuration::FromMilliseconds(kVisualWarningDuration) < now) {
-      mWarnTime = now;
-      mWarningLevel = severity;
-    }
-  }
-
   void UnusedApzTransformWarning() {
     mUnusedApzTransformWarning = true;
   }
   void DisabledApzWarning() {
     mDisabledApzWarning = true;
   }
 
   bool AsyncPanZoomEnabled() const override;
@@ -289,29 +353,22 @@ public:
     // because that's what they mean.
     // Also when we're not drawing to the screen, DidComposite will not be
     // called to extract and send these notifications, so they might linger
     // and contain stale ImageContainerParent pointers.
     if (!mCompositor->GetTargetContext()) {
       mImageCompositeNotifications.AppendElement(aNotification);
     }
   }
-  void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotification>* aNotifications)
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
   {
-    aNotifications->AppendElements(Move(mImageCompositeNotifications));
+    return mCompositor->GetTextureFactoryIdentifier();
   }
 
-  // Indicate that we need to composite even if nothing in our layers has
-  // changed, so that the widget can draw something different in its window
-  // overlay.
-  void SetWindowOverlayChanged() { mWindowOverlayChanged = true; }
-
-  void ForcePresent() { mCompositor->ForcePresent(); }
-
-  void SetPaintTime(const TimeDuration& aPaintTime) { mLastPaintTime = aPaintTime; }
+  void ForcePresent() override { mCompositor->ForcePresent(); }
 
 private:
   /** Region we're clipping our current drawing to. */
   nsIntRegion mClippingRegion;
   gfx::IntRect mRenderBounds;
 
   /** Current root layer. */
   LayerComposite* RootLayer() const;
@@ -349,121 +406,83 @@ private:
   void PopGroupForLayerEffects(RefPtr<CompositingRenderTarget> aPreviousTarget,
                                gfx::IntRect aClipRect,
                                bool aGrayscaleEffect,
                                bool aInvertEffect,
                                float aContrastEffect);
 
   void ChangeCompositorInternal(Compositor* aNewCompositor);
 
-  float mWarningLevel;
-  mozilla::TimeStamp mWarnTime;
   bool mUnusedApzTransformWarning;
   bool mDisabledApzWarning;
   RefPtr<Compositor> mCompositor;
   UniquePtr<LayerProperties> mClonedLayerTreeProperties;
 
-  nsTArray<ImageCompositeNotification> mImageCompositeNotifications;
-
   /**
    * Context target, nullptr when drawing directly to our swap chain.
    */
   RefPtr<gfx::DrawTarget> mTarget;
   gfx::IntRect mTargetBounds;
 
   nsIntRegion mInvalidRegion;
 
   typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
                            CSSIntRegion> VisibleRegions;
   VisibleRegions mVisibleRegions;
 
   UniquePtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
-  bool mDebugOverlayWantsNextFrame;
 
   RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
   RefPtr<TextRenderer> mTextRenderer;
   bool mGeometryChanged;
-
-  bool mWindowOverlayChanged;
-  RefPtr<PaintCounter> mPaintCounter;
-  TimeDuration mLastPaintTime;
-  TimeStamp mRenderStartTime;
 };
 
 /**
- * Composite layers are for use with OMTC on the compositor thread only. There
- * must be corresponding Basic layers on the content thread. For composite
- * layers, the layer manager only maintains the layer tree, all rendering is
- * done by a Compositor (see Compositor.h). As such, composite layers are
- * platform-independent and can be used on any platform for which there is a
- * Compositor implementation.
- *
- * The composite layer tree reflects exactly the basic layer tree. To
- * composite to screen, the layer manager walks the layer tree calling render
- * methods which in turn call into their CompositableHosts' Composite methods.
- * These call Compositor::DrawQuad to do the rendering.
- *
- * Mostly, layers are updated during the layers transaction. This is done from
- * CompositableClient to CompositableHost without interacting with the layer.
- *
- * A reference to the Compositor is stored in LayerManagerComposite.
+ * Compositor layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Client layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree.
  */
-class LayerComposite
+class HostLayer
 {
 public:
-  explicit LayerComposite(LayerManagerComposite* aManager);
+  explicit HostLayer(HostLayerManager* aManager)
+    : mCompositorManager(aManager)
+    , mShadowOpacity(1.0)
+    , mShadowTransformSetByAnimation(false)
+    , mShadowOpacitySetByAnimation(false)
+  {
+  }
 
-  virtual ~LayerComposite();
+  virtual void SetLayerManager(HostLayerManager* aManager)
+  {
+    mCompositorManager = aManager;
+  }
+  HostLayerManager* GetLayerManager() const { return mCompositorManager; }
+
+
+  virtual ~HostLayer() {}
 
   virtual LayerComposite* GetFirstChildComposite()
   {
     return nullptr;
   }
 
-  /* Do NOT call this from the generic LayerComposite destructor.  Only from the
-   * concrete class destructor
-   */
-  virtual void Destroy();
-
   virtual Layer* GetLayer() = 0;
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager);
-
-  LayerManagerComposite* GetLayerManager() const { return mCompositeManager; }
-
-  /**
-   * Perform a first pass over the layer tree to render all of the intermediate
-   * surfaces that we can. This allows us to avoid framebuffer switches in the
-   * middle of our render which is inefficient especially on mobile GPUs. This
-   * must be called before RenderLayer.
-   */
-  virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
-
-  // TODO: This should also take RenderTargetIntRect like Prepare.
-  virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
-
   virtual bool SetCompositableHost(CompositableHost*)
   {
     // We must handle this gracefully, see bug 967824
     NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
     return false;
   }
   virtual CompositableHost* GetCompositableHost() = 0;
 
-  virtual void CleanupResources() = 0;
-
-  virtual void DestroyFrontBuffer() { }
-
-  void AddBlendModeEffect(EffectChain& aEffectChain);
-
-  virtual void GenEffectChain(EffectChain& aEffect) { }
-
   /**
    * The following methods are
    *
    * CONSTRUCTION PHASE ONLY
    *
    * They are analogous to the Layer interface.
    */
   void SetShadowVisibleRegion(const LayerIntRegion& aRegion)
@@ -489,79 +508,151 @@ public:
   {
     mShadowTransform = aMatrix;
   }
   void SetShadowTransformSetByAnimation(bool aSetByAnimation)
   {
     mShadowTransformSetByAnimation = aSetByAnimation;
   }
 
+  // These getters can be used anytime.
+  float GetShadowOpacity() { return mShadowOpacity; }
+  const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
+  const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
+  const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
+  gfx::Matrix4x4 GetShadowTransform();
+  bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
+  bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
+  
+  /**
+   * Return true if a checkerboarding background color needs to be drawn
+   * for this layer.
+   */
+  virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr) { return false; }
+
+protected:
+  HostLayerManager* mCompositorManager;
+
+  gfx::Matrix4x4 mShadowTransform;
+  LayerIntRegion mShadowVisibleRegion;
+  Maybe<ParentLayerIntRect> mShadowClipRect;
+  float mShadowOpacity;
+  bool mShadowTransformSetByAnimation;
+  bool mShadowOpacitySetByAnimation;
+};
+
+/**
+ * Composite layers are for use with OMTC on the compositor thread only. There
+ * must be corresponding Client layers on the content thread. For composite
+ * layers, the layer manager only maintains the layer tree, all rendering is
+ * done by a Compositor (see Compositor.h). As such, composite layers are
+ * platform-independent and can be used on any platform for which there is a
+ * Compositor implementation.
+ *
+ * The composite layer tree reflects exactly the basic layer tree. To
+ * composite to screen, the layer manager walks the layer tree calling render
+ * methods which in turn call into their CompositableHosts' Composite methods.
+ * These call Compositor::DrawQuad to do the rendering.
+ *
+ * Mostly, layers are updated during the layers transaction. This is done from
+ * CompositableClient to CompositableHost without interacting with the layer.
+ *
+ * A reference to the Compositor is stored in LayerManagerComposite.
+ */
+class LayerComposite : public HostLayer
+{
+public:
+  explicit LayerComposite(LayerManagerComposite* aManager);
+
+  virtual ~LayerComposite();
+
+  virtual void SetLayerManager(HostLayerManager* aManager);
+
+  virtual LayerComposite* GetFirstChildComposite()
+  {
+    return nullptr;
+  }
+
+  /* Do NOT call this from the generic LayerComposite destructor.  Only from the
+   * concrete class destructor
+   */
+  virtual void Destroy();
+
+  /**
+   * Perform a first pass over the layer tree to render all of the intermediate
+   * surfaces that we can. This allows us to avoid framebuffer switches in the
+   * middle of our render which is inefficient especially on mobile GPUs. This
+   * must be called before RenderLayer.
+   */
+  virtual void Prepare(const RenderTargetIntRect& aClipRect) {}
+
+  // TODO: This should also take RenderTargetIntRect like Prepare.
+  virtual void RenderLayer(const gfx::IntRect& aClipRect) = 0;
+
+  virtual bool SetCompositableHost(CompositableHost*)
+  {
+    // We must handle this gracefully, see bug 967824
+    NS_WARNING("called SetCompositableHost for a layer type not accepting a compositable");
+    return false;
+  }
+
+  virtual void CleanupResources() = 0;
+
+  virtual void DestroyFrontBuffer() { }
+
+  void AddBlendModeEffect(EffectChain& aEffectChain);
+
+  virtual void GenEffectChain(EffectChain& aEffect) { }
+
   void SetLayerComposited(bool value)
   {
     mLayerComposited = value;
   }
 
   void SetClearRect(const gfx::IntRect& aRect)
   {
     mClearRect = aRect;
   }
 
-  // These getters can be used anytime.
-  float GetShadowOpacity() { return mShadowOpacity; }
-  const Maybe<ParentLayerIntRect>& GetShadowClipRect() { return mShadowClipRect; }
-  const LayerIntRegion& GetShadowVisibleRegion() { return mShadowVisibleRegion; }
-  const gfx::Matrix4x4& GetShadowBaseTransform() { return mShadowTransform; }
-  gfx::Matrix4x4 GetShadowTransform();
-  bool GetShadowTransformSetByAnimation() { return mShadowTransformSetByAnimation; }
-  bool GetShadowOpacitySetByAnimation() { return mShadowOpacitySetByAnimation; }
   bool HasLayerBeenComposited() { return mLayerComposited; }
   gfx::IntRect GetClearRect() { return mClearRect; }
 
   // Returns false if the layer is attached to an older compositor.
   bool HasStaleCompositor() const;
 
   /**
    * Return the part of the visible region that has been fully rendered.
    * While progressive drawing is in progress this region will be
    * a subset of the shadow visible region.
    */
   virtual nsIntRegion GetFullyRenderedRegion();
 
-  /**
-   * Return true if a checkerboarding background color needs to be drawn
-   * for this layer.
-   */
-  bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
+  virtual bool NeedToDrawCheckerboarding(gfx::Color* aOutCheckerboardingColor = nullptr);
 
 protected:
-  gfx::Matrix4x4 mShadowTransform;
-  LayerIntRegion mShadowVisibleRegion;
-  Maybe<ParentLayerIntRect> mShadowClipRect;
   LayerManagerComposite* mCompositeManager;
+
   RefPtr<Compositor> mCompositor;
-  float mShadowOpacity;
-  bool mShadowTransformSetByAnimation;
-  bool mShadowOpacitySetByAnimation;
   bool mDestroyed;
   bool mLayerComposited;
   gfx::IntRect mClearRect;
 };
 
 // Render aLayer using aCompositor and apply all mask layers of aLayer: The
 // layer's own mask layer (aLayer->GetMaskLayer()), and any ancestor mask
 // layers.
 // If more than one mask layer needs to be applied, we use intermediate surfaces
 // (CompositingRenderTargets) for rendering, applying one mask layer at a time.
 // Callers need to provide a callback function aRenderCallback that does the
 // actual rendering of the source. It needs to have the following form:
 // void (EffectChain& effectChain, const Rect& clipRect)
 // aRenderCallback is called exactly once, inside this function, unless aLayer's
 // visible region is completely clipped out (in that case, aRenderCallback won't
 // be called at all).
-// This function calls aLayer->AsLayerComposite()->AddBlendModeEffect for the
+// This function calls aLayer->AsHostLayer()->AddBlendModeEffect for the
 // final rendering pass.
 //
 // (This function should really live in LayerManagerComposite.cpp, but we
 // need to use templates for passing lambdas until bug 1164522 is resolved.)
 template<typename RenderCallbackType>
 void
 RenderWithAllMasks(Layer* aLayer, Compositor* aCompositor,
                    const gfx::IntRect& aClipRect,
@@ -584,17 +675,17 @@ RenderWithAllMasks(Layer* aLayer, Compos
     // no mask layers at all
   }
 
   if (maskLayerCount <= 1) {
     // This is the common case. Render in one pass and return.
     EffectChain effectChain(aLayer);
     LayerManagerComposite::AutoAddMaskEffect
       autoMaskEffect(firstMask, effectChain);
-    aLayer->AsLayerComposite()->AddBlendModeEffect(effectChain);
+    static_cast<LayerComposite*>(aLayer->AsHostLayer())->AddBlendModeEffect(effectChain);
     aRenderCallback(effectChain, aClipRect);
     return;
   }
 
   // We have multiple mask layers.
   // We split our list of mask layers into three parts:
   //  (1) The first mask
   //  (2) The list of intermediate masks (every mask except first and last)
@@ -660,17 +751,17 @@ RenderWithAllMasks(Layer* aLayer, Compos
 
   // Apply the final mask, rendering into originalTarget.
   EffectChain finalEffectChain(aLayer);
   finalEffectChain.mPrimaryEffect = new EffectRenderTarget(previousTarget);
   Layer* finalMask = aLayer->GetAncestorMaskLayerAt(ancestorMaskLayerCount - 1);
 
   // The blend mode needs to be applied in this final step, because this is
   // where we're blending with the actual background (which is in originalTarget).
-  aLayer->AsLayerComposite()->AddBlendModeEffect(finalEffectChain);
+  static_cast<LayerComposite*>(aLayer->AsHostLayer())->AddBlendModeEffect(finalEffectChain);
   LayerManagerComposite::AutoAddMaskEffect autoMaskEffect(finalMask, finalEffectChain);
   if (!autoMaskEffect.Failed()) {
     aCompositor->DrawQuad(gfx::Rect(surfaceRect), aClipRect,
                           finalEffectChain, 1.0, gfx::Matrix4x4());
   }
 }
 
 } // namespace layers
--- a/gfx/layers/composite/PaintedLayerComposite.cpp
+++ b/gfx/layers/composite/PaintedLayerComposite.cpp
@@ -74,17 +74,17 @@ PaintedLayerComposite::Destroy()
 
 Layer*
 PaintedLayerComposite::GetLayer()
 {
   return this;
 }
 
 void
-PaintedLayerComposite::SetLayerManager(LayerManagerComposite* aManager)
+PaintedLayerComposite::SetLayerManager(HostLayerManager* aManager)
 {
   LayerComposite::SetLayerManager(aManager);
   mManager = aManager;
   if (mBuffer && mCompositor) {
     mBuffer->SetCompositor(mCompositor);
   }
 }
 
--- a/gfx/layers/composite/PaintedLayerComposite.h
+++ b/gfx/layers/composite/PaintedLayerComposite.h
@@ -44,40 +44,33 @@ public:
   virtual LayerRenderState GetRenderState() override;
 
   CompositableHost* GetCompositableHost() override;
 
   virtual void Destroy() override;
 
   virtual Layer* GetLayer() override;
 
-  virtual void SetLayerManager(LayerManagerComposite* aManager) override;
+  virtual void SetLayerManager(HostLayerManager* aManager) override;
 
   virtual void RenderLayer(const gfx::IntRect& aClipRect) override;
 
   virtual void CleanupResources() override;
 
   virtual void GenEffectChain(EffectChain& aEffect) override;
 
   virtual bool SetCompositableHost(CompositableHost* aHost) override;
 
-  virtual LayerComposite* AsLayerComposite() override { return this; }
+  virtual HostLayer* AsHostLayer() override { return this; }
 
   virtual void InvalidateRegion(const nsIntRegion& aRegion) override
   {
     NS_RUNTIMEABORT("PaintedLayerComposites can't fill invalidated regions");
   }
 
-  void SetValidRegion(const nsIntRegion& aRegion)
-  {
-    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ValidRegion", this));
-    mValidRegion = aRegion;
-    Mutated();
-  }
-
   const virtual gfx::TiledIntRegion& GetInvalidRegion() override;
 
   MOZ_LAYER_DECL_NAME("PaintedLayerComposite", TYPE_PAINTED)
 
 protected:
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -850,24 +850,24 @@ CompositorBridgeParent::SetShadowPropert
         if (Layer* maskLayer = layer->GetMaskLayer()) {
           SetShadowProperties(maskLayer);
         }
         for (size_t i = 0; i < layer->GetAncestorMaskLayerCount(); i++) {
           SetShadowProperties(layer->GetAncestorMaskLayerAt(i));
         }
 
         // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
-        LayerComposite* layerComposite = layer->AsLayerComposite();
+        HostLayer* layerCompositor = layer->AsHostLayer();
         // Set the layerComposite's base transform to the layer's base transform.
-        layerComposite->SetShadowBaseTransform(layer->GetBaseTransform());
-        layerComposite->SetShadowTransformSetByAnimation(false);
-        layerComposite->SetShadowVisibleRegion(layer->GetVisibleRegion());
-        layerComposite->SetShadowClipRect(layer->GetClipRect());
-        layerComposite->SetShadowOpacity(layer->GetOpacity());
-        layerComposite->SetShadowOpacitySetByAnimation(false);
+        layerCompositor->SetShadowBaseTransform(layer->GetBaseTransform());
+        layerCompositor->SetShadowTransformSetByAnimation(false);
+        layerCompositor->SetShadowVisibleRegion(layer->GetVisibleRegion());
+        layerCompositor->SetShadowClipRect(layer->GetClipRect());
+        layerCompositor->SetShadowOpacity(layer->GetOpacity());
+        layerCompositor->SetShadowOpacitySetByAnimation(false);
       }
     );
 }
 
 void
 CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
@@ -1154,17 +1154,19 @@ CompositorBridgeParent::ShadowLayersUpda
 {
   ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
 
   // Instruct the LayerManager to update its render bounds now. Since all the orientation
   // change, dimension change would be done at the stage, update the size here is free of
   // race condition.
   mLayerManager->UpdateRenderBounds(aTargetConfig.naturalBounds());
   mLayerManager->SetRegionToClear(aTargetConfig.clearRegion());
-  mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation());
+  if (mLayerManager->GetCompositor()) {
+    mLayerManager->GetCompositor()->SetScreenRotation(aTargetConfig.rotation());
+  }
 
   mCompositionManager->Updated(aIsFirstPaint, aTargetConfig, aPaintSyncId);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
   if (mApzcTreeManager && !aIsRepeatTransaction && aHitTestUpdate) {
     AutoResolveRefLayers resolve(mCompositionManager);
 
@@ -1420,17 +1422,17 @@ CompositorBridgeParent::AllocPLayerTrans
     LayerTransactionParent* p = new LayerTransactionParent(nullptr, this, 0);
     p->AddIPDLReference();
     return p;
   }
 
   mCompositionManager = new AsyncCompositionManager(mLayerManager);
   *aSuccess = true;
 
-  *aTextureFactoryIdentifier = mCompositor->GetTextureFactoryIdentifier();
+  *aTextureFactoryIdentifier = mLayerManager->GetTextureFactoryIdentifier();
   LayerTransactionParent* p = new LayerTransactionParent(mLayerManager, this, 0);
   p->AddIPDLReference();
   return p;
 }
 
 bool
 CompositorBridgeParent::DeallocPLayerTransactionParent(PLayerTransactionParent* actor)
 {
@@ -1522,17 +1524,17 @@ CompositorBridgeParent::RecvAdoptChild(c
   APZCTreeManagerParent* parent;
   {
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     NotifyChildCreated(child);
     if (sIndirectLayerTrees[child].mLayerTree) {
       sIndirectLayerTrees[child].mLayerTree->mLayerManager = mLayerManager;
     }
     if (sIndirectLayerTrees[child].mRoot) {
-      sIndirectLayerTrees[child].mRoot->AsLayerComposite()->SetLayerManager(mLayerManager);
+      sIndirectLayerTrees[child].mRoot->AsHostLayer()->SetLayerManager(static_cast<HostLayerManager*>(mLayerManager.get()));
     }
     parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
   }
 
   if (mApzcTreeManager && parent) {
     parent->ChildAdopted(mApzcTreeManager);
   }
   return IPC_OK();
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -59,17 +59,17 @@ class Shmem;
 namespace layers {
 
 class APZCTreeManager;
 class APZCTreeManagerParent;
 class AsyncCompositionManager;
 class Compositor;
 class CompositorBridgeParent;
 class CompositorVsyncScheduler;
-class LayerManagerComposite;
+class HostLayerManager;
 class LayerTransactionParent;
 class PAPZParent;
 class CrossProcessCompositorBridgeParent;
 class CompositorThreadHolder;
 class InProcessCompositorSession;
 
 struct ScopedLayerTreeRegistration
 {
@@ -354,17 +354,17 @@ public:
 
   struct LayerTreeState {
     LayerTreeState();
     ~LayerTreeState();
     RefPtr<Layer> mRoot;
     RefPtr<GeckoContentController> mController;
     APZCTreeManagerParent* mApzcTreeManagerParent;
     RefPtr<CompositorBridgeParent> mParent;
-    LayerManagerComposite* mLayerManager;
+    HostLayerManager* mLayerManager;
     // Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share
     // their FrameMetrics with the corresponding child process that holds
     // the PCompositorBridgeChild
     CrossProcessCompositorBridgeParent* mCrossProcessParent;
     TargetConfig mTargetConfig;
     APZTestData mApzTestData;
     LayerTransactionParent* mLayerTree;
     nsTArray<PluginWindowData> mPluginData;
@@ -542,17 +542,17 @@ protected:
 
   void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
 
   // The indirect layer tree lock must be held before calling this function.
   // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
   template <typename Lambda>
   inline void ForEachIndirectLayerTree(const Lambda& aCallback);
 
-  RefPtr<LayerManagerComposite> mLayerManager;
+  RefPtr<HostLayerManager> mLayerManager;
   RefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
   widget::CompositorWidget* mWidget;
   TimeStamp mTestTime;
   CSSToLayoutDeviceScale mScale;
   TimeDuration mVsyncRate;
   bool mIsTesting;
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -75,17 +75,17 @@ CrossProcessCompositorBridgeParent::Allo
   CompositorBridgeParent::LayerTreeState* state = nullptr;
   LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
   if (sIndirectLayerTrees.end() != itr) {
     state = &itr->second;
   }
 
   if (state && state->mLayerManager) {
     state->mCrossProcessParent = this;
-    LayerManagerComposite* lm = state->mLayerManager;
+    HostLayerManager* lm = state->mLayerManager;
     *aTextureFactoryIdentifier = lm->GetCompositor()->GetTextureFactoryIdentifier();
     *aSuccess = true;
     LayerTransactionParent* p = new LayerTransactionParent(lm, this, aId);
     p->AddIPDLReference();
     sIndirectLayerTrees[aId].mLayerTree = p;
     p->SetPendingCompositorUpdates(state->mPendingCompositorUpdates);
     return p;
   }
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -135,17 +135,17 @@ ShadowContainer(const OpRaiseToTopChild&
 static ShadowLayerParent*
 ShadowChild(const OpRaiseToTopChild& op)
 {
   return cast(op.childLayerParent());
 }
 
 //--------------------------------------------------
 // LayerTransactionParent
-LayerTransactionParent::LayerTransactionParent(LayerManagerComposite* aManager,
+LayerTransactionParent::LayerTransactionParent(HostLayerManager* aManager,
                                                CompositorBridgeParentBase* aBridge,
                                                uint64_t aId)
   : mLayerManager(aManager)
   , mCompositorBridge(aBridge)
   , mId(aId)
   , mChildEpoch(0)
   , mParentEpoch(0)
   , mPendingTransaction(0)
@@ -281,90 +281,108 @@ LayerTransactionParent::RecvUpdate(Infal
     AutoResolveRefLayers resolve(mCompositorBridge->GetCompositionManager(this));
     layer_manager()->BeginTransaction();
   }
 
   // not all edits require an update to the hit testing tree
   bool updateHitTestingTree = false;
 
   for (EditArray::index_type i = 0; i < cset.Length(); ++i) {
-    const Edit& edit = cset[i];
+    Edit& edit = cset[i];
 
     switch (edit.type()) {
     // Create* ops
     case Edit::TOpCreatePaintedLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreatePaintedLayer"));
 
-      RefPtr<PaintedLayerComposite> layer =
-        layer_manager()->CreatePaintedLayerComposite();
+      RefPtr<PaintedLayer> layer =
+        layer_manager()->CreatePaintedLayer();
       AsLayerComposite(edit.get_OpCreatePaintedLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
     case Edit::TOpCreateContainerLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer"));
 
-      RefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayerComposite();
+      RefPtr<ContainerLayer> layer = layer_manager()->CreateContainerLayer();
       AsLayerComposite(edit.get_OpCreateContainerLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
     case Edit::TOpCreateImageLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer"));
 
-      RefPtr<ImageLayerComposite> layer =
-        layer_manager()->CreateImageLayerComposite();
+      RefPtr<ImageLayer> layer =
+        layer_manager()->CreateImageLayer();
       AsLayerComposite(edit.get_OpCreateImageLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
     case Edit::TOpCreateColorLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer"));
 
-      RefPtr<ColorLayerComposite> layer = layer_manager()->CreateColorLayerComposite();
+      RefPtr<ColorLayer> layer = layer_manager()->CreateColorLayer();
       AsLayerComposite(edit.get_OpCreateColorLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
+    case Edit::TOpCreateTextLayer: {
+      MOZ_LAYERS_LOG(("[ParentSide] CreateTextLayer"));
+
+      RefPtr<TextLayer> layer = layer_manager()->CreateTextLayer();
+      AsLayerComposite(edit.get_OpCreateTextLayer())->Bind(layer);
+
+      updateHitTestingTree = true;
+      break;
+    }
+    case Edit::TOpCreateBorderLayer: {
+      MOZ_LAYERS_LOG(("[ParentSide] CreateTextLayer"));
+
+      RefPtr<BorderLayer> layer = layer_manager()->CreateBorderLayer();
+      AsLayerComposite(edit.get_OpCreateBorderLayer())->Bind(layer);
+
+      updateHitTestingTree = true;
+      break;
+    }
     case Edit::TOpCreateCanvasLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer"));
 
-      RefPtr<CanvasLayerComposite> layer =
-        layer_manager()->CreateCanvasLayerComposite();
+      RefPtr<CanvasLayer> layer =
+        layer_manager()->CreateCanvasLayer();
       AsLayerComposite(edit.get_OpCreateCanvasLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
     case Edit::TOpCreateRefLayer: {
       MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer"));
 
-      RefPtr<RefLayerComposite> layer =
-        layer_manager()->CreateRefLayerComposite();
+      RefPtr<RefLayer> layer =
+        layer_manager()->CreateRefLayer();
       AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer);
 
       updateHitTestingTree = true;
       break;
     }
 
     // Attributes
     case Edit::TOpSetLayerAttributes: {
       MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
 
-      const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
+      OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
       ShadowLayerParent* layerParent = AsLayerComposite(osla);
       Layer* layer = layerParent->AsLayer();
       if (!layer) {
         return IPC_FAIL_NO_REASON(this);
       }
-      const LayerAttributes& attrs = osla.attrs();
+      LayerAttributes& attrs = osla.attrs();
 
       const CommonLayerAttributes& common = attrs.common();
       layer->SetLayerBounds(common.layerBounds());
       layer->SetVisibleRegion(common.visibleRegion());
       layer->SetEventRegions(common.eventRegions());
       layer->SetContentFlags(common.contentFlags());
       layer->SetOpacity(common.opacity());
       layer->SetClipRect(common.useClipRect() ? Some(common.clipRect()) : Nothing());
@@ -407,89 +425,114 @@ LayerTransactionParent::RecvUpdate(Infal
       nsTArray<RefPtr<Layer>> maskLayers;
       for (size_t i = 0; i < common.ancestorMaskLayersParent().Length(); i++) {
         Layer* maskLayer = cast(common.ancestorMaskLayersParent().ElementAt(i))->AsLayer();
         maskLayers.AppendElement(maskLayer);
       }
       layer->SetAncestorMaskLayers(maskLayers);
 
       typedef SpecificLayerAttributes Specific;
-      const SpecificLayerAttributes& specific = attrs.specific();
+      SpecificLayerAttributes& specific = attrs.specific();
       switch (specific.type()) {
       case Specific::Tnull_t:
         break;
 
       case Specific::TPaintedLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   painted layer"));
 
-        PaintedLayerComposite* paintedLayer = layerParent->AsPaintedLayerComposite();
+        PaintedLayer* paintedLayer = layerParent->AsPaintedLayer();
         if (!paintedLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         const PaintedLayerAttributes& attrs =
           specific.get_PaintedLayerAttributes();
 
         paintedLayer->SetValidRegion(attrs.validRegion());
 
         break;
       }
       case Specific::TContainerLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   container layer"));
 
-        ContainerLayerComposite* containerLayer = layerParent->AsContainerLayerComposite();
+        ContainerLayer* containerLayer = layerParent->AsContainerLayer();
         if (!containerLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         const ContainerLayerAttributes& attrs =
           specific.get_ContainerLayerAttributes();
         containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
         containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
         containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
                                              attrs.presShellResolution());
         containerLayer->SetEventRegionsOverride(attrs.eventRegionsOverride());
 
         break;
       }
       case Specific::TColorLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   color layer"));
 
-        ColorLayerComposite* colorLayer = layerParent->AsColorLayerComposite();
+        ColorLayer* colorLayer = layerParent->AsColorLayer();
         if (!colorLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         colorLayer->SetColor(specific.get_ColorLayerAttributes().color().value());
         colorLayer->SetBounds(specific.get_ColorLayerAttributes().bounds());
         break;
       }
+      case Specific::TTextLayerAttributes: {
+        MOZ_LAYERS_LOG(("[ParentSide]   text layer"));
+
+        TextLayer* textLayer = layerParent->AsTextLayer();
+        if (!textLayer) {
+          return IPC_FAIL_NO_REASON(this);
+        }
+        textLayer->SetBounds(specific.get_TextLayerAttributes().bounds());
+        textLayer->SetGlyphs(Move(specific.get_TextLayerAttributes().glyphs()));
+        textLayer->SetScaledFont(reinterpret_cast<gfx::ScaledFont*>(specific.get_TextLayerAttributes().scaledFont()));
+        break;
+      }
+      case Specific::TBorderLayerAttributes: {
+        MOZ_LAYERS_LOG(("[ParentSide]   border layer"));
+
+        BorderLayer* borderLayer = layerParent->AsBorderLayer();
+        if (!borderLayer) {
+          return IPC_FAIL_NO_REASON(this);
+        }
+        borderLayer->SetRect(specific.get_BorderLayerAttributes().rect());
+        borderLayer->SetColors(specific.get_BorderLayerAttributes().colors());
+        borderLayer->SetCornerRadii(specific.get_BorderLayerAttributes().corners());
+        borderLayer->SetWidths(specific.get_BorderLayerAttributes().widths());
+        break;
+      }
       case Specific::TCanvasLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   canvas layer"));
 
-        CanvasLayerComposite* canvasLayer = layerParent->AsCanvasLayerComposite();
+        CanvasLayer* canvasLayer = layerParent->AsCanvasLayer();
         if (!canvasLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         canvasLayer->SetSamplingFilter(specific.get_CanvasLayerAttributes().samplingFilter());
         canvasLayer->SetBounds(specific.get_CanvasLayerAttributes().bounds());
         break;
       }
       case Specific::TRefLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   ref layer"));
 
-        RefLayerComposite* refLayer = layerParent->AsRefLayerComposite();
+        RefLayer* refLayer = layerParent->AsRefLayer();
         if (!refLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         refLayer->SetReferentId(specific.get_RefLayerAttributes().id());
         refLayer->SetEventRegionsOverride(specific.get_RefLayerAttributes().eventRegionsOverride());
         break;
       }
       case Specific::TImageLayerAttributes: {
         MOZ_LAYERS_LOG(("[ParentSide]   image layer"));
 
-        ImageLayerComposite* imageLayer = layerParent->AsImageLayerComposite();
+        ImageLayer* imageLayer = layerParent->AsImageLayer();
         if (!imageLayer) {
           return IPC_FAIL_NO_REASON(this);
         }
         const ImageLayerAttributes& attrs = specific.get_ImageLayerAttributes();
         imageLayer->SetSamplingFilter(attrs.samplingFilter());
         imageLayer->SetScaleToSize(attrs.scaleToSize(), attrs.scaleMode());
         break;
       }
@@ -529,17 +572,17 @@ LayerTransactionParent::RecvUpdate(Infal
     case Edit::TOpInsertAfter: {
       MOZ_LAYERS_LOG(("[ParentSide] InsertAfter"));
 
       const OpInsertAfter& oia = edit.get_OpInsertAfter();
       Layer* child = ShadowChild(oia)->AsLayer();
       if (!child) {
         return IPC_FAIL_NO_REASON(this);
       }
-      ContainerLayerComposite* container = ShadowContainer(oia)->AsContainerLayerComposite();
+      ContainerLayer* container = ShadowContainer(oia)->AsContainerLayer();
       if (!container ||
           !container->InsertAfter(child, ShadowAfter(oia)->AsLayer()))
       {
         return IPC_FAIL_NO_REASON(this);
       }
 
       updateHitTestingTree = true;
       break;
@@ -547,17 +590,17 @@ LayerTransactionParent::RecvUpdate(Infal
     case Edit::TOpPrependChild: {
       MOZ_LAYERS_LOG(("[ParentSide] PrependChild"));
 
       const OpPrependChild& oac = edit.get_OpPrependChild();
       Layer* child = ShadowChild(oac)->AsLayer();
       if (!child) {
         return IPC_FAIL_NO_REASON(this);
       }
-      ContainerLayerComposite* container = ShadowContainer(oac)->AsContainerLayerComposite();
+      ContainerLayer* container = ShadowContainer(oac)->AsContainerLayer();
       if (!container ||
           !container->InsertAfter(child, nullptr))
       {
         return IPC_FAIL_NO_REASON(this);
       }
 
       updateHitTestingTree = true;
       break;
@@ -565,17 +608,17 @@ LayerTransactionParent::RecvUpdate(Infal
     case Edit::TOpRemoveChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RemoveChild"));
 
       const OpRemoveChild& orc = edit.get_OpRemoveChild();
       Layer* childLayer = ShadowChild(orc)->AsLayer();
       if (!childLayer) {
         return IPC_FAIL_NO_REASON(this);
       }
-      ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+      ContainerLayer* container = ShadowContainer(orc)->AsContainerLayer();
       if (!container ||
           !container->RemoveChild(childLayer))
       {
         return IPC_FAIL_NO_REASON(this);
       }
 
       updateHitTestingTree = true;
       break;
@@ -583,17 +626,17 @@ LayerTransactionParent::RecvUpdate(Infal
     case Edit::TOpRepositionChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RepositionChild"));
 
       const OpRepositionChild& orc = edit.get_OpRepositionChild();
       Layer* child = ShadowChild(orc)->AsLayer();
       if (!child) {
         return IPC_FAIL_NO_REASON(this);
       }
-      ContainerLayerComposite* container = ShadowContainer(orc)->AsContainerLayerComposite();
+      ContainerLayer* container = ShadowContainer(orc)->AsContainerLayer();
       if (!container ||
           !container->RepositionChild(child, ShadowAfter(orc)->AsLayer()))
       {
         return IPC_FAIL_NO_REASON(this);
       }
 
       updateHitTestingTree = true;
       break;
@@ -601,17 +644,17 @@ LayerTransactionParent::RecvUpdate(Infal
     case Edit::TOpRaiseToTopChild: {
       MOZ_LAYERS_LOG(("[ParentSide] RaiseToTopChild"));
 
       const OpRaiseToTopChild& rtc = edit.get_OpRaiseToTopChild();
       Layer* child = ShadowChild(rtc)->AsLayer();
       if (!child) {
         return IPC_FAIL_NO_REASON(this);
       }
-      ContainerLayerComposite* container = ShadowContainer(rtc)->AsContainerLayerComposite();
+      ContainerLayer* container = ShadowContainer(rtc)->AsContainerLayer();
       if (!container ||
           !container->RepositionChild(child, nullptr))
       {
         return IPC_FAIL_NO_REASON(this);
       }
 
       updateHitTestingTree = true;
       break;
@@ -629,17 +672,19 @@ LayerTransactionParent::RecvUpdate(Infal
       if (mPendingCompositorUpdates) {
         // Do not attach compositables from old layer trees. Return true since
         // content cannot handle errors.
         return IPC_OK();
       }
       if (!Attach(cast(op.layerParent()), host, false)) {
         return IPC_FAIL_NO_REASON(this);
       }
-      host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+      if (mLayerManager->GetCompositor()) {
+        host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+      }
       break;
     }
     case Edit::TOpAttachAsyncCompositable: {
       const OpAttachAsyncCompositable& op = edit.get_OpAttachAsyncCompositable();
       PCompositableParent* compositableParent = CompositableMap::Get(op.containerID());
       if (!compositableParent) {
         NS_ERROR("CompositableParent not found in the map");
         return IPC_FAIL_NO_REASON(this);
@@ -648,17 +693,19 @@ LayerTransactionParent::RecvUpdate(Infal
         // Do not attach compositables from old layer trees. Return true since
         // content cannot handle errors.
         return IPC_OK();
       }
       CompositableHost* host = CompositableHost::FromIPDLActor(compositableParent);
       if (!Attach(cast(op.layerParent()), host, true)) {
         return IPC_FAIL_NO_REASON(this);
       }
-      host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+      if (mLayerManager->GetCompositor()) {
+        host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
+      }
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
   mCompositorBridge->ShadowLayersUpdated(this, aTransactionId, targetConfig,
@@ -761,17 +808,17 @@ LayerTransactionParent::RecvGetAnimation
 
   Layer* layer = cast(aParent)->AsLayer();
   if (!layer) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   mCompositorBridge->ApplyAsyncProperties(this);
 
-  if (!layer->AsLayerComposite()->GetShadowOpacitySetByAnimation()) {
+  if (!layer->AsHostLayer()->GetShadowOpacitySetByAnimation()) {
     return IPC_OK();
   }
 
   *aOpacity = layer->GetLocalOpacity();
   *aHasAnimationOpacity = true;
   return IPC_OK();
 }
 
@@ -795,26 +842,26 @@ LayerTransactionParent::RecvGetAnimation
   mCompositorBridge->ApplyAsyncProperties(this);
 
   // This method is specific to transforms applied by animation.
   // This is because this method uses the information stored with an animation
   // such as the origin of the reference frame corresponding to the layer, to
   // recover the untranslated transform from the shadow transform. For
   // transforms that are not set by animation we don't have this information
   // available.
-  if (!layer->AsLayerComposite()->GetShadowTransformSetByAnimation()) {
+  if (!layer->AsHostLayer()->GetShadowTransformSetByAnimation()) {
     *aTransform = mozilla::void_t();
     return IPC_OK();
   }
 
   // The following code recovers the untranslated transform
   // from the shadow transform by undoing the translations in
   // AsyncCompositionManager::SampleValue.
 
-  Matrix4x4 transform = layer->AsLayerComposite()->GetShadowBaseTransform();
+  Matrix4x4 transform = layer->AsHostLayer()->GetShadowBaseTransform();
   if (ContainerLayer* c = layer->AsContainerLayer()) {
     // Undo the scale transform applied by AsyncCompositionManager::SampleValue
     transform.PostScale(1.0f/c->GetInheritedXScale(),
                         1.0f/c->GetInheritedYScale(),
                         1.0f);
   }
   float scale = 1;
   Point3D scaledOrigin;
@@ -948,23 +995,23 @@ LayerTransactionParent::Attach(ShadowLay
   if (!aCompositable) {
     return false;
   }
 
   Layer* baselayer = aLayerParent->AsLayer();
   if (!baselayer) {
     return false;
   }
-  LayerComposite* layer = baselayer->AsLayerComposite();
+  HostLayer* layer = baselayer->AsHostLayer();
   if (!layer) {
     return false;
   }
 
   Compositor* compositor
-    = static_cast<LayerManagerComposite*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
+    = static_cast<HostLayerManager*>(aLayerParent->AsLayer()->Manager())->GetCompositor();
 
   if (!layer->SetCompositableHost(aCompositable)) {
     // not all layer types accept a compositable, see bug 967824
     return false;
   }
   aCompositable->Attach(aLayerParent->AsLayer(),
                         compositor,
                         aIsAsync
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -24,43 +24,43 @@ class Shmem;
 
 namespace layout {
 class RenderFrameParent;
 } // namespace layout
 
 namespace layers {
 
 class Layer;
-class LayerManagerComposite;
+class HostLayerManager;
 class ShadowLayerParent;
 class CompositableParent;
 class CompositorBridgeParentBase;
 
 class LayerTransactionParent final : public PLayerTransactionParent,
                                      public CompositableParentManager,
                                      public ShmemAllocator
 {
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
   typedef InfallibleTArray<Edit> EditArray;
   typedef InfallibleTArray<OpDestroy> OpDestroyArray;
   typedef InfallibleTArray<EditReply> EditReplyArray;
   typedef InfallibleTArray<PluginWindowData> PluginsArray;
 
 public:
-  LayerTransactionParent(LayerManagerComposite* aManager,
+  LayerTransactionParent(HostLayerManager* aManager,
                          CompositorBridgeParentBase* aBridge,
                          uint64_t aId);
 
 protected:
   ~LayerTransactionParent();
 
 public:
   void Destroy();
 
-  LayerManagerComposite* layer_manager() const { return mLayerManager; }
+  HostLayerManager* layer_manager() const { return mLayerManager; }
 
   uint64_t GetId() const { return mId; }
   Layer* GetRoot() const { return mRoot; }
 
   uint64_t GetChildEpoch() const { return mChildEpoch; }
   bool ShouldParentObserveEpoch();
 
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
@@ -184,17 +184,17 @@ protected:
     mIPCOpen = false;
     Release();
   }
   friend class CompositorBridgeParent;
   friend class CrossProcessCompositorBridgeParent;
   friend class layout::RenderFrameParent;
 
 private:
-  RefPtr<LayerManagerComposite> mLayerManager;
+  RefPtr<HostLayerManager> mLayerManager;
   CompositorBridgeParentBase* mCompositorBridge;
   // Hold the root because it might be grafted under various
   // containers in the "real" layer tree
   RefPtr<Layer> mRoot;
   // When this is nonzero, it refers to a layer tree owned by the
   // compositor thread.  It is always true that
   //   mId != 0 => mRoot == null
   // because the "real tree" is owned by the compositor.
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -28,44 +28,52 @@ using struct nsPoint from "nsPoint.h";
 using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
 using nsCSSPropertyID from "nsCSSPropertyID.h";
 using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::LayerMargin from "Units.h";
 using mozilla::LayerPoint from "Units.h";
+using mozilla::LayerCoord from "Units.h";
+using mozilla::LayerSize from "Units.h";
 using mozilla::LayerRect from "Units.h";
 using mozilla::LayerIntRegion from "Units.h";
 using mozilla::ParentLayerIntRect from "Units.h";
 using mozilla::LayoutDeviceIntRect from "Units.h";
 using mozilla::layers::ScaleMode from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::ScrollMetadata from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
+using mozilla::gfx::Glyph from "Layers.h";
+using mozilla::layers::BorderColors from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::BorderCorners from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::BorderWidths from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 struct TargetConfig {
   IntRect naturalBounds;
   ScreenRotation rotation;
   ScreenOrientationInternal orientation;
   nsIntRegion clearRegion;
 };
 
 // Create a shadow layer for |layer|
 struct OpCreatePaintedLayer    { PLayer layer; };
 struct OpCreateContainerLayer  { PLayer layer; };
 struct OpCreateImageLayer      { PLayer layer; };
 struct OpCreateColorLayer      { PLayer layer; };
+struct OpCreateTextLayer       { PLayer layer; };
+struct OpCreateBorderLayer       { PLayer layer; };
 struct OpCreateCanvasLayer     { PLayer layer; };
 struct OpCreateRefLayer        { PLayer layer; };
 
 struct OpAttachCompositable {
   PLayer layer;
   PCompositable compositable;
 };
 
@@ -250,34 +258,52 @@ struct ContainerLayerAttributes {
   float preXScale;
   float preYScale;
   float inheritedXScale;
   float inheritedYScale;
   float presShellResolution;
   bool scaleToResolution;
   EventRegionsOverride eventRegionsOverride;
 };
+
+struct GlyphArray
+{
+  LayerColor color;
+  Glyph[] glyphs;
+};
+
+// XXX - Bas - Hack warning! This is using a raw pointer to a ScaledFont*
+// and won't work with e10s.
+struct TextLayerAttributes      { IntRect bounds; GlyphArray[] glyphs; uintptr_t scaledFont; };
 struct ColorLayerAttributes     { LayerColor color; IntRect bounds; };
 struct CanvasLayerAttributes    { SamplingFilter samplingFilter; IntRect bounds; };
 struct RefLayerAttributes {
   int64_t id;
   // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override
   // explicitly here.
   EventRegionsOverride eventRegionsOverride;
 };
 struct ImageLayerAttributes     { SamplingFilter samplingFilter; IntSize scaleToSize; ScaleMode scaleMode; };
+struct BorderLayerAttributes {
+  LayerRect rect;
+  BorderColors colors;
+  BorderCorners corners;
+  BorderWidths widths;
+};
 
 union SpecificLayerAttributes {
   null_t;
   PaintedLayerAttributes;
   ContainerLayerAttributes;
   ColorLayerAttributes;
   CanvasLayerAttributes;
+  TextLayerAttributes;
   RefLayerAttributes;
   ImageLayerAttributes;
+  BorderLayerAttributes;
 };
 
 struct LayerAttributes {
   CommonLayerAttributes common;
   SpecificLayerAttributes specific;
 };
 
 // See nsIWidget Configurations
@@ -434,16 +460,18 @@ struct CompositableOperation {
 // A unit of a changeset; a set of these comprise a changeset
 // If adding a new edit type that requires the hit testing tree to be updated,
 // set the updateHitTestingTree flag to true in RecvUpdate()
 union Edit {
   OpCreatePaintedLayer;
   OpCreateContainerLayer;
   OpCreateImageLayer;
   OpCreateColorLayer;
+  OpCreateTextLayer;
+  OpCreateBorderLayer;
   OpCreateCanvasLayer;
   OpCreateRefLayer;
 
   OpSetLayerAttributes;
   OpSetDiagnosticTypes;
   OpWindowOverlayChanged;
 
   OpSetRoot;
--- a/gfx/layers/ipc/ShadowLayerParent.cpp
+++ b/gfx/layers/ipc/ShadowLayerParent.cpp
@@ -51,61 +51,77 @@ ShadowLayerParent::Destroy()
 {
   // It's possible for Destroy() to come in just after this has been
   // created, but just before the transaction in which Bind() would
   // have been called.  In that case, we'll ignore shadow-layers
   // transactions from there on and never get a layer here.
   Disconnect();
 }
 
-ContainerLayerComposite*
-ShadowLayerParent::AsContainerLayerComposite() const
+ContainerLayer*
+ShadowLayerParent::AsContainerLayer() const
 {
   return mLayer && mLayer->GetType() == Layer::TYPE_CONTAINER
-         ? static_cast<ContainerLayerComposite*>(mLayer.get())
+         ? static_cast<ContainerLayer*>(mLayer.get())
          : nullptr;
 }
 
-CanvasLayerComposite*
-ShadowLayerParent::AsCanvasLayerComposite() const
+CanvasLayer*
+ShadowLayerParent::AsCanvasLayer() const
 {
   return mLayer && mLayer->GetType() == Layer::TYPE_CANVAS
-         ? static_cast<CanvasLayerComposite*>(mLayer.get())
+         ? static_cast<CanvasLayer*>(mLayer.get())
+         : nullptr;
+}
+
+ColorLayer*
+ShadowLayerParent::AsColorLayer() const
+{
+  return mLayer && mLayer->GetType() == Layer::TYPE_COLOR
+         ? static_cast<ColorLayer*>(mLayer.get())
          : nullptr;
 }
 
-ColorLayerComposite*
-ShadowLayerParent::AsColorLayerComposite() const
+ImageLayer*
+ShadowLayerParent::AsImageLayer() const
 {
-  return mLayer && mLayer->GetType() == Layer::TYPE_COLOR
-         ? static_cast<ColorLayerComposite*>(mLayer.get())
+  return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE
+         ? static_cast<ImageLayer*>(mLayer.get())
+         : nullptr;
+}
+
+RefLayer*
+ShadowLayerParent::AsRefLayer() const
+{
+  return mLayer && mLayer->GetType() == Layer::TYPE_REF
+         ? static_cast<RefLayer*>(mLayer.get())
          : nullptr;
 }
 
-ImageLayerComposite*
-ShadowLayerParent::AsImageLayerComposite() const
+PaintedLayer*
+ShadowLayerParent::AsPaintedLayer() const
 {
-  return mLayer && mLayer->GetType() == Layer::TYPE_IMAGE
-         ? static_cast<ImageLayerComposite*>(mLayer.get())
+  return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED
+    ? static_cast<PaintedLayer*>(mLayer.get())
          : nullptr;
 }
 
-RefLayerComposite*
-ShadowLayerParent::AsRefLayerComposite() const
+TextLayer*
+ShadowLayerParent::AsTextLayer() const
 {
-  return mLayer && mLayer->GetType() == Layer::TYPE_REF
-         ? static_cast<RefLayerComposite*>(mLayer.get())
+  return mLayer && mLayer->GetType() == Layer::TYPE_TEXT
+         ? static_cast<TextLayer*>(mLayer.get())
          : nullptr;
 }
 
-PaintedLayerComposite*
-ShadowLayerParent::AsPaintedLayerComposite() const
+BorderLayer*
+ShadowLayerParent::AsBorderLayer() const
 {
-  return mLayer && mLayer->GetType() == Layer::TYPE_PAINTED
-         ? static_cast<PaintedLayerComposite*>(mLayer.get())
+  return mLayer && mLayer->GetType() == Layer::TYPE_BORDER
+         ? static_cast<BorderLayer*>(mLayer.get())
          : nullptr;
 }
 
 void
 ShadowLayerParent::ActorDestroy(ActorDestroyReason why)
 {
   switch (why) {
   case AncestorDeletion:
--- a/gfx/layers/ipc/ShadowLayerParent.h
+++ b/gfx/layers/ipc/ShadowLayerParent.h
@@ -13,41 +13,44 @@
 #include "mozilla/layers/PLayerParent.h"  // for PLayerParent
 
 namespace mozilla {
 namespace layers {
 
 class ContainerLayer;
 class Layer;
 
-class CanvasLayerComposite;
-class ColorLayerComposite;
-class ContainerLayerComposite;
-class ImageLayerComposite;
-class RefLayerComposite;
-class PaintedLayerComposite;
+class CanvasLayer;
+class ColorLayer;
+class TextLayer;
+class ContainerLayer;
+class ImageLayer;
+class RefLayer;
+class PaintedLayer;
 
 class ShadowLayerParent : public PLayerParent
 {
 public:
   ShadowLayerParent();
 
   virtual ~ShadowLayerParent();
 
   void Bind(Layer* layer);
   void Destroy();
 
   Layer* AsLayer() const { return mLayer; }
 
-  ContainerLayerComposite* AsContainerLayerComposite() const;
-  CanvasLayerComposite* AsCanvasLayerComposite() const;
-  ColorLayerComposite* AsColorLayerComposite() const;
-  ImageLayerComposite* AsImageLayerComposite() const;
-  RefLayerComposite* AsRefLayerComposite() const;
-  PaintedLayerComposite* AsPaintedLayerComposite() const;
+  ContainerLayer* AsContainerLayer() const;
+  CanvasLayer* AsCanvasLayer() const;
+  ColorLayer* AsColorLayer() const;
+  TextLayer* AsTextLayer() const;
+  ImageLayer* AsImageLayer() const;
+  BorderLayer* AsBorderLayer() const;
+  RefLayer* AsRefLayer() const;
+  PaintedLayer* AsPaintedLayer() const;
 
 private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   void Disconnect();
 
   RefPtr<Layer> mLayer;
 };
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -261,16 +261,26 @@ ShadowLayerForwarder::CreatedImageLayer(
   CreatedLayer<OpCreateImageLayer>(mTxn, aImage);
 }
 void
 ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor)
 {
   CreatedLayer<OpCreateColorLayer>(mTxn, aColor);
 }
 void
+ShadowLayerForwarder::CreatedTextLayer(ShadowableLayer* aColor)
+{
+  CreatedLayer<OpCreateTextLayer>(mTxn, aColor);
+}
+void
+ShadowLayerForwarder::CreatedBorderLayer(ShadowableLayer* aBorder)
+{
+  CreatedLayer<OpCreateBorderLayer>(mTxn, aBorder);
+}
+void
 ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
 {
   CreatedLayer<OpCreateCanvasLayer>(mTxn, aCanvas);
 }
 void
 ShadowLayerForwarder::CreatedRefLayer(ShadowableLayer* aRef)
 {
   CreatedLayer<OpCreateRefLayer>(mTxn, aRef);
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -204,16 +204,18 @@ public:
    * the compositing process.
    */
   void CreatedPaintedLayer(ShadowableLayer* aThebes);
   void CreatedContainerLayer(ShadowableLayer* aContainer);
   void CreatedImageLayer(ShadowableLayer* aImage);
   void CreatedColorLayer(ShadowableLayer* aColor);
   void CreatedCanvasLayer(ShadowableLayer* aCanvas);
   void CreatedRefLayer(ShadowableLayer* aRef);
+  void CreatedTextLayer(ShadowableLayer* aRef);
+  void CreatedBorderLayer(ShadowableLayer* aRef);
 
   /**
    * At least one attribute of |aMutant| has changed, and |aMutant|
    * needs to sync to its shadow layer.  This initial implementation
    * forwards all attributes when any is mutated.
    */
   void Mutated(ShadowableLayer* aMutant);
 
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -7,17 +7,16 @@
 #include "VideoBridgeParent.h"
 #include "mozilla/layers/TextureHost.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
-using namespace mozilla::media;
 
 
 static VideoBridgeParent* sVideoBridgeSingleton;
 
 VideoBridgeParent::VideoBridgeParent()
   : mClosed(false)
 {
   mSelfRef = this;
--- a/gfx/layers/ipc/VideoBridgeParent.h
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -2,16 +2,17 @@
  * 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 gfx_layers_ipc_VideoBridgeParent_h_
 #define gfx_layers_ipc_VideoBridgeParent_h_
 
 #include "mozilla/layers/PVideoBridgeParent.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
 class VideoBridgeParent final : public PVideoBridgeParent,
                                 public HostIPCAllocator,
                                 public ShmemAllocator
 {
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -269,35 +269,39 @@ UNIFIED_SOURCES += [
     'apz/util/ContentProcessController.cpp',
     'apz/util/DoubleTapToZoom.cpp',
     'apz/util/InputAPZContext.cpp',
     'apz/util/ScrollLinkedEffectDetector.cpp',
     'apz/util/TouchActionHelper.cpp',
     'AsyncCanvasRenderer.cpp',
     'AxisPhysicsModel.cpp',
     'AxisPhysicsMSDModel.cpp',
+    'basic/BasicBorderLayer.cpp',
     'basic/BasicCanvasLayer.cpp',
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
     'basic/BasicLayersImpl.cpp',
     'basic/BasicPaintedLayer.cpp',
+    'basic/BasicTextLayer.cpp',
     'basic/TextureHostBasic.cpp',
     'BSPTree.cpp',
     'BufferTexture.cpp',
     'BufferUnrotate.cpp',
     'client/CanvasClient.cpp',
+    'client/ClientBorderLayer.cpp',
     'client/ClientCanvasLayer.cpp',
     'client/ClientColorLayer.cpp',
     'client/ClientContainerLayer.cpp',
     'client/ClientImageLayer.cpp',
     'client/ClientLayerManager.cpp',
     'client/ClientPaintedLayer.cpp',
+    'client/ClientTextLayer.cpp',
     'client/ClientTiledPaintedLayer.cpp',
     'client/CompositableChild.cpp',
     'client/CompositableClient.cpp',
     'client/ContentClient.cpp',
     'client/GPUVideoTextureClient.cpp',
     'client/ImageClient.cpp',
     'client/SingleTiledContentClient.cpp',
     'client/TextureClient.cpp',
--- a/gfx/layers/protobuf/LayerScopePacket.pb.h
+++ b/gfx/layers/protobuf/LayerScopePacket.pb.h
@@ -67,18 +67,19 @@ const int TexturePacket_Filter_Filter_AR
 enum LayersPacket_Layer_LayerType {
   LayersPacket_Layer_LayerType_UnknownLayer = 0,
   LayersPacket_Layer_LayerType_LayerManager = 1,
   LayersPacket_Layer_LayerType_ContainerLayer = 2,
   LayersPacket_Layer_LayerType_PaintedLayer = 3,
   LayersPacket_Layer_LayerType_CanvasLayer = 4,
   LayersPacket_Layer_LayerType_ImageLayer = 5,
   LayersPacket_Layer_LayerType_ColorLayer = 6,
-  LayersPacket_Layer_LayerType_RefLayer = 7,
-  LayersPacket_Layer_LayerType_ReadbackLayer = 8
+  LayersPacket_Layer_LayerType_TextLayer = 7,
+  LayersPacket_Layer_LayerType_RefLayer = 8,
+  LayersPacket_Layer_LayerType_ReadbackLayer = 9
 };
 bool LayersPacket_Layer_LayerType_IsValid(int value);
 const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MIN = LayersPacket_Layer_LayerType_UnknownLayer;
 const LayersPacket_Layer_LayerType LayersPacket_Layer_LayerType_LayerType_MAX = LayersPacket_Layer_LayerType_ReadbackLayer;
 const int LayersPacket_Layer_LayerType_LayerType_ARRAYSIZE = LayersPacket_Layer_LayerType_LayerType_MAX + 1;
 
 enum LayersPacket_Layer_ScrollingDirect {
   LayersPacket_Layer_ScrollingDirect_VERTICAL = 1,
@@ -1668,16 +1669,17 @@ class LayersPacket_Layer : public ::goog
   typedef LayersPacket_Layer_LayerType LayerType;
   static const LayerType UnknownLayer = LayersPacket_Layer_LayerType_UnknownLayer;
   static const LayerType LayerManager = LayersPacket_Layer_LayerType_LayerManager;
   static const LayerType ContainerLayer = LayersPacket_Layer_LayerType_ContainerLayer;
   static const LayerType PaintedLayer = LayersPacket_Layer_LayerType_PaintedLayer;
   static const LayerType CanvasLayer = LayersPacket_Layer_LayerType_CanvasLayer;
   static const LayerType ImageLayer = LayersPacket_Layer_LayerType_ImageLayer;
   static const LayerType ColorLayer = LayersPacket_Layer_LayerType_ColorLayer;
+  static const LayerType TextLayer = LayersPacket_Layer_LayerType_TextLayer;
   static const LayerType RefLayer = LayersPacket_Layer_LayerType_RefLayer;
   static const LayerType ReadbackLayer = LayersPacket_Layer_LayerType_ReadbackLayer;
   static inline bool LayerType_IsValid(int value) {
     return LayersPacket_Layer_LayerType_IsValid(value);
   }
   static const LayerType LayerType_MIN =
     LayersPacket_Layer_LayerType_LayerType_MIN;
   static const LayerType LayerType_MAX =
--- a/gfx/skia/skia/src/gpu/GrResourceProvider.cpp
+++ b/gfx/skia/skia/src/gpu/GrResourceProvider.cpp
@@ -104,18 +104,21 @@ GrBuffer* GrResourceProvider::createBuff
     if (!(flags & kRequireGpuMemory_Flag) &&
         this->gpu()->caps()->preferClientSideDynamicBuffers() &&
         GrBufferTypeIsVertexOrIndex(intendedType) &&
         kDynamic_GrAccessPattern == accessPattern) {
         return GrBuffer::CreateCPUBacked(this->gpu(), size, intendedType, data);
     }
 
     // bin by pow2 with a reasonable min
-    static const uint32_t MIN_SIZE = 1 << 12;
-    size_t allocSize = SkTMax(MIN_SIZE, GrNextPow2(SkToUInt(size)));
+    static const size_t MIN_SIZE = 1 << 12;
+    size_t allocSize = size > (1u << 31)
+                       ? size_t(SkTMin(uint64_t(SIZE_MAX), uint64_t(GrNextPow2(uint32_t(uint64_t(size) >> 32))) << 32))
+                       : size_t(GrNextPow2(uint32_t(size)));
+    allocSize = SkTMax(allocSize, MIN_SIZE);
 
     GrScratchKey key;
     GrBuffer::ComputeScratchKeyForDynamicVBO(allocSize, intendedType, &key);
     uint32_t scratchFlags = 0;
     if (flags & kNoPendingIO_Flag) {
         scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag;
     } else {
         scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag;
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -73,16 +73,20 @@ public:
   virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
     RefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
     return layer.forget();
   }
   virtual already_AddRefed<ColorLayer> CreateColorLayer() {
     NS_RUNTIMEABORT("Not implemented.");
     return nullptr;
   }
+  virtual already_AddRefed<TextLayer> CreateTextLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
   virtual void SetRoot(Layer* aLayer) {}
   virtual bool BeginTransactionWithTarget(gfxContext* aTarget) { return true; }
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
     NS_RUNTIMEABORT("Not implemented.");
     return nullptr;
   }
   virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
                               void* aCallbackData,
@@ -233,17 +237,17 @@ already_AddRefed<Layer> CreateLayerTree(
         layer->SetParent(parentContainerLayer);
       }
       lastLayer = layer;
     }
   }
   if (rootLayer) {
     rootLayer->ComputeEffectiveTransforms(Matrix4x4());
     manager->SetRoot(rootLayer);
-    if (rootLayer->AsLayerComposite()) {
+    if (rootLayer->AsHostLayer()) {
       // Only perform this for LayerManagerComposite
       CompositorBridgeParent::SetShadowProperties(rootLayer);
     }
   }
   return rootLayer.forget();
 }
 
 TEST(Layers, LayerTree) {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -442,16 +442,18 @@ private:
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.border-layers",         LayersAllowBorderLayers, bool, false);
+  DECL_GFX_PREF(Live, "layers.advanced.text-layers",           LayersAllowTextLayers, bool, false);
   DECL_GFX_PREF(Once, "layers.allow-d3d9-fallback",            LayersAllowD3D9Fallback, bool, false);
   DECL_GFX_PREF(Once, "layers.amd-switchable-gfx.enabled",     LayersAMDSwitchableGfxEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.enabled",         AsyncPanZoomEnabledDoNotUseDirectly, bool, true);
   DECL_GFX_PREF(Once, "layers.async-pan-zoom.separate-event-thread", AsyncPanZoomSeparateEventThread, bool, false);
   DECL_GFX_PREF(Live, "layers.bench.enabled",                  LayersBenchEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.bufferrotation.enabled",         BufferRotationEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.child-process-shutdown",         ChildProcessShutdown, bool, true);
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
@@ -533,16 +535,17 @@ private:
   DECL_GFX_PREF(Live, "layout.display-list.dump",              LayoutDumpDisplayList, bool, false);
   DECL_GFX_PREF(Live, "layout.display-list.dump-content",      LayoutDumpDisplayListContent, bool, false);
   DECL_GFX_PREF(Live, "layout.event-regions.enabled",          LayoutEventRegionsEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "layout.frame_rate",                     LayoutFrameRate, int32_t, -1);
   DECL_GFX_PREF(Once, "layout.paint_rects_separately",         LayoutPaintRectsSeparately, bool, true);
 
   // This and code dependent on it should be removed once containerless scrolling looks stable.
   DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers",   LayoutUseContainersForRootFrames, bool, true);
+  DECL_GFX_PREF(Live, "layout.smaller-painted-layers",         LayoutSmallerPaintedLayers, bool, false);
 
   DECL_GFX_PREF(Once, "media.hardware-video-decoding.force-enabled",
                                                                HardwareVideoDecodingForceEnabled, bool, false);
 #ifdef XP_WIN
   DECL_GFX_PREF(Live, "media.windows-media-foundation.allow-d3d11-dxva", PDMWMFAllowD3D11, bool, true);
   DECL_GFX_PREF(Live, "media.windows-media-foundation.max-dxva-videos", PDMWMFMaxDXVAVideos, uint32_t, 8);
   DECL_GFX_PREF(Live, "media.wmf.low-latency.enabled", PDMWMFLowLatencyEnabled, bool, false);
   DECL_GFX_PREF(Live, "media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false);
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -171,28 +171,16 @@ struct GCPolicy<jsid>
     }
 };
 
 } // namespace JS
 
 namespace js {
 
 template <>
-struct DefaultHasher<jsid>
-{
-    typedef jsid Lookup;
-    static HashNumber hash(jsid id) {
-        return JSID_BITS(id);
-    }
-    static bool match(jsid id1, jsid id2) {
-        return id1 == id2;
-    }
-};
-
-template <>
 struct BarrierMethods<jsid>
 {
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
     static void exposeToJS(jsid id) {
         if (JSID_IS_GCTHING(id))
             js::gc::ExposeGCThingToActiveJS(JSID_TO_GCTHING(id));
     }
 };
new file mode 100644
--- /dev/null
+++ b/js/public/Result.h
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * `Result` is used as the return type of many SpiderMonkey functions that
+ * can either succeed or fail. See "/mfbt/Result.h".
+ *
+ *
+ * ## Which return type to use
+ *
+ * `Result` is for return values. Obviously, if you're writing a function that
+ * can't fail, don't use Result. Otherwise:
+ *
+ *     JS::Result<>  - function can fail, doesn't return anything on success
+ *         (defaults to `JS::Result<JS::Ok, JS::Error&>`)
+ *     JS::Result<JS::OOM&> - like JS::Result<>, but fails only on OOM
+ *
+ *     JS::Result<Data>  - function can fail, returns Data on success
+ *     JS::Result<Data, JS::OOM&>  - returns Data, fails only on OOM
+ *
+ *     mozilla::GenericErrorResult<JS::Error&> - always fails
+ *
+ * That last type is like a Result with no success type. It's used for
+ * functions like `js::ReportNotFunction` that always return an error
+ * result. `GenericErrorResult<E>` implicitly converts to `Result<V, E>`,
+ * regardless of V.
+ *
+ *
+ * ## Checking Results when your return type is Result
+ *
+ * When you call a function that returns a `Result`, use the `MOZ_TRY` macro to
+ * check for errors:
+ *
+ *     MOZ_TRY(DefenestrateObject(cx, obj));
+ *
+ * If `DefenestrateObject` returns a success result, `MOZ_TRY` is done, and
+ * control flows to the next statement. If `DefenestrateObject` returns an
+ * error result, `MOZ_TRY` will immediately return it, propagating the error to
+ * your caller. It's kind of like exceptions, but more explicit -- you can see
+ * in the code exactly where errors can happen.
+ *
+ * You can do a tail call instead of using `MOZ_TRY`:
+ *
+ *     return DefenestrateObject(cx, obj);
+ *
+ * Indicate success with `return Ok();`.
+ *
+ * If the function returns a value on success, use `MOZ_TRY_VAR` to get it:
+ *
+ *     RootedValue thrug(cx);
+ *     MOZ_TRY_VAR(thrug, GetObjectThrug(cx, obj));
+ *
+ * This behaves the same as `MOZ_TRY` on error. On success, the success
+ * value of `GetObjectThrug(cx, obj)` is assigned to the variable `thrug`.
+ *
+ *
+ * ## Checking Results when your return type is not Result
+ *
+ * This header defines alternatives to MOZ_TRY and MOZ_TRY_VAR for when you
+ * need to call a `Result` function from a function that uses false or nullptr
+ * to indicate errors:
+ *
+ *     JS_TRY_OR_RETURN_FALSE(cx, DefenestrateObject(cx, obj));
+ *     JS_TRY_VAR_OR_RETURN_FALSE(cx, v, GetObjectThrug(cx, obj));
+ *
+ *     JS_TRY_OR_RETURN_NULL(cx, DefenestrateObject(cx, obj));
+ *     JS_TRY_VAR_OR_RETURN_NULL(cx, v, GetObjectThrug(cx, obj));
+ *
+ * When TRY is not what you want, because you need to do some cleanup or
+ * recovery on error, use this idiom:
+ *
+ *     if (!cx->resultToBool(expr_that_is_a_Result)) {
+ *         ... your recovery code here ...
+ *     }
+ *
+ * In place of a tail call, you can use one of these methods:
+ *
+ *     return cx->resultToBool(expr);  // false on error
+ *     return cx->resultToPtr(expr);  // null on error
+ *
+ * Once we are using `Result` everywhere, including in public APIs, all of
+ * these will go away.
+ *
+ *
+ * ## GC safety
+ *
+ * When a function returns a `JS::Result<JSObject*>`, it is the program's
+ * responsibility to check for errors and root the object before continuing:
+ *
+ *     RootedObject wrapper(cx);
+ *     MOZ_TRY_VAR(wrapper, Enwrapify(cx, thing));
+ *
+ * This is ideal. On error, there is no object to root; on success, the
+ * assignment to wrapper roots it. GC safety is ensured.
+ *
+ * `Result` has methods .isOk(), .isErr(), .unwrap(), and .unwrapErr(), but if
+ * you're actually using them, it's possible to create a GC hazard. The static
+ * analysis will catch it if so, but that's hardly convenient. So try to stick
+ * to the idioms shown above.
+ *
+ *
+ * ## Future directions
+ *
+ * At present, JS::Error and JS::OOM are empty structs. The plan is to make them
+ * GC things that contain the actual error information (including the exception
+ * value and a saved stack).
+ *
+ * The long-term plan is to remove JS_IsExceptionPending and
+ * JS_GetPendingException in favor of JS::Error. Exception state will no longer
+ * exist.
+ */
+
+#ifndef js_Result_h
+#define js_Result_h
+
+#include "mozilla/Result.h"
+
+struct JSContext;
+
+/**
+ * Evaluate the boolean expression expr. If it's true, do nothing.
+ * If it's false, return an error result.
+ */
+#define JS_TRY_BOOL_TO_RESULT(cx, expr) \
+    do { \
+        bool ok_ = (expr); \
+        if (!ok_) \
+            return (cx)->boolToResult(ok_); \
+    } while (0)
+
+/**
+ * JS_TRY_OR_RETURN_FALSE(cx, expr) runs expr to compute a Result value.
+ * On success, nothing happens; on error, it returns false immediately.
+ *
+ * Implementation note: this involves cx because this may eventually
+ * do the work of setting a pending exception or reporting OOM.
+ */
+#define JS_TRY_OR_RETURN_FALSE(cx, expr) \
+    do { \
+        auto tmpResult_ = (expr); \
+        if (tmpResult_.isErr()) \
+            return (cx)->resultToBool(tmpResult_); \
+    } while (0)
+
+/**
+ * Like JS_TRY_OR_RETURN_FALSE, but returning nullptr on error,
+ * rather than false.
+ */
+#define JS_TRY_OR_RETURN_NULL(cx, expr) \
+    do { \
+        auto tmpResult_ = (expr); \
+        if (tmpResult_.isErr()) { \
+            JS_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
+            return nullptr; \
+        } \
+    } while (0)
+
+#define JS_TRY_VAR_OR_RETURN_FALSE(cx, target, expr) \
+    do { \
+        auto tmpResult_ = (expr); \
+        if (tmpResult_.isErr()) \
+            return (cx)->resultToBool(tmpResult_); \
+        (target) = tmpResult_.unwrap(); \
+    } while (0)
+
+#define JS_TRY_VAR_OR_RETURN_NULL(cx, target, expr) \
+    do { \
+        auto tmpResult_ = (expr); \
+        if (tmpResult_.isErr()) {  \
+            JS_ALWAYS_FALSE((cx)->resultToBool(tmpResult_)); \
+            return nullptr; \
+        } \
+        (target) = tmpResult_.unwrap(); \
+    } while (0)
+
+namespace JS {
+
+using mozilla::Ok;
+
+/**
+ * Type representing a JS error or exception. At the moment this only "represents"
+ * an error in a rather abstract way.
+ */
+struct Error
+{
+    // Ensure sizeof(Error) > 1 so that Result<V, Error&> can use pointer
+    // tagging.
+    int dummy;
+};
+
+struct OOM : public Error
+{
+};
+
+/**
+ * `Result` is intended to be the return type of JSAPI calls and internal
+ * functions that can run JS code or allocate memory from the JS GC heap. Such
+ * functions can:
+ *
+ * -   succeed, possibly returning a value;
+ *
+ * -   fail with a JS exception (out-of-memory falls in this category); or
+ *
+ * -   fail because JS execution was terminated, which occurs when e.g. a
+ *     user kills a script from the "slow script" UI. This is also how we
+ *     unwind the stack when the debugger forces the current function to
+ *     return. JS `catch` blocks can't catch this kind of failure,
+ *     and JS `finally` blocks don't execute.
+ */
+template <typename V = Ok, typename E = Error&>
+using Result = mozilla::Result<V, E>;
+
+static_assert(sizeof(Result<>) == sizeof(uintptr_t),
+              "Result<> should be pointer-sized");
+
+static_assert(sizeof(Result<int*, Error&>) == sizeof(uintptr_t),
+              "Result<V*, Error&> should be pointer-sized");
+
+} // namespace JS
+
+#endif  // js_Result_h
--- a/js/src/NamespaceImports.h
+++ b/js/src/NamespaceImports.h
@@ -73,16 +73,20 @@ using JS::Latin1CharsZ;
 using JS::ConstTwoByteChars;
 using JS::TwoByteChars;
 using JS::TwoByteCharsZ;
 using JS::UTF8Chars;
 using JS::UTF8CharsZ;
 using JS::UniqueChars;
 using JS::UniqueTwoByteChars;
 
+using JS::Result;
+using JS::Ok;
+using JS::OOM;
+
 using JS::AutoValueVector;
 using JS::AutoIdVector;
 using JS::AutoObjectVector;
 
 using JS::ValueVector;
 using JS::IdVector;
 using JS::ScriptVector;
 
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -99,22 +99,16 @@ Char16ToUChar(const char16_t* chars)
 }
 
 inline UChar*
 Char16ToUChar(char16_t* chars)
 {
     MOZ_CRASH("Char16ToUChar: Intl API disabled");
 }
 
-int32_t
-u_strlen(const UChar* s)
-{
-    MOZ_CRASH("u_strlen: Intl API disabled");
-}
-
 struct UEnumeration;
 
 int32_t
 uenum_count(UEnumeration* en, UErrorCode* status)
 {
     MOZ_CRASH("uenum_count: Intl API disabled");
 }
 
@@ -1519,22 +1513,20 @@ NewUNumberFormat(JSContext* cx, HandleOb
         return nullptr;
 
     if (equal(style, "currency")) {
         if (!GetProperty(cx, internals, internals, cx->names().currency, &value))
             return nullptr;
         currency = value.toString();
         MOZ_ASSERT(currency->length() == 3,
                    "IsWellFormedCurrencyCode permits only length-3 strings");
-        if (!currency->ensureFlat(cx) || !stableChars.initTwoByte(cx, currency))
+        if (!stableChars.initTwoByte(cx, currency))
             return nullptr;
         // uCurrency remains owned by stableChars.
         uCurrency = Char16ToUChar(stableChars.twoByteRange().begin().get());
-        if (!uCurrency)
-            return nullptr;
 
         if (!GetProperty(cx, internals, internals, cx->names().currencyDisplay, &value))
             return nullptr;
         JSAutoByteString currencyDisplay(cx, value.toString());
         if (!currencyDisplay)
             return nullptr;
         if (equal(currencyDisplay, "code")) {
             uStyle = UNUM_CURRENCY_ISO;
@@ -1629,29 +1621,31 @@ intl_FormatNumber(JSContext* cx, UNumber
     // FormatNumber doesn't consider -0.0 to be negative.
     if (IsNegativeZero(x))
         x = 0.0;
 
     Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
     if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
         return false;
     UErrorCode status = U_ZERO_ERROR;
-    int size = unum_formatDouble(nf, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                                 nullptr, &status);
+    int32_t size = unum_formatDouble(nf, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
+                                     nullptr, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(size >= 0);
         if (!chars.resize(size))
             return false;
         status = U_ZERO_ERROR;
         unum_formatDouble(nf, x, Char16ToUChar(chars.begin()), size, nullptr, &status);
     }
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
+    MOZ_ASSERT(size >= 0);
     JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
     if (!str)
         return false;
 
     result.setString(str);
     return true;
 }
 
@@ -2043,17 +2037,17 @@ static js::HashNumber
 HashStringIgnoreCaseASCII(const Char* s, size_t length)
 {
     uint32_t hash = 0;
     for (size_t i = 0; i < length; i++)
         hash = mozilla::AddToHash(hash, ToUpperASCII(s[i]));
     return hash;
 }
 
-js::SharedIntlData::TimeZoneHasher::Lookup::Lookup(JSFlatString* timeZone)
+js::SharedIntlData::TimeZoneHasher::Lookup::Lookup(JSLinearString* timeZone)
   : isLatin1(timeZone->hasLatin1Chars()), length(timeZone->length())
 {
     if (isLatin1) {
         latin1Chars = timeZone->latin1Chars(nogc);
         hash = HashStringIgnoreCaseASCII(latin1Chars, length);
     } else {
         twoByteChars = timeZone->twoByteChars(nogc);
         hash = HashStringIgnoreCaseASCII(twoByteChars, length);
@@ -2209,39 +2203,39 @@ js::SharedIntlData::ensureTimeZones(JSCo
 
 bool
 js::SharedIntlData::validateTimeZoneName(JSContext* cx, HandleString timeZone,
                                          MutableHandleString result)
 {
     if (!ensureTimeZones(cx))
         return false;
 
-    Rooted<JSFlatString*> timeZoneFlat(cx, timeZone->ensureFlat(cx));
-    if (!timeZoneFlat)
+    RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
+    if (!timeZoneLinear)
         return false;
 
-    TimeZoneHasher::Lookup lookup(timeZoneFlat);
+    TimeZoneHasher::Lookup lookup(timeZoneLinear);
     if (TimeZoneSet::Ptr p = availableTimeZones.lookup(lookup))
         result.set(*p);
 
     return true;
 }
 
 bool
 js::SharedIntlData::tryCanonicalizeTimeZoneConsistentWithIANA(JSContext* cx, HandleString timeZone,
                                                               MutableHandleString result)
 {
     if (!ensureTimeZones(cx))
         return false;
 
-    Rooted<JSFlatString*> timeZoneFlat(cx, timeZone->ensureFlat(cx));
-    if (!timeZoneFlat)
+    RootedLinearString timeZoneLinear(cx, timeZone->ensureLinear(cx));
+    if (!timeZoneLinear)
         return false;
 
-    TimeZoneHasher::Lookup lookup(timeZoneFlat);
+    TimeZoneHasher::Lookup lookup(timeZoneLinear);
     MOZ_ASSERT(availableTimeZones.has(lookup), "Invalid time zone name");
 
     if (TimeZoneMap::Ptr p = ianaLinksCanonicalizedDifferentlyByICU.lookup(lookup)) {
         // The effectively supported time zones aren't known at compile time,
         // when
         // 1. SpiderMonkey was compiled with "--with-system-icu".
         // 2. ICU's dynamic time zone data loading feature was used.
         //    (ICU supports loading time zone files at runtime through the
@@ -2435,54 +2429,52 @@ js::intl_patternForSkeleton(JSContext* c
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isString());
     MOZ_ASSERT(args[1].isString());
 
     JSAutoByteString locale(cx, args[0].toString());
     if (!locale)
         return false;
 
-    JSFlatString* skeletonFlat = args[1].toString()->ensureFlat(cx);
-    if (!skeletonFlat)
+    AutoStableStringChars skeleton(cx);
+    if (!skeleton.initTwoByte(cx, args[1].toString()))
         return false;
 
-    AutoStableStringChars stableChars(cx);
-    if (!stableChars.initTwoByte(cx, skeletonFlat))
-        return false;
-
-    mozilla::Range<const char16_t> skeletonChars = stableChars.twoByteRange();
-    uint32_t skeletonLen = u_strlen(Char16ToUChar(skeletonChars.begin().get()));
+    mozilla::Range<const char16_t> skeletonChars = skeleton.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
     UDateTimePatternGenerator* gen = udatpg_open(icuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     ScopedICUObject<UDateTimePatternGenerator, udatpg_close> toClose(gen);
 
+    Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
+    if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
+        return false;
+
     int32_t size = udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
-                                         skeletonLen, nullptr, 0, &status);
-    if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
-        return false;
+                                         skeletonChars.length(), Char16ToUChar(chars.begin()),
+                                         INITIAL_CHAR_BUFFER_SIZE, &status);
+    if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(size >= 0);
+        if (!chars.resize(size))
+            return false;
+        status = U_ZERO_ERROR;
+        udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
+                              skeletonChars.length(), Char16ToUChar(chars.begin()), size, &status);
     }
-    ScopedJSFreePtr<UChar> pattern(cx->pod_malloc<UChar>(size + 1));
-    if (!pattern)
-        return false;
-    pattern[size] = '\0';
-    status = U_ZERO_ERROR;
-    udatpg_getBestPattern(gen, Char16ToUChar(skeletonChars.begin().get()),
-                          skeletonLen, pattern, size, &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
-    RootedString str(cx, JS_NewUCStringCopyZ(cx, reinterpret_cast<char16_t*>(pattern.get())));
+    MOZ_ASSERT(size >= 0);
+    JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 /**
  * Returns a new UDateFormat with the locale and date-time formatting options
@@ -2505,39 +2497,36 @@ NewUDateFormat(JSContext* cx, HandleObje
 
     // We don't need to look at calendar and numberingSystem - they can only be
     // set via the Unicode locale extension and are therefore already set on
     // locale.
 
     if (!GetProperty(cx, internals, internals, cx->names().timeZone, &value))
         return nullptr;
 
-    AutoStableStringChars timeZoneChars(cx);
-    Rooted<JSFlatString*> timeZoneFlat(cx, value.toString()->ensureFlat(cx));
-    if (!timeZoneFlat || !timeZoneChars.initTwoByte(cx, timeZoneFlat))
+    AutoStableStringChars timeZone(cx);
+    if (!timeZone.initTwoByte(cx, value.toString()))
         return nullptr;
 
-    const UChar* uTimeZone = Char16ToUChar(timeZoneChars.twoByteRange().begin().get());
-    uint32_t uTimeZoneLength = u_strlen(uTimeZone);
+    mozilla::Range<const char16_t> timeZoneChars = timeZone.twoByteRange();
 
     if (!GetProperty(cx, internals, internals, cx->names().pattern, &value))
         return nullptr;
 
-    AutoStableStringChars patternChars(cx);
-    Rooted<JSFlatString*> patternFlat(cx, value.toString()->ensureFlat(cx));
-    if (!patternFlat || !patternChars.initTwoByte(cx, patternFlat))
+    AutoStableStringChars pattern(cx);
+    if (!pattern.initTwoByte(cx, value.toString()))
         return nullptr;
 
-    const UChar* uPattern = Char16ToUChar(patternChars.twoByteRange().begin().get());
-    uint32_t uPatternLength = u_strlen(uPattern);
+    mozilla::Range<const char16_t> patternChars = pattern.twoByteRange();
 
     UErrorCode status = U_ZERO_ERROR;
     UDateFormat* df =
-        udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()), uTimeZone, uTimeZoneLength,
-                  uPattern, uPatternLength, &status);
+        udat_open(UDAT_PATTERN, UDAT_PATTERN, icuLocale(locale.ptr()),
+                  Char16ToUChar(timeZoneChars.begin().get()), timeZoneChars.length(),
+                  Char16ToUChar(patternChars.begin().get()), patternChars.length(), &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return nullptr;
     }
 
     // ECMAScript requires the Gregorian calendar to be used from the beginning
     // of ECMAScript time.
     UCalendar* cal = const_cast<UCalendar*>(udat_getCalendar(df));
@@ -2555,29 +2544,31 @@ intl_FormatDateTime(JSContext* cx, UDate
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DATE_NOT_FINITE);
         return false;
     }
 
     Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
     if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
         return false;
     UErrorCode status = U_ZERO_ERROR;
-    int size = udat_format(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
-                           nullptr, &status);
+    int32_t size = udat_format(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
+                               nullptr, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(size >= 0);
         if (!chars.resize(size))
             return false;
         status = U_ZERO_ERROR;
         udat_format(df, x, Char16ToUChar(chars.begin()), size, nullptr, &status);
     }
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
+    MOZ_ASSERT(size >= 0);
     JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
     if (!str)
         return false;
 
     result.setString(str);
 
     return true;
 }
@@ -2685,33 +2676,36 @@ intl_FormatToPartsDateTime(JSContext* cx
     UErrorCode status = U_ZERO_ERROR;
     UFieldPositionIterator* fpositer = ufieldpositer_open(&status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
     auto closeFieldPosIter = MakeScopeExit([&]() { ufieldpositer_close(fpositer); });
 
-    int resultSize =
+    int32_t resultSize =
         udat_formatForFields(df, x, Char16ToUChar(chars.begin()), INITIAL_CHAR_BUFFER_SIZE,
                              fpositer, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(resultSize >= 0);
         if (!chars.resize(resultSize))
             return false;
         status = U_ZERO_ERROR;
         udat_formatForFields(df, x, Char16ToUChar(chars.begin()), resultSize, fpositer, &status);
     }
     if (U_FAILURE(status)) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
 
     RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
     if (!partsArray)
         return false;
+
+    MOZ_ASSERT(resultSize >= 0);
     if (resultSize == 0) {
         // An empty string contains no parts, so avoid extra work below.
         result.setObject(*partsArray);
         return true;
     }
 
     RootedString overallResult(cx, NewStringCopyN<CanGC>(cx, chars.begin(), resultSize));
     if (!overallResult)
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -15,16 +15,18 @@
 
 #include "js/GCAPI.h"
 #include "js/GCHashTable.h"
 
 #if ENABLE_INTL_API
 #include "unicode/utypes.h"
 #endif
 
+class JSLinearString;
+
 /*
  * The Intl module specified by standard ECMA-402,
  * ECMAScript Internationalization API Specification.
  */
 
 namespace js {
 
 /**
@@ -77,17 +79,17 @@ class SharedIntlData
                 const JS::Latin1Char* latin1Chars;
                 const char16_t* twoByteChars;
             };
             bool isLatin1;
             size_t length;
             JS::AutoCheckCannotGC nogc;
             HashNumber hash;
 
-            explicit Lookup(JSFlatString* timeZone);
+            explicit Lookup(JSLinearString* timeZone);
         };
 
         static js::HashNumber hash(const Lookup& lookup) { return lookup.hash; }
         static bool match(TimeZoneName key, const Lookup& lookup);
     };
 
     using TimeZoneSet = js::GCHashSet<TimeZoneName,
                                       TimeZoneHasher,
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -59,23 +59,31 @@ HashableValue::setValue(JSContext* cx, H
         value = v;
     }
 
     MOZ_ASSERT(value.isUndefined() || value.isNull() || value.isBoolean() || value.isNumber() ||
                value.isString() || value.isSymbol() || value.isObject());
     return true;
 }
 
-HashNumber
-HashableValue::hash() const
+static HashNumber
+HashValue(const Value& v)
 {
     // HashableValue::setValue normalizes values so that the SameValue relation
     // on HashableValues is the same as the == relationship on
     // value.data.asBits.
-    return value.asRawBits();
+    if (v.isString())
+        return v.toString()->asAtom().hash();
+    return v.asRawBits();
+}
+
+HashNumber
+HashableValue::hash() const
+{
+    return HashValue(value);
 }
 
 bool
 HashableValue::operator==(const HashableValue& other) const
 {
     // Two HashableValues are equal if they have equal bits.
     bool b = (value.asRawBits() == other.value.asRawBits());
 
@@ -86,17 +94,17 @@ HashableValue::operator==(const Hashable
     RootedValue otherRoot(rcx, other.value);
     MOZ_ASSERT(SameValue(nullptr, valueRoot, otherRoot, &same));
     MOZ_ASSERT(same == b);
 #endif
     return b;
 }
 
 HashableValue
-HashableValue::mark(JSTracer* trc) const
+HashableValue::trace(JSTracer* trc) const
 {
     HashableValue hv(*this);
     TraceEdge(trc, &hv.value, "key");
     return hv;
 }
 
 
 /*** MapIterator *********************************************************************************/
@@ -270,17 +278,17 @@ const ClassOps MapObject::classOps_ = {
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
-    mark
+    trace
 };
 
 const ClassSpec MapObject::classSpec_ = {
     GenericCreateConstructor<MapObject::construct, 0, gc::AllocKind::FUNCTION>,
     CreateMapPrototype,
     nullptr,
     MapObject::staticProperties,
     MapObject::methods,
@@ -328,41 +336,41 @@ const JSFunctionSpec MapObject::methods[
 
 const JSPropertySpec MapObject::staticProperties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "MapSpecies", 0),
     JS_PS_END
 };
 
 template <class Range>
 static void
-MarkKey(Range& r, const HashableValue& key, JSTracer* trc)
+TraceKey(Range& r, const HashableValue& key, JSTracer* trc)
 {
-    HashableValue newKey = key.mark(trc);
+    HashableValue newKey = key.trace(trc);
 
     if (newKey.get() != key.get()) {
         // The hash function only uses the bits of the Value, so it is safe to
         // rekey even when the object or string has been modified by the GC.
         r.rekeyFront(newKey);
     }
 }
 
 void
-MapObject::mark(JSTracer* trc, JSObject* obj)
+MapObject::trace(JSTracer* trc, JSObject* obj)
 {
     if (ValueMap* map = obj->as<MapObject>().getData()) {
         for (ValueMap::Range r = map->all(); !r.empty(); r.popFront()) {
-            MarkKey(r, r.front().key, trc);
+            TraceKey(r, r.front().key, trc);
             TraceEdge(trc, &r.front().value, "value");
         }
     }
 }
 
 struct js::UnbarrieredHashPolicy {
     typedef Value Lookup;
-    static HashNumber hash(const Lookup& v) { return v.asRawBits(); }
+    static HashNumber hash(const Lookup& v) { return HashValue(v); }
     static bool match(const Value& k, const Lookup& l) { return k == l; }
     static bool isEmpty(const Value& v) { return v.isMagic(JS_HASH_KEY_EMPTY); }
     static void makeEmpty(Value* vp) { vp->setMagic(JS_HASH_KEY_EMPTY); }
 };
 
 using NurseryKeysVector = Vector<JSObject*, 0, SystemAllocPolicy>;
 
 template <typename TableObject>
@@ -718,17 +726,17 @@ MapObject::delete_(JSContext *cx, Handle
         return false;
     }
     return true;
 }
 
 bool
 MapObject::delete_impl(JSContext *cx, const CallArgs& args)
 {
-    // MapObject::mark does not mark deleted entries. Incremental GC therefore
+    // MapObject::trace does not trace deleted entries. Incremental GC therefore
     // requires that no HeapPtr<Value> objects pointing to heap values be left
     // alive in the ValueMap.
     //
     // OrderedHashMap::remove() doesn't destroy the removed entry. It merely
     // calls OrderedHashMap::MapOps::makeEmpty. But that is sufficient, because
     // makeEmpty clears the value by doing e->value = Value(), and in the case
     // of a ValueMap, Value() means HeapPtr<Value>(), which is the same as
     // HeapPtr<Value>(UndefinedValue()).
@@ -988,17 +996,17 @@ const ClassOps SetObject::classOps_ = {
     nullptr, // setProperty
     nullptr, // enumerate
     nullptr, // resolve
     nullptr, // mayResolve
     finalize,
     nullptr, // call
     nullptr, // hasInstance
     nullptr, // construct
-    mark
+    trace
 };
 
 const ClassSpec SetObject::classSpec_ = {
     GenericCreateConstructor<SetObject::construct, 0, gc::AllocKind::FUNCTION>,
     CreateSetPrototype,
     nullptr,
     SetObject::staticProperties,
     SetObject::methods,
@@ -1097,22 +1105,22 @@ SetObject::create(JSContext* cx, HandleO
         return nullptr;
 
     obj->setPrivate(set.release());
     obj->setReservedSlot(NurseryKeysSlot, PrivateValue(nullptr));
     return obj;
 }
 
 void
-SetObject::mark(JSTracer* trc, JSObject* obj)
+SetObject::trace(JSTracer* trc, JSObject* obj)
 {
     SetObject* setobj = static_cast<SetObject*>(obj);
     if (ValueSet* set = setobj->getData()) {
         for (ValueSet::Range r = set->all(); !r.empty(); r.popFront())
-            MarkKey(r, r.front(), trc);
+            TraceKey(r, r.front(), trc);
     }
 }
 
 void
 SetObject::finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->onMainThread());
     SetObject* setobj = static_cast<SetObject*>(obj);
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -38,17 +38,17 @@ class HashableValue
         static void makeEmpty(HashableValue* vp) { vp->value = MagicValue(JS_HASH_KEY_EMPTY); }
     };
 
     HashableValue() : value(UndefinedValue()) {}
 
     MOZ_MUST_USE bool setValue(JSContext* cx, HandleValue v);
     HashNumber hash() const;
     bool operator==(const HashableValue& other) const;
-    HashableValue mark(JSTracer* trc) const;
+    HashableValue trace(JSTracer* trc) const;
     Value get() const { return value.get(); }
 
     void trace(JSTracer* trc) {
         TraceEdge(trc, &value, "HashableValue");
     }
 };
 
 template <>
@@ -128,17 +128,17 @@ class MapObject : public NativeObject {
     static const ClassOps classOps_;
 
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
     ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
     static ValueMap& extract(HandleObject o);
     static ValueMap& extract(const CallArgs& args);
-    static void mark(JSTracer* trc, JSObject* obj);
+    static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
     static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
     static bool is(HandleValue v);
     static bool is(HandleObject o);
 
     static MOZ_MUST_USE bool iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind);
 
@@ -230,17 +230,17 @@ class SetObject : public NativeObject {
 
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSPropertySpec staticProperties[];
 
     ValueSet* getData() { return static_cast<ValueSet*>(getPrivate()); }
     static ValueSet& extract(HandleObject o);
     static ValueSet& extract(const CallArgs& args);
-    static void mark(JSTracer* trc, JSObject* obj);
+    static void trace(JSTracer* trc, JSObject* obj);
     static void finalize(FreeOp* fop, JSObject* obj);
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
 
     static bool is(HandleValue v);
     static bool is(HandleObject o);
 
     static bool isBuiltinAdd(HandleValue add, JSContext* cx);
 
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -219,17 +219,17 @@ JS_NondeterministicGetWeakMapKeys(JSCont
                 return false;
         }
     }
     ret.set(arr);
     return true;
 }
 
 static void
-WeakMap_mark(JSTracer* trc, JSObject* obj)
+WeakMap_trace(JSTracer* trc, JSObject* obj)
 {
     if (ObjectValueMap* map = obj->as<WeakMapObject>().getMap())
         map->trace(trc);
 }
 
 static void
 WeakMap_finalize(FreeOp* fop, JSObject* obj)
 {
@@ -321,17 +321,17 @@ static const ClassOps WeakMapObjectClass
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     WeakMap_finalize,
     nullptr, /* call */
     nullptr, /* hasInstance */
     nullptr, /* construct */
-    WeakMap_mark
+    WeakMap_trace
 };
 
 const Class WeakMapObject::class_ = {
     "WeakMap",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap) |
     JSCLASS_BACKGROUND_FINALIZE,
     &WeakMapObjectClassOps
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -110,16 +110,16 @@ IsIdentifier(JSLinearString* str);
  */
 bool
 IsIdentifier(const char16_t* chars, size_t length);
 
 /* True if str is a keyword. Defined in TokenStream.cpp. */
 bool
 IsKeyword(JSLinearString* str);
 
-/* GC marking. Defined in Parser.cpp. */
+/* Trace all GC things reachable from parser. Defined in Parser.cpp. */
 void
-MarkParser(JSTracer* trc, JS::AutoGCRooter* parser);
+TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_BytecodeCompiler_h */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -851,17 +851,17 @@ ModuleSharedContext::ModuleSharedContext
 template <typename ParseHandler>
 void
 Parser<ParseHandler>::trace(JSTracer* trc)
 {
     ObjectBox::TraceList(trc, traceListHead);
 }
 
 void
-MarkParser(JSTracer* trc, AutoGCRooter* parser)
+TraceParser(JSTracer* trc, AutoGCRooter* parser)
 {
     static_cast<Parser<FullParseHandler>*>(parser)->trace(trc);
 }
 
 /*
  * Parse a top-level JS script.
  */
 template <typename ParseHandler>
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -964,17 +964,17 @@ class Parser final : private JS::AutoGCR
         m.traceListHead = traceListHead;
         return m;
     }
     void release(Mark m) {
         alloc.release(m.mark);
         traceListHead = m.traceListHead;
     }
 
-    friend void js::frontend::MarkParser(JSTracer* trc, JS::AutoGCRooter* parser);
+    friend void js::frontend::TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
     const char* getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
 
     /*
      * Parse a top-level JS script.
      */
     Node parse();
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -186,22 +186,25 @@ bool
 GCRuntime::checkAllocatorState(JSContext* cx, AllocKind kind)
 {
     if (allowGC) {
         if (!gcIfNeededPerAllocation(cx))
             return false;
     }
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
-    MOZ_ASSERT_IF(rt->isAtomsCompartment(cx->compartment()),
-                  kind == AllocKind::STRING ||
-                  kind == AllocKind::FAT_INLINE_STRING ||
+    MOZ_ASSERT_IF(cx->compartment()->isAtomsCompartment(),
+                  kind == AllocKind::ATOM ||
+                  kind == AllocKind::FAT_INLINE_ATOM ||
                   kind == AllocKind::SYMBOL ||
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
+    MOZ_ASSERT_IF(!cx->compartment()->isAtomsCompartment(),
+                  kind != AllocKind::ATOM &&
+                  kind != AllocKind::FAT_INLINE_ATOM);
     MOZ_ASSERT(!rt->isHeapBusy());
     MOZ_ASSERT(isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
         rt->gc.verifyIsSafeToGC();
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -240,25 +240,16 @@ CurrentThreadIsIonCompilingSafeForMinorG
 
 bool
 CurrentThreadIsGCSweeping();
 
 bool
 IsMarkedBlack(NativeObject* obj);
 #endif
 
-namespace gc {
-
-// Marking.h depends on these barrier definitions, so we need a separate
-// entry point for marking to implement the pre-barrier.
-void MarkValueForBarrier(JSTracer* trc, Value* v, const char* name);
-void MarkIdForBarrier(JSTracer* trc, jsid* idp, const char* name);
-
-} // namespace gc
-
 template <typename T>
 struct InternalBarrierMethods {};
 
 template <typename T>
 struct InternalBarrierMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -105,16 +105,18 @@ enum class AllocKind {
     LAZY_SCRIPT,
     SHAPE,
     ACCESSOR_SHAPE,
     BASE_SHAPE,
     OBJECT_GROUP,
     FAT_INLINE_STRING,
     STRING,
     EXTERNAL_STRING,
+    FAT_INLINE_ATOM,
+    ATOM,
     SYMBOL,
     JITCODE,
     SCOPE,
     LIMIT,
     LAST = LIMIT - 1
 };
 
 // Macro to enumerate the different allocation kinds supplying information about
@@ -142,16 +144,18 @@ enum class AllocKind {
     D(LAZY_SCRIPT,         LazyScript,  js::LazyScript,    js::LazyScript) \
     D(SHAPE,               Shape,       js::Shape,         js::Shape) \
     D(ACCESSOR_SHAPE,      Shape,       js::AccessorShape, js::AccessorShape) \
     D(BASE_SHAPE,          BaseShape,   js::BaseShape,     js::BaseShape) \
     D(OBJECT_GROUP,        ObjectGroup, js::ObjectGroup,   js::ObjectGroup) \
     D(FAT_INLINE_STRING,   String,      JSFatInlineString, JSFatInlineString) \
     D(STRING,              String,      JSString,          JSString) \
     D(EXTERNAL_STRING,     String,      JSExternalString,  JSExternalString) \
+    D(FAT_INLINE_ATOM,     String,      js::FatInlineAtom, js::FatInlineAtom) \
+    D(ATOM,                String,      js::NormalAtom,    js::NormalAtom) \
     D(SYMBOL,              Symbol,      JS::Symbol,        JS::Symbol) \
     D(JITCODE,             JitCode,     js::jit::JitCode,  js::jit::JitCode) \
     D(SCOPE,               Scope,       js::Scope,         js::Scope)
 
 #define FOR_EACH_ALLOCKIND(D) \
     FOR_EACH_OBJECT_ALLOCKIND(D) \
     FOR_EACH_NONOBJECT_ALLOCKIND(D)
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -218,17 +218,17 @@ js::CheckTracedThing(JSTracer* trc, T* t
      * Do not check IsMarkingTracer directly -- it should only be used in paths
      * where we cannot be the gray buffering tracer.
      */
     bool isGcMarkingTracer = trc->isMarkingTracer();
 
     MOZ_ASSERT_IF(zone->requireGCTracer(), isGcMarkingTracer || IsBufferGrayRootsTracer(trc));
 
     if (isGcMarkingTracer) {
-        GCMarker* gcMarker = static_cast<GCMarker*>(trc);
+        GCMarker* gcMarker = GCMarker::fromTracer(trc);
         MOZ_ASSERT_IF(gcMarker->shouldCheckCompartments(),
                       zone->isCollecting() || zone->isAtomsZone());
 
         MOZ_ASSERT_IF(gcMarker->markColor() == GRAY,
                       !zone->isGCMarkingBlack() || zone->isAtomsZone());
 
         MOZ_ASSERT(!(zone->isGCSweeping() || zone->isGCFinished() || zone->isGCCompacting()));
     }
@@ -266,22 +266,22 @@ js::CheckTracedThing(JSTracer* trc, T th
 namespace js {
 #define IMPL_CHECK_TRACED_THING(_, type, __) \
     template void CheckTracedThing<type>(JSTracer*, type*);
 JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_THING);
 #undef IMPL_CHECK_TRACED_THING
 } // namespace js
 
 static bool
-ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
+ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
 
-    uint32_t color = static_cast<GCMarker*>(trc)->markColor();
+    uint32_t color = GCMarker::fromTracer(trc)->markColor();
     MOZ_ASSERT(color == BLACK || color == GRAY);
 
     if (!cell->isTenured()) {
         MOZ_ASSERT(color == BLACK);
         return false;
     }
     TenuredCell& tenured = cell->asTenured();
 
@@ -310,19 +310,19 @@ ShouldMarkCrossCompartment(JSTracer* trc
                 DelayCrossCompartmentGrayMarking(src);
             return false;
         }
         return zone->isGCMarkingGray();
     }
 }
 
 static bool
-ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, const Value& val)
+ShouldTraceCrossCompartment(JSTracer* trc, JSObject* src, const Value& val)
 {
-    return val.isMarkable() && ShouldMarkCrossCompartment(trc, src, (Cell*)val.toGCThing());
+    return val.isMarkable() && ShouldTraceCrossCompartment(trc, src, (Cell*)val.toGCThing());
 }
 
 static void
 AssertZoneIsMarking(Cell* thing)
 {
     MOZ_ASSERT(TenuredCell::fromPointer(thing)->zone()->isGCMarking());
 }
 
@@ -462,17 +462,17 @@ js::UnsafeTraceManuallyBarrieredEdge(JST
 template <typename T>
 void
 js::TraceWeakEdge(JSTracer* trc, WeakRef<T>* thingp, const char* name)
 {
     // Non-marking tracers treat the edge strongly.
     if (!trc->isMarkingTracer())
         return DispatchToTracer(trc, ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
 
-    NoteWeakEdge(static_cast<GCMarker*>(trc),
+    NoteWeakEdge(GCMarker::fromTracer(trc),
                  ConvertToBase(thingp->unsafeUnbarrieredForTracing()));
 }
 
 template <typename T>
 void
 js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
 {
     AssertRootMarkingPhase(trc);
@@ -560,30 +560,30 @@ FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTI
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
 #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS
 
 template <typename T>
 void
 js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
                                                const char* name)
 {
-    if (ShouldMarkCrossCompartment(trc, src, *dst))
+    if (ShouldTraceCrossCompartment(trc, src, *dst))
         DispatchToTracer(trc, dst, name);
 }
 template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSObject*>(JSTracer*, JSObject*,
                                                                         JSObject**, const char*);
 template void js::TraceManuallyBarrieredCrossCompartmentEdge<JSScript*>(JSTracer*, JSObject*,
                                                                         JSScript**, const char*);
 
 template <typename T>
 void
 js::TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src, WriteBarrieredBase<T>* dst,
                               const char* name)
 {
-    if (ShouldMarkCrossCompartment(trc, src, dst->get()))
+    if (ShouldTraceCrossCompartment(trc, src, dst->get()))
         DispatchToTracer(trc, dst->unsafeUnbarrieredForTracing(), name);
 }
 template void js::TraceCrossCompartmentEdge<Value>(JSTracer*, JSObject*,
                                                    WriteBarrieredBase<Value>*, const char*);
 
 template <typename T>
 void
 js::TraceProcessGlobalRoot(JSTracer* trc, T* thing, const char* name)
@@ -653,17 +653,17 @@ DispatchToTracer(JSTracer* trc, T* thing
     static_assert(
             JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
             mozilla::IsSame<T, JS::Value>::value ||
             mozilla::IsSame<T, jsid>::value ||
             mozilla::IsSame<T, TaggedProto>::value,
             "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
     if (trc->isMarkingTracer())
-        return DoMarking(static_cast<GCMarker*>(trc), *thingp);
+        return DoMarking(GCMarker::fromTracer(trc), *thingp);
     if (trc->isTenuringTracer())
         return static_cast<TenuringTracer*>(trc)->traverse(thingp);
     MOZ_ASSERT(trc->isCallbackTracer());
     DoCallback(trc->asCallbackTracer(), thingp, name);
 }
 
 
 /*** GC Marking Interface *************************************************************************/
@@ -690,17 +690,17 @@ struct ImplicitEdgeHolderType<JSScript*>
     typedef JSScript* Type;
 };
 
 void
 GCMarker::markEphemeronValues(gc::Cell* markedCell, WeakEntryVector& values)
 {
     size_t initialLen = values.length();
     for (size_t i = 0; i < initialLen; i++)
-        values[i].weakmap->traceEntry(this, markedCell, values[i].key);
+        values[i].weakmap->markEntry(this, markedCell, values[i].key);
 
     // The vector should not be appended to during iteration because the key is
     // already marked, and even in cases where we have a multipart key, we
     // should only be inserting entries for the unmarked portions.
     MOZ_ASSERT(values.length() == initialLen);
 }
 
 template <typename T>
@@ -2084,17 +2084,17 @@ GCMarker::enterWeakMarkingMode()
     // weakmap marking, this initialization step will become unnecessary, as
     // the table will already hold all such keys.)
     if (weakMapAction() == ExpandWeakMaps) {
         tag_ = TracerKindTag::WeakMarking;
 
         for (GCZoneGroupIter zone(runtime()); !zone.done(); zone.next()) {
             for (WeakMapBase* m : zone->gcWeakMapList) {
                 if (m->marked)
-                    (void) m->traceEntries(this);
+                    (void) m->markIteratively(this);
             }
         }
     }
 }
 
 void
 GCMarker::leaveWeakMarkingMode()
 {
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -232,16 +232,21 @@ class GCMarker : public JSTracer
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #ifdef DEBUG
     bool shouldCheckCompartments() { return strictCompartmentChecking; }
 #endif
 
     void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
 
+    static GCMarker* fromTracer(JSTracer* trc) {
+        MOZ_ASSERT(trc->isMarkingTracer());
+        return static_cast<GCMarker*>(trc);
+    }
+
   private:
 #ifdef DEBUG
     void checkZone(void* p);
 #else
     void checkZone(void* p) {}
 #endif
 
     /*
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -700,17 +700,17 @@ js::Nursery::doCollection(JSRuntime* rt,
 
     maybeStartProfile(ProfileKey::MarkRuntime);
     rt->gc.traceRuntimeForMinorGC(&mover, session.lock);
     maybeEndProfile(ProfileKey::MarkRuntime);
 
     maybeStartProfile(ProfileKey::MarkDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
-        Debugger::markAll(&mover);
+        Debugger::traceAll(&mover);
     }
     maybeEndProfile(ProfileKey::MarkDebugger);
 
     maybeStartProfile(ProfileKey::ClearNewObjectCache);
     rt->contextFromMainThread()->caches.newObjectCache.clearNurseryObjects(rt);
     maybeEndProfile(ProfileKey::ClearNewObjectCache);
 
     // Most of the work is done here. This loop iterates over objects that have
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -51,78 +51,78 @@ using TraceFunction = void (*)(JSTracer*
 // the real trace function has been stored inline in the DispatchWrapper.
 struct ConcreteTraceable {
     ConcreteTraceable() { MOZ_CRASH("instantiation of ConcreteTraceable"); }
     void trace(JSTracer*) {}
 };
 
 template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
 static inline void
-MarkExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name)
+TraceExactStackRootList(JSTracer* trc, JS::Rooted<void*>* rooter, const char* name)
 {
     while (rooter) {
         T* addr = reinterpret_cast<JS::Rooted<T>*>(rooter)->address();
         TraceFn(trc, addr, name);
         rooter = rooter->previous();
     }
 }
 
 static inline void
 TraceStackRoots(JSTracer* trc, RootedListHeads& stackRoots)
 {
-#define MARK_ROOTS(name, type, _) \
-    MarkExactStackRootList<type*>(trc, stackRoots[JS::RootKind::name], "exact-" #name);
-JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
-#undef MARK_ROOTS
-    MarkExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id");
-    MarkExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value], "exact-value");
-    MarkExactStackRootList<ConcreteTraceable,
+#define TRACE_ROOTS(name, type, _) \
+    TraceExactStackRootList<type*>(trc, stackRoots[JS::RootKind::name], "exact-" #name);
+JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
+#undef TRACE_ROOTS
+    TraceExactStackRootList<jsid>(trc, stackRoots[JS::RootKind::Id], "exact-id");
+    TraceExactStackRootList<Value>(trc, stackRoots[JS::RootKind::Value], "exact-value");
+    TraceExactStackRootList<ConcreteTraceable,
                            js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(
         trc, stackRoots[JS::RootKind::Traceable], "Traceable");
 }
 
 void
 js::RootLists::traceStackRoots(JSTracer* trc)
 {
     TraceStackRoots(trc, stackRoots_);
 }
 
 static void
-MarkExactStackRoots(JSRuntime* rt, JSTracer* trc)
+TraceExactStackRoots(JSRuntime* rt, JSTracer* trc)
 {
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next())
         TraceStackRoots(trc, zone->stackRoots_);
     rt->contextFromMainThread()->roots.traceStackRoots(trc);
 }
 
 template <typename T, TraceFunction<T> TraceFn = TraceNullableRoot>
 static inline void
-MarkPersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
+TracePersistentRootedList(JSTracer* trc, mozilla::LinkedList<PersistentRooted<void*>>& list,
                          const char* name)
 {
     for (PersistentRooted<void*>* r : list)
         TraceFn(trc, reinterpret_cast<PersistentRooted<T>*>(r)->address(), name);
 }
 
 void
 js::RootLists::tracePersistentRoots(JSTracer* trc)
 {
-#define MARK_ROOTS(name, type, _) \
-    MarkPersistentRootedList<type*>(trc, heapRoots_[JS::RootKind::name], "persistent-" #name);
-JS_FOR_EACH_TRACEKIND(MARK_ROOTS)
-#undef MARK_ROOTS
-    MarkPersistentRootedList<jsid>(trc, heapRoots_[JS::RootKind::Id], "persistent-id");
-    MarkPersistentRootedList<Value>(trc, heapRoots_[JS::RootKind::Value], "persistent-value");
-    MarkPersistentRootedList<ConcreteTraceable,
+#define TRACE_ROOTS(name, type, _) \
+    TracePersistentRootedList<type*>(trc, heapRoots_[JS::RootKind::name], "persistent-" #name);
+JS_FOR_EACH_TRACEKIND(TRACE_ROOTS)
+#undef TRACE_ROOTS
+    TracePersistentRootedList<jsid>(trc, heapRoots_[JS::RootKind::Id], "persistent-id");
+    TracePersistentRootedList<Value>(trc, heapRoots_[JS::RootKind::Value], "persistent-value");
+    TracePersistentRootedList<ConcreteTraceable,
                              js::DispatchWrapper<ConcreteTraceable>::TraceWrapped>(trc,
             heapRoots_[JS::RootKind::Traceable], "persistent-traceable");
 }
 
 static void
-MarkPersistentRooted(JSRuntime* rt, JSTracer* trc)
+TracePersistentRooted(JSRuntime* rt, JSTracer* trc)
 {
     rt->contextFromMainThread()->roots.tracePersistentRoots(trc);
 }
 
 template <typename T>
 static void
 FinishPersistentRootedChain(mozilla::LinkedList<PersistentRooted<void*>>& listArg)
 {
@@ -146,17 +146,17 @@ JS_FOR_EACH_TRACEKIND(FINISH_ROOT_LIST)
     // See the comment on RootLists::~RootLists for details.
 }
 
 inline void
 AutoGCRooter::trace(JSTracer* trc)
 {
     switch (tag_) {
       case PARSER:
-        frontend::MarkParser(trc, this);
+        frontend::TraceParser(trc, this);
         return;
 
       case VALARRAY: {
         /*
          * We don't know the template size parameter, but we can safely treat it
          * as an AutoValueArray<1> because the length is stored separately.
          */
         AutoValueArray<1>* array = static_cast<AutoValueArray<1>*>(this);
@@ -166,29 +166,29 @@ AutoGCRooter::trace(JSTracer* trc)
 
       case IONMASM: {
         static_cast<js::jit::MacroAssembler::AutoRooter*>(this)->masm()->trace(trc);
         return;
       }
 
       case WRAPPER: {
         /*
-         * We need to use TraceManuallyBarrieredEdge here because we mark
+         * We need to use TraceManuallyBarrieredEdge here because we trace
          * wrapper roots in every slice. This is because of some rule-breaking
          * in RemapAllWrappersForObject; see comment there.
          */
         TraceManuallyBarrieredEdge(trc, &static_cast<AutoWrapperRooter*>(this)->value.get(),
                                    "JS::AutoWrapperRooter.value");
         return;
       }
 
       case WRAPVECTOR: {
         AutoWrapperVector::VectorImpl& vector = static_cast<AutoWrapperVector*>(this)->vector;
         /*
-         * We need to use TraceManuallyBarrieredEdge here because we mark
+         * We need to use TraceManuallyBarrieredEdge here because we trace
          * wrapper roots in every slice. This is because of some rule-breaking
          * in RemapAllWrappersForObject; see comment there.
          */
         for (WrapperValue* p = vector.begin(); p < vector.end(); p++)
             TraceManuallyBarrieredEdge(trc, &p->get(), "js::AutoWrapperVector.vector");
         return;
       }
 
@@ -276,17 +276,17 @@ js::gc::GCRuntime::traceRuntimeForMinorG
     // does not clear the crossCompartmentWrapper map. It cannot do this
     // because Proxy's trace for CrossCompartmentWrappers asserts presence in
     // the map. And we can reach its trace function despite having finished the
     // roots via the edges stored by the pre-barrier verifier when we finish
     // the verifier for the last time.
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
 
     // FIXME: As per bug 1298816 comment 12, we should be able to remove this.
-    jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
+    jit::JitRuntime::TraceJitcodeGlobalTable(trc);
 
     traceRuntimeCommon(trc, TraceRuntime, lock);
 }
 
 void
 js::TraceRuntime(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->isMarkingTracer());
@@ -307,62 +307,62 @@ js::gc::GCRuntime::traceRuntime(JSTracer
     traceRuntimeAtoms(trc, lock);
     traceRuntimeCommon(trc, TraceRuntime, lock);
 }
 
 void
 js::gc::GCRuntime::traceRuntimeAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
-    MarkPermanentAtoms(trc);
-    MarkAtoms(trc, lock);
-    MarkWellKnownSymbols(trc);
-    jit::JitRuntime::Mark(trc, lock);
+    TracePermanentAtoms(trc);
+    TraceAtoms(trc, lock);
+    TraceWellKnownSymbols(trc);
+    jit::JitRuntime::Trace(trc, lock);
 }
 
 void
 js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
                                       AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_STACK);
 
         // Trace active interpreter and JIT stack roots.
-        MarkInterpreterActivations(rt, trc);
-        jit::MarkJitActivations(rt, trc);
+        TraceInterpreterActivations(rt, trc);
+        jit::TraceJitActivations(rt, trc);
 
         // Trace legacy C stack roots.
         AutoGCRooter::traceAll(trc);
 
         for (RootRange r = rootsHash.all(); !r.empty(); r.popFront()) {
             const RootEntry& entry = r.front();
             TraceRoot(trc, entry.key(), entry.value());
         }
 
         // Trace C stack roots.
-        MarkExactStackRoots(rt, trc);
+        TraceExactStackRoots(rt, trc);
     }
 
     // Trace runtime global roots.
-    MarkPersistentRooted(rt, trc);
+    TracePersistentRooted(rt, trc);
 
     // Trace the self-hosting global compartment.
-    rt->markSelfHostingGlobal(trc);
+    rt->traceSelfHostingGlobal(trc);
 
     // Trace the shared Intl data.
     rt->traceSharedIntlData(trc);
 
     // Trace anything in the single context. Note that this is actually the
     // same struct as the JSRuntime, but is still split for historical reasons.
-    rt->contextFromMainThread()->mark(trc);
+    rt->contextFromMainThread()->trace(trc);
 
     // Trace all compartment roots, but not the compartment itself; it is
-    // marked via the parent pointer if traceRoots actually traces anything.
+    // traced via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
     // Trace SPS.
     rt->spsProfiler.trace(trc);
 
     // Trace helper thread roots.
     HelperThreadState().trace(trc);
@@ -378,17 +378,17 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
          * the nursery should be in the store buffer, and we want to avoid the
          * time taken to trace all these roots.
          */
         for (size_t i = 0; i < blackRootTracers.length(); i++) {
             const Callback<JSTraceDataOp>& e = blackRootTracers[i];
             (*e.op)(trc, e.data);
         }
 
-        /* During GC, we don't mark gray roots at this stage. */
+        /* During GC, we don't trace gray roots at this stage. */
         if (JSTraceDataOp op = grayRootTracer.op) {
             if (traceOrMark == TraceRuntime)
                 (*op)(trc, grayRootTracer.data);
         }
     }
 }
 
 #ifdef DEBUG
--- a/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-string.js
@@ -39,46 +39,48 @@ function tByteSize(obj) {
 // representation       Latin-1   char16_t      Latin-1   char16_t    label
 // ========================================================================
 // JSExternalString            (cannot be tested in shell)            -
 // JSThinInlineString   7         3             15        7           T
 // JSFatInlineString    23        11            23        11          F
 // JSExtensibleString          - limited by available memory -        X
 // JSUndependedString          - same as JSExtensibleString -
 
+// Note that atoms are 8 bytes larger than non-atoms, to store the atom's hash code.
+
 // Latin-1
-assertEq(tByteSize(""),                                                 s(16, 24)); // T, T
-assertEq(tByteSize("1"),                                                s(16, 24)); // T, T
-assertEq(tByteSize("1234567"),                                          s(16, 24)); // T, T
-assertEq(tByteSize("12345678"),                                         s(32, 24)); // F, T
-assertEq(tByteSize("123456789.12345"),                                  s(32, 24)); // F, T
-assertEq(tByteSize("123456789.123456"),                                 s(32, 32)); // F, F
-assertEq(tByteSize("123456789.123456789.123"),                          s(32, 32)); // F, F
-assertEq(tByteSize("123456789.123456789.1234"),                         s(48, 56)); // X, X
-assertEq(tByteSize("123456789.123456789.123456789.1"),                  s(48, 56)); // X, X
-assertEq(tByteSize("123456789.123456789.123456789.12"),                 s(64, 72)); // X, X
+assertEq(tByteSize(""),                                                 s(24, 32)); // T, T
+assertEq(tByteSize("1"),                                                s(24, 32)); // T, T
+assertEq(tByteSize("1234567"),                                          s(24, 32)); // T, T
+assertEq(tByteSize("12345678"),                                         s(40, 32)); // F, T
+assertEq(tByteSize("123456789.12345"),                                  s(40, 32)); // F, T
+assertEq(tByteSize("123456789.123456"),                                 s(40, 40)); // F, F
+assertEq(tByteSize("123456789.123456789.123"),                          s(40, 40)); // F, F
+assertEq(tByteSize("123456789.123456789.1234"),                         s(56, 64)); // X, X
+assertEq(tByteSize("123456789.123456789.123456789.1"),                  s(56, 64)); // X, X
+assertEq(tByteSize("123456789.123456789.123456789.12"),                 s(72, 80)); // X, X
 
 // Inline char16_t atoms.
 // "Impassionate gods have never seen the red that is the Tatsuta River."
 //   - Ariwara no Narihira
-assertEq(tByteSize("千"),						s(16, 24)); // T, T
-assertEq(tByteSize("千早"),    						s(16, 24)); // T, T
-assertEq(tByteSize("千早ぶ"),    					s(16, 24)); // T, T
-assertEq(tByteSize("千早ぶる"),    					s(32, 24)); // F, T
-assertEq(tByteSize("千早ぶる神"),    					s(32, 24)); // F, T
-assertEq(tByteSize("千早ぶる神代"),					s(32, 24)); // F, T
-assertEq(tByteSize("千早ぶる神代も"),					s(32, 24)); // F, T
-assertEq(tByteSize("千早ぶる神代もき"),					s(32, 32)); // F, F
-assertEq(tByteSize("千早ぶる神代もきかず龍"),				s(32, 32)); // F, F
-assertEq(tByteSize("千早ぶる神代もきかず龍田"),    			s(48, 56)); // X, X
-assertEq(tByteSize("千早ぶる神代もきかず龍田川 か"),    			s(48, 56)); // X, X
-assertEq(tByteSize("千早ぶる神代もきかず龍田川 から"),    			s(64, 72)); // X, X
-assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水く"),    	s(64, 72)); // X, X
-assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くく"),    	s(80, 88)); // X, X
-assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くくるとは"),	s(80, 88)); // X, X
+assertEq(tByteSize("千"),						s(24, 32)); // T, T
+assertEq(tByteSize("千早"),    						s(24, 32)); // T, T
+assertEq(tByteSize("千早ぶ"),    					s(24, 32)); // T, T
+assertEq(tByteSize("千早ぶる"),    					s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神"),    					s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代"),					s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代も"),					s(40, 32)); // F, T
+assertEq(tByteSize("千早ぶる神代もき"),					s(40, 40)); // F, F
+assertEq(tByteSize("千早ぶる神代もきかず龍"),				s(40, 40)); // F, F
+assertEq(tByteSize("千早ぶる神代もきかず龍田"),    			s(56, 64)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 か"),    			s(56, 64)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 から"),    			s(72, 80)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水く"),    	s(72, 80)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くく"),    	s(88, 96)); // X, X
+assertEq(tByteSize("千早ぶる神代もきかず龍田川 からくれなゐに水くくるとは"),	s(88, 96)); // X, X
 
 // A Latin-1 rope. This changes size when flattened.
 // "In a village of La Mancha, the name of which I have no desire to call to mind"
 //   - Miguel de Cervantes, Don Quixote
 var fragment8 = "En un lugar de la Mancha, de cuyo nombre no quiero acordarme"; // 60 characters
 var rope8 = fragment8;
 for (var i = 0; i < 10; i++) // 1024 repetitions
   rope8 = rope8 + rope8;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/fold-linear-arith-bug1319242.js
@@ -0,0 +1,7 @@
+function f(x) {
+    // Check that we do not fold +1 and -2 across truncated/non-truncated operations.
+    return (((x | 0) + 1) | 0) + -2;
+}
+const int32_min = -Math.pow(2,31);
+f(Infinity);
+assertEq(f(int32_min - 1), int32_min - 2);
--- a/js/src/jit-test/tests/wasm/full-cycle.js
+++ b/js/src/jit-test/tests/wasm/full-cycle.js
@@ -104,8 +104,40 @@ wasmFullPass(`(module
         i32.add
         set_global $g
     )
     (start $start)
     (func (export "run") (result i32)
         get_global $g
     )
 )`, 1);
+
+// Branch table.
+for (let [p, result] of [
+    [0, 7],
+    [1, 6],
+    [2, 4],
+    [42, 4]
+]) {
+    wasmFullPass(`(module
+        (func (export "run") (result i32) (param $p i32) (local $n i32)
+            i32.const 0
+            set_local $n
+            block $c block $b block $a
+                get_local $p
+                br_table $a $b $c
+            end $a
+                get_local $n
+                i32.const 1
+                i32.add
+                set_local $n
+            end $b
+                get_local $n
+                i32.const 2
+                i32.add
+                set_local $n
+            end $c
+            get_local $n
+            i32.const 4
+            i32.add
+        )
+    )`, result, {}, p);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/baseline-pop-before-capture.js
@@ -0,0 +1,14 @@
+// Bug 1319415
+
+load(libdir + "wasm.js");
+
+var src =
+`(module
+  (func (result i32)
+    i32.const 0
+    i32.const 1
+    br_if 0
+    unreachable)
+  (export "run" 0))`;
+
+wasmFullPass(src, 0);
--- a/js/src/jit-test/tests/wasm/spec/address.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/address.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['address.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['address.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/binary.wast
+++ b/js/src/jit-test/tests/wasm/spec/binary.wast
@@ -1,15 +1,18 @@
 (module "\00asm\0d\00\00\00")
 (module "\00asm" "\0d\00\00\00")
 (module $M1 "\00asm\0d\00\00\00")
 (module $M2 "\00asm" "\0d\00\00\00")
 
 (assert_malformed (module "") "unexpected end")
 (assert_malformed (module "\01") "unexpected end")
 (assert_malformed (module "\00as") "unexpected end")
-(assert_malformed (module "\01") "unexpected end")
 (assert_malformed (module "asm\00") "magic header not detected")
+(assert_malformed (module "msa\00") "magic header not detected")
+(assert_malformed (module "msa\00\0d\00\00\00") "magic header not detected")
+(assert_malformed (module "msa\00\00\00\00\0d") "magic header not detected")
 
 (assert_malformed (module "\00asm") "unexpected end")
 (assert_malformed (module "\00asm\0d") "unexpected end")
 (assert_malformed (module "\00asm\0d\00\00") "unexpected end")
 (assert_malformed (module "\00asm\0e\00\00\00") "unknown binary version")
+(assert_malformed (module "\00asm\00\00\00\0d") "unknown binary version")
--- a/js/src/jit-test/tests/wasm/spec/binary.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/binary.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['binary.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['binary.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/block.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/block.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['block.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['block.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/br.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/br.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['br.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['br.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/br_if.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/br_if.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['br_if.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['br_if.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/br_table.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/br_table.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['br_table.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['br_table.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/break-drop.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/break-drop.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['break-drop.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['break-drop.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/call.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/call.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['call.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['call.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/call_indirect.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/call_indirect.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['call_indirect.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['call_indirect.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/comments.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/comments.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['comments.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['comments.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/conversions.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/conversions.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['conversions.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['conversions.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/endianness.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/endianness.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['endianness.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['endianness.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/exports.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/exports.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['exports.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['exports.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/f32.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/f32.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['f32.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['f32.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/f32_cmp.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/f32_cmp.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['f32_cmp.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['f32_cmp.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/f64.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/f64.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['f64.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['f64.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/f64_cmp.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/f64_cmp.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['f64_cmp.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['f64_cmp.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/fac.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/fac.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['fac.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['fac.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/float_exprs.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/float_exprs.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['float_exprs.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['float_exprs.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/float_literals.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/float_literals.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['float_literals.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['float_literals.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/float_memory.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/float_memory.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['float_memory.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['float_memory.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/float_misc.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/float_misc.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['float_misc.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['float_misc.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/forward.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/forward.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['forward.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['forward.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/func.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/func.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['func.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['func.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/func_ptrs.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/func_ptrs.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['func_ptrs.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['func_ptrs.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/get_local.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/get_local.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['get_local.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['get_local.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/globals.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/globals.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['globals.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['globals.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/i32.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/i32.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['i32.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['i32.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/i64.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/i64.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['i64.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['i64.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/import_tests.sh
+++ b/js/src/jit-test/tests/wasm/spec/import_tests.sh
@@ -6,10 +6,10 @@ git clone https://github.com/WebAssembly
 mv spec/interpreter/test/*.wast ./
 rm -rf spec/
 
 # TODO not handled yet
 rm -f *.fail.wast
 
 for i in $(ls *.wast);
 do
-    echo "var importedArgs = ['$i']; load(scriptdir + '../spec.js');" > $i.js
+    echo "var importedArgs = ['$i']; load(scriptdir + '../wast.js');" > $i.js
 done;
--- a/js/src/jit-test/tests/wasm/spec/imports.wast
+++ b/js/src/jit-test/tests/wasm/spec/imports.wast
@@ -2,76 +2,90 @@
 
 (module
   (func (export "func"))
   (func (export "func-i32") (param i32))
   (func (export "func-f32") (param f32))
   (func (export "func->i32") (result i32) (i32.const 22))
   (func (export "func->f32") (result f32) (f32.const 11))
   (func (export "func-i32->i32") (param i32) (result i32) (get_local 0))
+  (func (export "func-i64->i64") (param i64) (result i64) (get_local 0))
   (global (export "global-i32") i32 (i32.const 55))
   (global (export "global-f32") f32 (f32.const 44))
   (table (export "table-10-inf") 10 anyfunc)
   ;; (table (export "table-10-20") 10 20 anyfunc)
   (memory (export "memory-2-inf") 2)
   ;; (memory (export "memory-2-4") 2 4)
 )
 
 (register "test")
 
 
 ;; Functions
 
 (module
   (type $func_i32 (func (param i32)))
   (type $func_i64 (func (param i64)))
+  (type $func_f32 (func (param f32)))
+  (type $func_f64 (func (param f64)))
 
   (import "spectest" "print" (func (param i32)))
   (func (import "spectest" "print") (param i64))
   (import "spectest" "print" (func $print_i32 (param i32)))
   (import "spectest" "print" (func $print_i64 (param i64)))
+  (import "spectest" "print" (func $print_f32 (param f32)))
+  (import "spectest" "print" (func $print_f64 (param f64)))
   (import "spectest" "print" (func $print_i32_f32 (param i32 f32)))
-  (import "spectest" "print" (func $print_i64_f64 (param i64 f64)))
+  (import "spectest" "print" (func $print_f64_f64 (param f64 f64)))
   (func $print_i32-2 (import "spectest" "print") (param i32))
-  (func $print_i64-2 (import "spectest" "print") (param i64))
+  (func $print_f64-2 (import "spectest" "print") (param f64))
+  (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64)))
 
-  (table anyfunc (elem $print_i32 $print_i64))
+  (table anyfunc (elem $print_i32 $print_f64))
 
   (func (export "print32") (param $i i32)
+    (local $x f32)
+    (set_local $x (f32.convert_s/i32 (get_local $i)))
     (call 0 (get_local $i))
     (call $print_i32_f32
       (i32.add (get_local $i) (i32.const 1))
       (f32.const 42)
     )
     (call $print_i32 (get_local $i))
     (call $print_i32-2 (get_local $i))
+    (call $print_f32 (get_local $x))
     (call_indirect $func_i32 (get_local $i) (i32.const 0))
   )
 
   (func (export "print64") (param $i i64)
+    (local $x f64)
+    (set_local $x (f64.convert_s/i64 (call $i64->i64 (get_local $i))))
     (call 1 (get_local $i))
-    (call $print_i64_f64
-      (i64.add (get_local $i) (i64.const 1))
+    (call $print_f64_f64
+      (f64.add (get_local $x) (f64.const 1))
       (f64.const 53)
     )
-    (call $print_i64 (get_local $i))
-    (call $print_i64-2 (get_local $i))
-    (call_indirect $func_i64 (get_local $i) (i32.const 1))
+    ;; JavaScript can't handle i64 yet.
+    ;; (call $print_i64 (get_local $i))
+    (call $print_f64 (get_local $x))
+    (call $print_f64-2 (get_local $x))
+    (call_indirect $func_f64 (get_local $x) (i32.const 1))
   )
 )
 
 (assert_return (invoke "print32" (i32.const 13)))
 (assert_return (invoke "print64" (i64.const 24)))
 
 (module (import "test" "func" (func)))
 (module (import "test" "func-i32" (func (param i32))))
 (module (import "test" "func-f32" (func (param f32))))
 (module (import "test" "func->i32" (func (result i32))))
 (module (import "test" "func->f32" (func (result f32))))
 (module (import "test" "func-i32->i32" (func (param i32) (result i32))))
+(module (import "test" "func-i64->i64" (func (param i64) (result i64))))
 
 (assert_unlinkable
   (module (import "test" "unknown" (func)))
   "unknown import"
 )
 (assert_unlinkable
   (module (import "spectest" "unknown" (func)))
   "unknown import"
--- a/js/src/jit-test/tests/wasm/spec/imports.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/imports.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['imports.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['imports.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/int_exprs.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/int_exprs.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['int_exprs.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['int_exprs.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/int_literals.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/int_literals.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['int_literals.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['int_literals.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/labels.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/labels.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['labels.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['labels.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/left-to-right.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/left-to-right.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['left-to-right.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['left-to-right.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/linking.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/linking.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['linking.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['linking.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/loop.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/loop.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['loop.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['loop.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/memory.wast
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast
@@ -6,20 +6,21 @@
 ;; (module (memory 0 65536))
 (module (memory 0 0) (data (i32.const 0)))
 (module (memory 0 0) (data (i32.const 0) ""))
 (module (memory 1 1) (data (i32.const 0) "a"))
 (module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 65535) "b"))
 (module (memory 1 2)
   (data (i32.const 0) "a") (data (i32.const 1) "b") (data (i32.const 2) "c")
 )
-(module (memory 1) (global i32 (i32.const 0)) (data (get_global 0) "a"))
-(module (memory 1) (global $g i32 (i32.const 0)) (data (get_global $g) "a"))
-(module (memory 1) (data (get_global 0) "a") (global i32 (i32.const 0)))
-(module (memory 1) (data (get_global $g) "a") (global $g i32 (i32.const 0)))
+(module (global (import "spectest" "global") i32) (memory 1) (data (get_global 0) "a"))
+(module (global $g (import "spectest" "global") i32) (memory 1) (data (get_global $g) "a"))
+;; Use of internal globals in constant expressions is not allowed in MVP.
+;; (module (memory 1) (data (get_global 0) "a") (global i32 (i32.const 0)))
+;; (module (memory 1) (data (get_global $g) "a") (global $g i32 (i32.const 0)))
 
 (module (memory (data)) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 0))
 (module (memory (data "")) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 0))
 (module (memory (data "x")) (func (export "memsize") (result i32) (current_memory)))
 (assert_return (invoke "memsize") (i32.const 1))
 
@@ -34,36 +35,37 @@
 (assert_invalid
   (module (memory 1) (data (i32.ctz (i32.const 0))))
   "constant expression required"
 )
 (assert_invalid
   (module (memory 1) (data (nop)))
   "constant expression required"
 )
-(assert_invalid
-  (module (memory 1) (data (get_global $g)) (global $g (mut i32) (i32.const 0)))
-  "constant expression required"
-)
+;; Use of internal globals in constant expressions is not allowed in MVP.
+;; (assert_invalid
+;;   (module (memory 1) (data (get_global $g)) (global $g (mut i32) (i32.const 0)))
+;;   "constant expression required"
+;; )
 
 (assert_unlinkable
   (module (memory 0 0) (data (i32.const 0) "a"))
   "data segment does not fit"
 )
 (assert_unlinkable
   (module (memory 1 2) (data (i32.const 0) "a") (data (i32.const 98304) "b"))
   "data segment does not fit"
 )
 ;; This seems to cause a time-out on Travis.
 (;assert_unlinkable
   (module (memory 0x10000) (data (i32.const 0xffffffff) "ab"))
   ""  ;; either out of memory or segment does not fit
 ;)
 (assert_unlinkable
-  (module (memory 1) (data (get_global 0) "a") (global i32 (i32.const 0x10000)))
+  (module (global (import "spectest" "global") i32) (memory 0) (data (get_global 0) "a"))
   "data segment does not fit"
 )
 
 (module (memory 0 0) (data (i32.const 0) ""))
 (module (memory 0 0) (data (i32.const 1) ""))
 (module (memory 1 2) (data (i32.const 0) "abc") (data (i32.const 0) "def"))
 (module (memory 1 2) (data (i32.const 3) "ab") (data (i32.const 0) "de"))
 (module
@@ -102,37 +104,16 @@
 
 ;; Test alignment annotation rules
 (module (memory 0) (func (drop (i32.load8_u align=1 (i32.const 0)))))
 (module (memory 0) (func (drop (i32.load16_u align=2 (i32.const 0)))))
 (module (memory 0) (func (drop (i32.load align=4 (i32.const 0)))))
 (module (memory 0) (func (drop (f32.load align=4 (i32.const 0)))))
 
 (assert_invalid
-  (module (memory 0) (func (drop (i64.load align=0 (i32.const 0)))))
-  "alignment must be a power of two"
-)
-(assert_invalid
-  (module (memory 0) (func (drop (i64.load align=3 (i32.const 0)))))
-  "alignment must be a power of two"
-)
-(assert_invalid
-  (module (memory 0) (func (drop (i64.load align=5 (i32.const 0)))))
-  "alignment must be a power of two"
-)
-(assert_invalid
-  (module (memory 0) (func (drop (i64.load align=6 (i32.const 0)))))
-  "alignment must be a power of two"
-)
-(assert_invalid
-  (module (memory 0) (func (drop (i64.load align=7 (i32.const 0)))))
-  "alignment must be a power of two"
-)
-
-(assert_invalid
   (module (memory 0) (func (drop (i64.load align=16 (i32.const 0)))))
   "alignment must not be larger than natural"
 )
 (assert_invalid
   (module (memory 0) (func (drop (i64.load align=32 (i32.const 0)))))
   "alignment must not be larger than natural"
 )
 (assert_invalid
--- a/js/src/jit-test/tests/wasm/spec/memory.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory.wast.js
@@ -1,3 +1,1 @@
-// TODO initializer expression can reference global module-defined variables?
-quit();
-var importedArgs = ['memory.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['memory.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/memory_redundancy.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory_redundancy.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['memory_redundancy.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['memory_redundancy.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/memory_trap.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/memory_trap.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['memory_trap.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['memory_trap.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/names.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/names.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['names.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['names.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/nop.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/nop.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['nop.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['nop.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/resizing.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/resizing.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['resizing.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['resizing.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/return.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/return.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['return.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['return.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/select.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/select.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['select.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['select.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/set_local.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/set_local.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['set_local.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['set_local.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/skip-stack-guard-page.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/skip-stack-guard-page.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['skip-stack-guard-page.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['skip-stack-guard-page.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/soft-fail.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/soft-fail.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['soft-fail.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['soft-fail.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/stack.wast
+++ b/js/src/jit-test/tests/wasm/spec/stack.wast
@@ -58,31 +58,31 @@
     set_local $i
     i64.const 1
     set_local $res
     block $done
       loop $loop
         get_local $i
         i64.const 0
         i64.eq
-        if
+        if $body
           br $done
-        else
+        else $body
           get_local $i
           get_local $res
           i64.mul
           set_local $res
           get_local $i
           i64.const 1
           i64.sub
           set_local $i
-        end
+        end $body
         br $loop
-      end
-    end
+      end $loop
+    end $done
     get_local $res
   )
 
   (func (export "fac-mixed") (param $n i64) (result i64)
     (local $i i64)
     (local $res i64)
     (set_local $i (get_local $n))
     (set_local $res (i64.const 1))
--- a/js/src/jit-test/tests/wasm/spec/stack.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/stack.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['stack.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['stack.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/start.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/start.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['start.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['start.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/store_retval.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/store_retval.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['store_retval.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['store_retval.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/switch.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/switch.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['switch.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['switch.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/tee_local.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/tee_local.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['tee_local.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['tee_local.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/traps.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/traps.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['traps.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['traps.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/typecheck.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/typecheck.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['typecheck.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['typecheck.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/unreachable.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/unreachable.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['unreachable.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['unreachable.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/spec/unwind.wast.js
+++ b/js/src/jit-test/tests/wasm/spec/unwind.wast.js
@@ -1,1 +1,1 @@
-var importedArgs = ['unwind.wast']; load(scriptdir + '../spec.js');
+var importedArgs = ['unwind.wast']; load(scriptdir + '../wast.js');
--- a/js/src/jit-test/tests/wasm/to-text.js
+++ b/js/src/jit-test/tests/wasm/to-text.js
@@ -6,21 +6,21 @@ try {
 } catch (e) {
     caught = true;
 }
 assertEq(caught, true);
 
 assertErrorMessage(() => wasmBinaryToText(wasmTextToBinary(`(module (func (result i32) (f32.const 13.37)))`)), WebAssembly.CompileError, /type mismatch/);
 
 function runTest(code) {
-  var expected = wasmTextToBinary(code);
-  var s = wasmBinaryToText(expected);
-  print("TEXT: " + s);
-  var roundtrip = wasmTextToBinary(s);
-  assertDeepEq(expected, roundtrip);
+    var expected = wasmTextToBinary(code);
+    var s = wasmBinaryToText(expected);
+    print("TEXT: " + s);
+    var roundtrip = wasmTextToBinary(s);
+    assertDeepEq(expected, roundtrip);
 }
 
 // Smoke test
 runTest(`
 (module
   (func (param i32) (result f64)
      (local $l f32)
      (block
@@ -254,8 +254,36 @@ runTest(`
       if
       end
      else
      end
     end
    )
   (export "" 0)
 )`);
+
+// Branch table.
+runTest(`(module
+    (func (export "run") (param $p i32) (local $n i32)
+        i32.const 0
+        set_local $n
+        loop $outer
+            block $c block $b block $a
+                loop $inner
+                    get_local $p
+                    br_table $b $a $c $inner $outer
+                end $inner
+            end $a
+                get_local $n
+                i32.const 1
+                i32.add
+                set_local $n
+            end $b
+                block
+                    get_local $n
+                    i32.const 2
+                    i32.add
+                    set_local $n
+                end
+            end $c
+        end $outer
+    )
+)`);
rename from js/src/jit-test/tests/wasm/spec.js
rename to js/src/jit-test/tests/wasm/wast.js
--- a/js/src/jit/BaselineFrame.cpp
+++ b/js/src/jit/BaselineFrame.cpp
@@ -13,83 +13,83 @@
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 static void
-MarkLocals(BaselineFrame* frame, JSTracer* trc, unsigned start, unsigned end)
+TraceLocals(BaselineFrame* frame, JSTracer* trc, unsigned start, unsigned end)
 {
     if (start < end) {
         // Stack grows down.
         Value* last = frame->valueSlot(end - 1);
         TraceRootRange(trc, end - start, last, "baseline-stack");
     }
 }
 
 void
 BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
 {
-    replaceCalleeToken(MarkCalleeToken(trc, calleeToken()));
+    replaceCalleeToken(TraceCalleeToken(trc, calleeToken()));
 
-    // Mark |this|, actual and formal args.
+    // Trace |this|, actual and formal args.
     if (isFunctionFrame()) {
         TraceRoot(trc, &thisArgument(), "baseline-this");
 
         unsigned numArgs = js::Max(numActualArgs(), numFormalArgs());
         TraceRootRange(trc, numArgs + isConstructing(), argv(), "baseline-args");
     }
 
-    // Mark environment chain, if it exists.
+    // Trace environment chain, if it exists.
     if (envChain_)
         TraceRoot(trc, &envChain_, "baseline-envchain");
 
-    // Mark return value.
+    // Trace return value.
     if (hasReturnValue())
         TraceRoot(trc, returnValue().address(), "baseline-rval");
 
     if (isEvalFrame() && script()->isDirectEvalInFunction())
         TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget");
 
     if (hasArgsObj())
         TraceRoot(trc, &argsObj_, "baseline-args-obj");
 
-    // Mark locals and stack values.
+    // Trace locals and stack values.
     JSScript* script = this->script();
     size_t nfixed = script->nfixed();
     jsbytecode* pc;
     frameIterator.baselineScriptAndPc(nullptr, &pc);
     size_t nlivefixed = script->calculateLiveFixed(pc);
 
     // NB: It is possible that numValueSlots() could be zero, even if nfixed is
     // nonzero.  This is the case if the function has an early stack check.
     if (numValueSlots() == 0)
         return;
 
     MOZ_ASSERT(nfixed <= numValueSlots());
 
     if (nfixed == nlivefixed) {
         // All locals are live.
-        MarkLocals(this, trc, 0, numValueSlots());
+        TraceLocals(this, trc, 0, numValueSlots());
     } else {
-        // Mark operand stack.
-        MarkLocals(this, trc, nfixed, numValueSlots());
+        // Trace operand stack.
+        TraceLocals(this, trc, nfixed, numValueSlots());
 
         // Clear dead block-scoped locals.
         while (nfixed > nlivefixed)
             unaliasedLocal(--nfixed).setUndefined();
 
-        // Mark live locals.
-        MarkLocals(this, trc, 0, nlivefixed);
+        // Trace live locals.
+        TraceLocals(this, trc, 0, nlivefixed);
     }
 
     if (script->compartment()->debugEnvs)
-        script->compartment()->debugEnvs->markLiveFrame(trc, this);
+        script->compartment()->debugEnvs->traceLiveFrame(trc, this);
 }
 
 bool
 BaselineFrame::isNonGlobalEvalFrame() const
 {
     return isEvalFrame() && script()->enclosingScope()->as<EvalScope>().isNonGlobal();
 }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -592,63 +592,63 @@ jit::LazyLinkTopActivation(JSContext* cx
 
     MOZ_ASSERT(calleeScript->hasBaselineScript());
     MOZ_ASSERT(calleeScript->baselineOrIonRawPointer());
 
     return calleeScript->baselineOrIonRawPointer();
 }
 
 /* static */ void
-JitRuntime::Mark(JSTracer* trc, AutoLockForExclusiveAccess& lock)
+JitRuntime::Trace(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
 
     // Shared stubs are allocated in the atoms compartment, so do not iterate
     // them after the atoms heap after it has been "finished."
     if (trc->runtime()->atomsAreFinished())
         return;
 
     Zone* zone = trc->runtime()->atomsCompartment(lock)->zone();
     for (auto i = zone->cellIter<JitCode>(); !i.done(); i.next()) {
         JitCode* code = i;
         TraceRoot(trc, &code, "wrapper");
     }
 }
 
 /* static */ void
-JitRuntime::MarkJitcodeGlobalTableUnconditionally(JSTracer* trc)
+JitRuntime::TraceJitcodeGlobalTable(JSTracer* trc)
 {
     if (trc->runtime()->spsProfiler.enabled() &&
         trc->runtime()->hasJitRuntime() &&
         trc->runtime()->jitRuntime()->hasJitcodeGlobalTable())
     {
-        trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->markUnconditionally(trc);
+        trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->trace(trc);
     }
 }
 
 /* static */ bool
-JitRuntime::MarkJitcodeGlobalTableIteratively(JSTracer* trc)
+JitRuntime::MarkJitcodeGlobalTableIteratively(GCMarker* marker)
 {
-    if (trc->runtime()->hasJitRuntime() &&
-        trc->runtime()->jitRuntime()->hasJitcodeGlobalTable())
+    if (marker->runtime()->hasJitRuntime() &&
+        marker->runtime()->jitRuntime()->hasJitcodeGlobalTable())
     {
-        return trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->markIteratively(trc);
+        return marker->runtime()->jitRuntime()->getJitcodeGlobalTable()->markIteratively(marker);
     }
     return false;
 }
 
 /* static */ void
 JitRuntime::SweepJitcodeGlobalTable(JSRuntime* rt)
 {
     if (rt->hasJitRuntime() && rt->jitRuntime()->hasJitcodeGlobalTable())
         rt->jitRuntime()->getJitcodeGlobalTable()->sweep(rt);
 }
 
 void
-JitCompartment::mark(JSTracer* trc, JSCompartment* compartment)
+JitCompartment::trace(JSTracer* trc, JSCompartment* compartment)
 {
     // Free temporary OSR buffer.
     trc->runtime()->jitRuntime()->freeOsrTempData();
 }
 
 void
 JitCompartment::sweep(FreeOp* fop, JSCompartment* compartment)
 {
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -3094,55 +3094,98 @@ FindDominatingBoundsCheck(BoundsCheckMap
             return nullptr;
 
         return check;
     }
 
     return p->value().check;
 }
 
+static MathSpace
+ExtractMathSpace(MDefinition* ins)
+{
+    MOZ_ASSERT(ins->isAdd() || ins->isSub());
+    MBinaryArithInstruction* arith = nullptr;
+    if (ins->isAdd())
+        arith = ins->toAdd();
+    else
+        arith = ins->toSub();
+    switch (arith->truncateKind()) {
+      case MDefinition::NoTruncate:
+      case MDefinition::TruncateAfterBailouts:
+        // TruncateAfterBailouts is considered as infinite space because the
+        // LinearSum will effectively remove the bailout check.
+        return MathSpace::Infinite;
+      case MDefinition::IndirectTruncate:
+      case MDefinition::Truncate:
+        return MathSpace::Modulo;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unknown TruncateKind");
+}
+
 // Extract a linear sum from ins, if possible (otherwise giving the sum 'ins + 0').
 SimpleLinearSum
-jit::ExtractLinearSum(MDefinition* ins)
+jit::ExtractLinearSum(MDefinition* ins, MathSpace space)
 {
     if (ins->isBeta())
         ins = ins->getOperand(0);
 
     if (ins->type() != MIRType::Int32)
         return SimpleLinearSum(ins, 0);
 
     if (ins->isConstant())
         return SimpleLinearSum(nullptr, ins->toConstant()->toInt32());
 
-    if (ins->isAdd() || ins->isSub()) {
-        MDefinition* lhs = ins->getOperand(0);
-        MDefinition* rhs = ins->getOperand(1);
-        if (lhs->type() == MIRType::Int32 && rhs->type() == MIRType::Int32) {
-            SimpleLinearSum lsum = ExtractLinearSum(lhs);
-            SimpleLinearSum rsum = ExtractLinearSum(rhs);
-
-            if (lsum.term && rsum.term)
-                return SimpleLinearSum(ins, 0);
-
-            // Check if this is of the form <SUM> + n, n + <SUM> or <SUM> - n.
-            if (ins->isAdd()) {
-                int32_t constant;
-                if (!SafeAdd(lsum.constant, rsum.constant, &constant))
-                    return SimpleLinearSum(ins, 0);
-                return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
-            }
-            if (lsum.term) {
-                int32_t constant;
-                if (!SafeSub(lsum.constant, rsum.constant, &constant))
-                    return SimpleLinearSum(ins, 0);
-                return SimpleLinearSum(lsum.term, constant);
-            }
-        }
+    if (!ins->isAdd() && !ins->isSub())
+        return SimpleLinearSum(ins, 0);
+
+    // Only allow math which are in the same space.
+    MathSpace insSpace = ExtractMathSpace(ins);
+    if (space == MathSpace::Unknown)
+        space = insSpace;
+    else if (space != insSpace)
+        return SimpleLinearSum(ins, 0);
+    MOZ_ASSERT(space == MathSpace::Modulo || space == MathSpace::Infinite);
+
+    MDefinition* lhs = ins->getOperand(0);
+    MDefinition* rhs = ins->getOperand(1);
+    if (lhs->type() != MIRType::Int32 || rhs->type() != MIRType::Int32)
+        return SimpleLinearSum(ins, 0);
+
+    // Extract linear sums of each operand.
+    SimpleLinearSum lsum = ExtractLinearSum(lhs, space);
+    SimpleLinearSum rsum = ExtractLinearSum(rhs, space);
+
+    // LinearSum only considers a single term operand, if both sides have
+    // terms, then ignore extracted linear sums.
+    if (lsum.term && rsum.term)
+        return SimpleLinearSum(ins, 0);
+
+    // Check if this is of the form <SUM> + n or n + <SUM>.
+    if (ins->isAdd()) {
+        int32_t constant;
+        if (space == MathSpace::Modulo)
+            constant = lsum.constant + rsum.constant;
+        else if (!SafeAdd(lsum.constant, rsum.constant, &constant))
+            return SimpleLinearSum(ins, 0);
+        return SimpleLinearSum(lsum.term ? lsum.term : rsum.term, constant);
     }
 
+    MOZ_ASSERT(ins->isSub());
+    // Check if this is of the form <SUM> - n.
+    if (lsum.term) {
+        int32_t constant;
+        if (space == MathSpace::Modulo)
+            constant = lsum.constant - rsum.constant;
+        else if (!SafeSub(lsum.constant, rsum.constant, &constant))
+            return SimpleLinearSum(ins, 0);
+        return SimpleLinearSum(lsum.term, constant);
+    }
+
+    // Ignore any of the form n - <SUM>.
     return SimpleLinearSum(ins, 0);
 }
 
 // Extract a linear inequality holding when a boolean test goes in the
 // specified direction, of the form 'lhs + lhsN <= rhs' (or >=).
 bool
 jit::ExtractLinearInequality(MTest* test, BranchDirection direction,
                              SimpleLinearSum* plhs, MDefinition** prhs, bool* plessEqual)
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -104,18 +104,31 @@ struct SimpleLinearSum
     MDefinition* term;
     int32_t constant;
 
     SimpleLinearSum(MDefinition* term, int32_t constant)
         : term(term), constant(constant)
     {}
 };
 
+// Math done in a Linear sum can either be in a modulo space, in which case
+// overflow are wrapped around, or they can be computed in the integer-space in
+// which case we have to check that no overflow can happen when summing
+// constants.
+//
+// When the caller ignores which space it is, the definition would be used to
+// deduce it.
+enum class MathSpace {
+    Modulo,
+    Infinite,
+    Unknown
+};
+
 SimpleLinearSum
-ExtractLinearSum(MDefinition* ins);
+ExtractLinearSum(MDefinition* ins, MathSpace space = MathSpace::Unknown);
 
 MOZ_MUST_USE bool
 ExtractLinearInequality(MTest* test, BranchDirection direction,
                         SimpleLinearSum* plhs, MDefinition** prhs, bool* plessEqual);
 
 struct LinearTerm
 {
     MDefinition* term;
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -216,19 +216,19 @@ class JitRuntime
   public:
     explicit JitRuntime(JSRuntime* rt);
     ~JitRuntime();
     MOZ_MUST_USE bool initialize(JSContext* cx, js::AutoLockForExclusiveAccess& lock);
 
     uint8_t* allocateOsrTempData(size_t size);
     void freeOsrTempData();
 
-    static void Mark(JSTracer* trc, js::AutoLockForExclusiveAccess& lock);
-    static void MarkJitcodeGlobalTableUnconditionally(JSTracer* trc);
-    static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(JSTracer* trc);
+    static void Trace(JSTracer* trc, js::AutoLockForExclusiveAccess& lock);
+    static void TraceJitcodeGlobalTable(JSTracer* trc);
+    static MOZ_MUST_USE bool MarkJitcodeGlobalTableIteratively(GCMarker* marker);
     static void SweepJitcodeGlobalTable(JSRuntime* rt);
 
     ExecutableAllocator& execAlloc() {
         return execAlloc_;
     }
     ExecutableAllocator& backedgeExecAlloc() {
         return backedgeExecAlloc_;
     }
@@ -561,17 +561,17 @@ class JitCompartment
     JitCompartment();
     ~JitCompartment();
 
     MOZ_MUST_USE bool initialize(JSContext* cx);
 
     // Initialize code stubs only used by Ion, not Baseline.
     MOZ_MUST_USE bool ensureIonStubsExist(JSContext* cx);
 
-    void mark(JSTracer* trc, JSCompartment* compartment);
+    void trace(JSTracer* trc, JSCompartment* compartment);
     void sweep(FreeOp* fop, JSCompartment* compartment);
 
     JitCode* stringConcatStubNoBarrier() const {
         return stringConcatStub_;
     }
 
     JitCode* regExpMatcherStubNoBarrier() const {
         return regExpMatcherStub_;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -285,17 +285,17 @@ JitFrameIterator::spillBase() const
     return reinterpret_cast<uintptr_t*>(fp() - ionScript()->frameSize());
 }
 
 MachineState
 JitFrameIterator::machineState() const
 {
     MOZ_ASSERT(isIonScripted());
 
-    // The MachineState is used by GCs for marking call-sites.
+    // The MachineState is used by GCs for tracing call-sites.
     if (MOZ_UNLIKELY(isBailoutJS()))
         return *activation_->bailoutData()->machineState();
 
     SafepointReader reader(ionScript(), safepoint());
     uintptr_t* spill = spillBase();
     MachineState machine;
 
     for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); ++iter)
@@ -887,17 +887,17 @@ HandleException(ResumeFromException* rfe
             ReportOverRecursed(cx);
         }
     }
 
     rfe->stackPointer = iter.fp();
 }
 
 // Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a
-// bare exit frame so it's ignored by MarkJitExitFrame.
+// bare exit frame so it's ignored by TraceJitExitFrame.
 void
 EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame)
 {
     ExitFrameLayout* exitFrame = reinterpret_cast<ExitFrameLayout*>(frame);
 
     if (cx->runtime()->jitTop == (uint8_t*)frame) {
         // If we already called this function for the current frame, do
         // nothing.
@@ -916,17 +916,17 @@ EnsureBareExitFrame(JSContext* cx, JitFr
 #endif
 
     cx->runtime()->jitTop = (uint8_t*)frame;
     *exitFrame->footer()->addressOfJitCode() = ExitFrameLayout::BareToken();
     MOZ_ASSERT(exitFrame->isBareExit());
 }
 
 CalleeToken
-MarkCalleeToken(JSTracer* trc, CalleeToken token)
+TraceCalleeToken(JSTracer* trc, CalleeToken token)
 {
     switch (CalleeTokenTag tag = GetCalleeTokenTag(token)) {
       case CalleeToken_Function:
       case CalleeToken_FunctionConstructing:
       {
         JSFunction* fun = CalleeTokenToFunction(token);
         TraceRoot(trc, &fun, "jit-callee");
         return CalleeToToken(fun, tag == CalleeToken_FunctionConstructing);
@@ -958,23 +958,23 @@ ReadAllocation(const JitFrameIterator& f
         Register reg = a->toGeneralReg()->reg();
         return frame.machineState().read(reg);
     }
     return *frame.jsFrame()->slotRef(SafepointSlotEntry(a));
 }
 #endif
 
 static void
-MarkThisAndArguments(JSTracer* trc, const JitFrameIterator& frame)
+TraceThisAndArguments(JSTracer* trc, const JitFrameIterator& frame)
 {
-    // Mark |this| and any extra actual arguments for an Ion frame. Marking of
-    // formal arguments is taken care of by the frame's safepoint/snapshot,
+    // Trace |this| and any extra actual arguments for an Ion frame. Tracinging
+    // of formal arguments is taken care of by the frame's safepoint/snapshot,
     // except when the script might have lazy arguments or rest, in which case
-    // we mark them as well. We also have to mark formals if we have a LazyLink
-    // frame.
+    // we trace them as well. We also have to trace formals if we have a
+    // LazyLink frame.
 
     JitFrameLayout* layout = frame.isExitFrameLayout<LazyLinkExitFrameLayout>()
                              ? frame.exitFrame()->as<LazyLinkExitFrameLayout>()->jsFrame()
                              : frame.jsFrame();
 
     if (!CalleeTokenIsFunction(layout->calleeToken()))
         return;
 
@@ -994,17 +994,17 @@ MarkThisAndArguments(JSTracer* trc, cons
 
     // Trace |this|.
     TraceRoot(trc, argv, "ion-thisv");
 
     // Trace actual arguments beyond the formals. Note + 1 for thisv.
     for (size_t i = nformals + 1; i < nargs + 1; i++)
         TraceRoot(trc, &argv[i], "ion-argv");
 
-    // Always mark the new.target from the frame. It's not in the snapshots.
+    // Always trace the new.target from the frame. It's not in the snapshots.
     // +1 to pass |this|
     if (CalleeTokenIsConstructing(layout->calleeToken()))
         TraceRoot(trc, &argv[1 + newTargetOffset], "ion-newTarget");
 }
 
 #ifdef JS_NUNBOX32
 static inline void
 WriteAllocation(const JitFrameIterator& frame, const LAllocation* a, uintptr_t value)
@@ -1014,33 +1014,33 @@ WriteAllocation(const JitFrameIterator& 
         frame.machineState().write(reg, value);
     } else {
         *frame.jsFrame()->slotRef(SafepointSlotEntry(a)) = value;
     }
 }
 #endif
 
 static void
-MarkIonJSFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceIonJSFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
     JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
 
-    layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
+    layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
 
     IonScript* ionScript = nullptr;
     if (frame.checkInvalidation(&ionScript)) {
         // This frame has been invalidated, meaning that its IonScript is no
         // longer reachable through the callee token (JSFunction/JSScript->ion
         // is now nullptr or recompiled). Manually trace it here.
         IonScript::Trace(trc, ionScript);
     } else {
         ionScript = frame.ionScriptFromCalleeToken();
     }
 
-    MarkThisAndArguments(trc, frame);
+    TraceThisAndArguments(trc, frame);
 
     const SafepointIndex* si = ionScript->getSafepointIndex(frame.returnAddressToFp());
 
     SafepointReader safepoint(ionScript, si);
 
     // Scan through slots which contain pointers (or on punboxing systems,
     // actual values).
     SafepointSlotEntry entry;
@@ -1080,25 +1080,25 @@ MarkIonJSFrame(JSTracer* trc, const JitF
             rawPayload = *v.payloadUIntPtr();
             WriteAllocation(frame, &payload, rawPayload);
         }
     }
 #endif
 }
 
 static void
-MarkBailoutFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceBailoutFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
     JitFrameLayout* layout = (JitFrameLayout*)frame.fp();
 
-    layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
-
-    // We have to mark the list of actual arguments, as only formal arguments
+    layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
+
+    // We have to trace the list of actual arguments, as only formal arguments
     // are represented in the Snapshot.
-    MarkThisAndArguments(trc, frame);
+    TraceThisAndArguments(trc, frame);
 
     // Under a bailout, do not have a Safepoint to only iterate over GC-things.
     // Thus we use a SnapshotIterator to trace all the locations which would be
     // used to reconstruct the Baseline frame.
     //
     // Note that at the time where this function is called, we have not yet
     // started to reconstruct baseline frames.
 
@@ -1161,49 +1161,49 @@ UpdateIonJSFrameForMinorGC(JSTracer* trc
 
     while (safepoint.getSlotsOrElementsSlot(&entry)) {
         HeapSlot** slots = reinterpret_cast<HeapSlot**>(layout->slotRef(entry));
         nursery.forwardBufferPointer(slots);
     }
 }
 
 static void
-MarkJitStubFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceJitStubFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
-    // Mark the ICStub pointer stored in the stub frame. This is necessary
+    // Trace the ICStub pointer stored in the stub frame. This is necessary
     // so that we don't destroy the stub code after unlinking the stub.
 
     MOZ_ASSERT(frame.type() == JitFrame_IonStub || frame.type() == JitFrame_BaselineStub);
     JitStubFrameLayout* layout = (JitStubFrameLayout*)frame.fp();
 
     if (ICStub* stub = layout->maybeStubPtr()) {
         MOZ_ASSERT(stub->makesGCCalls());
         stub->trace(trc);
     }
 }
 
 static void
-MarkIonAccessorICFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceIonAccessorICFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
     MOZ_ASSERT(frame.type() == JitFrame_IonAccessorIC);
     IonAccessorICFrameLayout* layout = (IonAccessorICFrameLayout*)frame.fp();
     TraceRoot(trc, layout->stubCode(), "ion-ic-accessor-code");
 }
 
 #ifdef JS_CODEGEN_MIPS32
 uint8_t*
 alignDoubleSpillWithOffset(uint8_t* pointer, int32_t offset)
 {
     uint32_t address = reinterpret_cast<uint32_t>(pointer);
     address = (address - offset) & ~(ABIStackAlignment - 1);
     return reinterpret_cast<uint8_t*>(address);
 }
 
 static void
-MarkJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
+TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
 {
     uint8_t* doubleArgs = reinterpret_cast<uint8_t*>(footer);
     doubleArgs = alignDoubleSpillWithOffset(doubleArgs, sizeof(intptr_t));
     if (f->outParam == Type_Handle)
         doubleArgs -= sizeof(Value);
     doubleArgs -= f->doubleByRefArgs() * sizeof(double);
 
     for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
@@ -1214,36 +1214,36 @@ MarkJitExitFrameCopiedArguments(JSTracer
             else
                 MOZ_ASSERT(f->argRootType(explicitArg) == VMFunction::RootNone);
             doubleArgs += sizeof(double);
         }
     }
 }
 #else
 static void
-MarkJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
+TraceJitExitFrameCopiedArguments(JSTracer* trc, const VMFunction* f, ExitFooterFrame* footer)
 {
     // This is NO-OP on other platforms.
 }
 #endif
 
 static void
-MarkJitExitFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceJitExitFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
     ExitFooterFrame* footer = frame.exitFrame()->footer();
 
-    // Mark the code of the code handling the exit path.  This is needed because
-    // invalidated script are no longer marked because data are erased by the
+    // Trace the code of the code handling the exit path.  This is needed because
+    // invalidated script are no longer traced because data are erased by the
     // invalidation and relocation data are no longer reliable.  So the VM
     // wrapper or the invalidation code may be GC if no JitCode keep reference
     // on them.
     MOZ_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1));
 
     // This corresponds to the case where we have build a fake exit frame which
-    // handles the case of a native function call. We need to mark the argument
+    // handles the case of a native function call. We need to trace the argument
     // vector of the function call, and also new.target if it was a constructing
     // call.
     if (frame.isExitFrameLayout<NativeExitFrameLayout>()) {
         NativeExitFrameLayout* native = frame.exitFrame()->as<NativeExitFrameLayout>();
         size_t len = native->argc() + 2;
         Value* vp = native->vp();
         TraceRootRange(trc, len, vp, "ion-native-args");
         if (frame.isExitFrameLayout<ConstructNativeExitFrameLayout>())
@@ -1260,17 +1260,17 @@ MarkJitExitFrame(JSTracer* trc, const Ji
         TraceRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs");
         return;
     }
 
     if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>() ||
         frame.isExitFrameLayout<IonOOLSetterOpExitFrameLayout>())
     {
         // A SetterOp frame is a different size, but that's the only relevant
-        // difference between the two. The fields that need marking are all in
+        // difference between the two. The fields that need tracing are all in
         // the common base class.
         IonOOLPropertyOpExitFrameLayout* oolgetter =
             frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>()
             ? frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>()
             : frame.exitFrame()->as<IonOOLSetterOpExitFrameLayout>();
         TraceRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code");
         TraceRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp");
         TraceRoot(trc, oolgetter->id(), "ion-ool-property-op-id");
@@ -1302,34 +1302,34 @@ MarkJitExitFrame(JSTracer* trc, const Ji
         return;
     }
 
     if (frame.isExitFrameLayout<LazyLinkExitFrameLayout>()) {
         LazyLinkExitFrameLayout* ll = frame.exitFrame()->as<LazyLinkExitFrameLayout>();
         JitFrameLayout* layout = ll->jsFrame();
 
         TraceRoot(trc, ll->stubCode(), "lazy-link-code");
-        layout->replaceCalleeToken(MarkCalleeToken(trc, layout->calleeToken()));
-        MarkThisAndArguments(trc, frame);
+        layout->replaceCalleeToken(TraceCalleeToken(trc, layout->calleeToken()));
+        TraceThisAndArguments(trc, frame);
         return;
     }
 
     if (frame.isBareExit()) {
-        // Nothing to mark. Fake exit frame pushed for VM functions with
-        // nothing to mark on the stack.
+        // Nothing to trace. Fake exit frame pushed for VM functions with
+        // nothing to trace on the stack.
         return;
     }
 
     TraceRoot(trc, footer->addressOfJitCode(), "ion-exit-code");
 
     const VMFunction* f = footer->function();
     if (f == nullptr)
         return;
 
-    // Mark arguments of the VM wrapper.
+    // Trace arguments of the VM wrapper.
     uint8_t* argBase = frame.exitFrame()->argBase();
     for (uint32_t explicitArg = 0; explicitArg < f->explicitArgs; explicitArg++) {
         switch (f->argRootType(explicitArg)) {
           case VMFunction::RootNone:
             break;
           case VMFunction::RootObject: {
             // Sometimes we can bake in HandleObjects to nullptr.
             JSObject** pobj = reinterpret_cast<JSObject**>(argBase);
@@ -1382,82 +1382,82 @@ MarkJitExitFrame(JSTracer* trc, const Ji
             TraceRoot(trc, footer->outParam<Value>(), "ion-vm-outvp");
             break;
           case VMFunction::RootCell:
             TraceGenericPointerRoot(trc, footer->outParam<gc::Cell*>(), "ion-vm-out");
             break;
         }
     }
 
-    MarkJitExitFrameCopiedArguments(trc, f, footer);
+    TraceJitExitFrameCopiedArguments(trc, f, footer);
 }
 
 static void
-MarkRectifierFrame(JSTracer* trc, const JitFrameIterator& frame)
+TraceRectifierFrame(JSTracer* trc, const JitFrameIterator& frame)
 {
-    // Mark thisv.
+    // Trace thisv.
     //
     // Baseline JIT code generated as part of the ICCall_Fallback stub may use
     // it if we're calling a constructor that returns a primitive value.
     RectifierFrameLayout* layout = (RectifierFrameLayout*)frame.fp();
     TraceRoot(trc, &layout->argv()[0], "ion-thisv");
 }
 
 static void
-MarkJitActivation(JSTracer* trc, const JitActivationIterator& activations)
+TraceJitActivation(JSTracer* trc, const JitActivationIterator& activations)
 {
     JitActivation* activation = activations->asJit();
 
 #ifdef CHECK_OSIPOINT_REGISTERS
     if (JitOptions.checkOsiPointRegisters) {
         // GC can modify spilled registers, breaking our register checks.
         // To handle this, we disable these checks for the current VM call
         // when a GC happens.
         activation->setCheckRegs(false);
     }
 #endif
 
-    activation->markRematerializedFrames(trc);
-    activation->markIonRecovery(trc);
+    activation->traceRematerializedFrames(trc);
+    activation->traceIonRecovery(trc);
 
     for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case JitFrame_Exit:
-            MarkJitExitFrame(trc, frames);
+            TraceJitExitFrame(trc, frames);
             break;
           case JitFrame_BaselineJS:
             frames.baselineFrame()->trace(trc, frames);
             break;
           case JitFrame_IonJS:
-            MarkIonJSFrame(trc, frames);
+            TraceIonJSFrame(trc, frames);
             break;
           case JitFrame_BaselineStub:
           case JitFrame_IonStub:
-            MarkJitStubFrame(trc, frames);
+            TraceJitStubFrame(trc, frames);
             break;
           case JitFrame_Bailout:
-            MarkBailoutFrame(trc, frames);
+            TraceBailoutFrame(trc, frames);
             break;
           case JitFrame_Rectifier:
-            MarkRectifierFrame(trc, frames);
+            TraceRectifierFrame(trc, frames);
             break;
           case JitFrame_IonAccessorIC:
-            MarkIonAccessorICFrame(trc, frames);
+            TraceIonAccessorICFrame(trc, frames);
             break;
           default:
             MOZ_CRASH("unexpected frame type");
         }
     }
 }
 
 void
-MarkJitActivations(JSRuntime* rt, JSTracer* trc)
+TraceJitActivations(JSRuntime* rt, JSTracer* trc)
 {
     for (JitActivationIterator activations(rt); !activations.done(); ++activations)
-        MarkJitActivation(trc, activations);
+        TraceJitActivation(trc, activations);
 }
 
 JSCompartment*
 TopmostIonActivationCompartment(JSRuntime* rt)
 {
     for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
         for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
             if (frames.type() == JitFrame_IonJS)
@@ -1913,17 +1913,17 @@ SnapshotIterator::maybeRead(const RValue
 }
 
 void
 SnapshotIterator::writeAllocationValuePayload(const RValueAllocation& alloc, const Value& v)
 {
     uintptr_t payload = *v.payloadUIntPtr();
 #if defined(JS_PUNBOX64)
     // Do not write back the tag, as this will trigger an assertion when we will
-    // reconstruct the JS Value while marking again or when bailing out.
+    // reconstruct the JS Value while tracing again or when bailing out.
     payload &= JSVAL_PAYLOAD_MASK;
 #endif
 
     switch (alloc.mode()) {
       case RValueAllocation::CONSTANT:
         ionScript_->getConstant(alloc.index()) = v;
         break;
 
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -279,17 +279,17 @@ struct ResumeFromException
 
     BaselineBailoutInfo* bailoutInfo;
 };
 
 void HandleException(ResumeFromException* rfe);
 
 void EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame);
 
-void MarkJitActivations(JSRuntime* rt, JSTracer* trc);
+void TraceJitActivations(JSRuntime* rt, JSTracer* trc);
 
 JSCompartment*
 TopmostIonActivationCompartment(JSRuntime* rt);
 
 void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc);
 
 static inline uint32_t
 EncodeFrameHeaderSize(size_t headerSize)
@@ -524,17 +524,17 @@ enum ExitFrameTokenValues
 class ExitFrameLayout : public CommonFrameLayout
 {
     inline uint8_t* top() {
         return reinterpret_cast<uint8_t*>(this + 1);
     }
 
   public:
     // Pushed for "bare" fake exit frames that have no GC things on stack to be
-    // marked.
+    // traced.
     static JitCode* BareToken() { return (JitCode*)ExitFrameLayoutBareToken; }
 
     static inline size_t Size() {
         return sizeof(ExitFrameLayout);
     }
     static inline size_t SizeWithFooter() {
         return Size() + ExitFooterFrame::Size();
     }
@@ -1028,17 +1028,17 @@ class InvalidationBailoutStack
 
     void checkInvariants() const;
 };
 
 void
 GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes);
 
 CalleeToken
-MarkCalleeToken(JSTracer* trc, CalleeToken token);
+TraceCalleeToken(JSTracer* trc, CalleeToken token);
 
 // The minimum stack size is two. Two slots are needed because INITGLEXICAL
 // (stack depth 1) is compiled as a SETPROP (stack depth 2) on the global
 // lexical scope. Baseline also requires one slot for this/argument type
 // checks.
 static const uint32_t MinJITStackSize = 2;
 
 } /* namespace jit */
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -719,46 +719,46 @@ JitcodeGlobalTable::setAllEntriesAsExpir
     AutoSuppressProfilerSampling suppressSampling(rt);
     for (Range r(*this); !r.empty(); r.popFront())
         r.front()->setAsExpired();
 }
 
 struct Unconditionally
 {
     template <typename T>
-    static bool ShouldMark(T* thingp) { return true; }
+    static bool ShouldTrace(T* thingp) { return true; }
 };
 
 void
-JitcodeGlobalTable::markUnconditionally(JSTracer* trc)
+JitcodeGlobalTable::trace(JSTracer* trc)
 {
-    // Mark all entries unconditionally. This is done during minor collection
-    // to account for tenuring.
+    // Trace all entries unconditionally. This is done during minor collection
+    // to tenure and update object pointers.
 
     MOZ_ASSERT(trc->runtime()->spsProfiler.enabled());
 
     AutoSuppressProfilerSampling suppressSampling(trc->runtime());
     for (Range r(*this); !r.empty(); r.popFront())
-        r.front()->mark<Unconditionally>(trc);
+        r.front()->trace<Unconditionally>(trc);
 }
 
 struct IfUnmarked
 {
     template <typename T>
-    static bool ShouldMark(T* thingp) { return !IsMarkedUnbarriered(thingp); }
+    static bool ShouldTrace(T* thingp) { return !IsMarkedUnbarriered(thingp); }
 };
 
 template <>
-bool IfUnmarked::ShouldMark<TypeSet::Type>(TypeSet::Type* type)
+bool IfUnmarked::ShouldTrace<TypeSet::Type>(TypeSet::Type* type)
 {
     return !TypeSet::IsTypeMarked(type);
 }
 
 bool
-JitcodeGlobalTable::markIteratively(JSTracer* trc)
+JitcodeGlobalTable::markIteratively(GCMarker* marker)
 {
     // JitcodeGlobalTable must keep entries that are in the sampler buffer
     // alive. This conditionality is akin to holding the entries weakly.
     //
     // If this table were marked at the beginning of the mark phase, then
     // sampling would require a read barrier for sampling in between
     // incremental GC slices. However, invoking read barriers from the sampler
     // is wildly unsafe. The sampler may run at any time, including during GC
@@ -772,24 +772,24 @@ JitcodeGlobalTable::markIteratively(JSTr
     // the frame was on-stack at the beginning of the sweep phase, or 2) the
     // frame was pushed between incremental sweep slices. Frames of case 1)
     // are already marked. Frames of case 2) must have been reachable to have
     // been newly pushed, and thus are already marked.
     //
     // The approach above obviates the need for read barriers. The assumption
     // above is checked in JitcodeGlobalTable::lookupForSampler.
 
-    MOZ_ASSERT(!trc->runtime()->isHeapMinorCollecting());
+    MOZ_ASSERT(!marker->runtime()->isHeapMinorCollecting());
 
-    AutoSuppressProfilerSampling suppressSampling(trc->runtime());
-    uint32_t gen = trc->runtime()->profilerSampleBufferGen();
-    uint32_t lapCount = trc->runtime()->profilerSampleBufferLapCount();
+    AutoSuppressProfilerSampling suppressSampling(marker->runtime());
+    uint32_t gen = marker->runtime()->profilerSampleBufferGen();
+    uint32_t lapCount = marker->runtime()->profilerSampleBufferLapCount();
 
     // If the profiler is off, all entries are considered to be expired.
-    if (!trc->runtime()->spsProfiler.enabled())
+    if (!marker->runtime()->spsProfiler.enabled())
         gen = UINT32_MAX;
 
     bool markedAny = false;
     for (Range r(*this); !r.empty(); r.popFront()) {
         JitcodeGlobalEntry* entry = r.front();
 
         // If an entry is not sampled, reset its generation to the invalid
         // generation, and conditionally mark the rest of the entry if its
@@ -804,17 +804,17 @@ JitcodeGlobalTable::markIteratively(JSTr
                 continue;
         }
 
         // The table is runtime-wide. Not all zones may be participating in
         // the GC.
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
             continue;
 
-        markedAny |= entry->mark<IfUnmarked>(trc);
+        markedAny |= entry->trace<IfUnmarked>(marker);
     }
 
     return markedAny;
 }
 
 void
 JitcodeGlobalTable::sweep(JSRuntime* rt)
 {
@@ -827,21 +827,21 @@ JitcodeGlobalTable::sweep(JSRuntime* rt)
 
         if (entry->baseEntry().isJitcodeAboutToBeFinalized())
             e.removeFront();
         else
             entry->sweepChildren(rt);
     }
 }
 
-template <class ShouldMarkProvider>
+template <class ShouldTraceProvider>
 bool
-JitcodeGlobalEntry::BaseEntry::markJitcode(JSTracer* trc)
+JitcodeGlobalEntry::BaseEntry::traceJitcode(JSTracer* trc)
 {
-    if (ShouldMarkProvider::ShouldMark(&jitcode_)) {
+    if (ShouldTraceProvider::ShouldTrace(&jitcode_)) {
         TraceManuallyBarrieredEdge(trc, &jitcode_, "jitcodglobaltable-baseentry-jitcode");
         return true;
     }
     return false;
 }
 
 bool
 JitcodeGlobalEntry::BaseEntry::isJitcodeMarkedFromAnyThread()
@@ -851,21 +851,21 @@ JitcodeGlobalEntry::BaseEntry::isJitcode
 }
 
 bool
 JitcodeGlobalEntry::BaseEntry::isJitcodeAboutToBeFinalized()
 {
     return IsAboutToBeFinalizedUnbarriered(&jitcode_);
 }
 
-template <class ShouldMarkProvider>
+template <class ShouldTraceProvider>
 bool
-JitcodeGlobalEntry::BaselineEntry::mark(JSTracer* trc)
+JitcodeGlobalEntry::BaselineEntry::trace(JSTracer* trc)
 {
-    if (ShouldMarkProvider::ShouldMark(&script_)) {
+    if (ShouldTraceProvider::ShouldTrace(&script_)) {
         TraceManuallyBarrieredEdge(trc, &script_, "jitcodeglobaltable-baselineentry-script");
         return true;
     }
     return false;
 }
 
 void
 JitcodeGlobalEntry::BaselineEntry::sweepChildren()
@@ -875,52 +875,52 @@ JitcodeGlobalEntry::BaselineEntry::sweep
 
 bool
 JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread()
 {
     return IsMarkedUnbarriered(&script_) ||
            script_->arena()->allocatedDuringIncremental;
 }
 
-template <class ShouldMarkProvider>
+template <class ShouldTraceProvider>
 bool
-JitcodeGlobalEntry::IonEntry::mark(JSTracer* trc)
+JitcodeGlobalEntry::IonEntry::trace(JSTracer* trc)
 {
-    bool markedAny = false;
+    bool tracedAny = false;
 
     for (unsigned i = 0; i < numScripts(); i++) {
-        if (ShouldMarkProvider::ShouldMark(&sizedScriptList()->pairs[i].script)) {
+        if (ShouldTraceProvider::ShouldTrace(&sizedScriptList()->pairs[i].script)) {
             TraceManuallyBarrieredEdge(trc, &sizedScriptList()->pairs[i].script,
                                        "jitcodeglobaltable-ionentry-script");
-            markedAny = true;
+            tracedAny = true;
         }
     }
 
     if (!optsAllTypes_)
-        return markedAny;
+        return tracedAny;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
-        if (ShouldMarkProvider::ShouldMark(&iter->type)) {
+        if (ShouldTraceProvider::ShouldTrace(&iter->type)) {
             iter->type.trace(trc);
-            markedAny = true;
+            tracedAny = true;
         }
-        if (iter->hasAllocationSite() && ShouldMarkProvider::ShouldMark(&iter->script)) {
+        if (iter->hasAllocationSite() && ShouldTraceProvider::ShouldTrace(&iter->script)) {
             TraceManuallyBarrieredEdge(trc, &iter->script,
                                        "jitcodeglobaltable-ionentry-type-addendum-script");
-            markedAny = true;
-        } else if (iter->hasConstructor() && ShouldMarkProvider::ShouldMark(&iter->constructor)) {
+            tracedAny = true;
+        } else if (iter->hasConstructor() && ShouldTraceProvider::ShouldTrace(&iter->constructor)) {
             TraceManuallyBarrieredEdge(trc, &iter->constructor,
                                        "jitcodeglobaltable-ionentry-type-addendum-constructor");
-            markedAny = true;
+            tracedAny = true;
         }
     }
 
-    return markedAny;
+    return tracedAny;
 }
 
 void
 JitcodeGlobalEntry::IonEntry::sweepChildren()
 {
     for (unsigned i = 0; i < numScripts(); i++)
         MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&sizedScriptList()->pairs[i].script));
 
@@ -962,22 +962,22 @@ JitcodeGlobalEntry::IonEntry::isMarkedFr
         {
             return false;
         }
     }
 
     return true;
 }
 
-template <class ShouldMarkProvider>
+template <class ShouldTraceProvider>
 bool
-JitcodeGlobalEntry::IonCacheEntry::mark(JSTracer* trc)
+JitcodeGlobalEntry::IonCacheEntry::trace(JSTracer* trc)
 {
     JitcodeGlobalEntry& entry = RejoinEntry(trc->runtime(), *this, nativeStartAddr());
-    return entry.mark<ShouldMarkProvider>(trc);
+    return entry.trace<ShouldTraceProvider>(trc);
 }
 
 void
 JitcodeGlobalEntry::IonCacheEntry::sweepChildren(JSRuntime* rt)
 {
     JitcodeGlobalEntry& entry = RejoinEntry(rt, *this, nativeStartAddr());
     entry.sweepChildren(rt);
 }
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -202,17 +202,17 @@ class JitcodeGlobalEntry
         }
         bool endsAbovePointer(void* ptr) const {
             return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr);
         }
         bool containsPointer(void* ptr) const {
             return startsBelowPointer(ptr) && endsAbovePointer(ptr);
         }
 
-        template <class ShouldMarkProvider> bool markJitcode(JSTracer* trc);
+        template <class ShouldTraceProvider> bool traceJitcode(JSTracer* trc);
         bool isJitcodeMarkedFromAnyThread();
         bool isJitcodeAboutToBeFinalized();
     };
 
     struct IonEntry : public BaseEntry
     {
         // regionTable_ points to the start of the region table within the
         // packed map for compile represented by this entry.  Since the
@@ -363,17 +363,17 @@ class JitcodeGlobalEntry
             void* ptr,
             uint32_t* entryOffsetOut);
 
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
-        template <class ShouldMarkProvider> bool mark(JSTracer* trc);
+        template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
         bool isMarkedFromAnyThread();
     };
 
     struct BaselineEntry : public BaseEntry
     {
         JSScript* script_;
         const char* str_;
@@ -421,17 +421,17 @@ class JitcodeGlobalEntry
                                           uint32_t* depth) const;
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
 
-        template <class ShouldMarkProvider> bool mark(JSTracer* trc);
+        template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren();
         bool isMarkedFromAnyThread();
     };
 
     struct IonCacheEntry : public BaseEntry
     {
         void* rejoinAddr_;
         JS::TrackedOutcome trackedOutcome_;
@@ -470,17 +470,17 @@ class JitcodeGlobalEntry
             JSRuntime *rt,
             void* ptr,
             uint32_t* entryOffsetOut);
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
-        template <class ShouldMarkProvider> bool mark(JSTracer* trc);
+        template <class ShouldTraceProvider> bool trace(JSTracer* trc);
         void sweepChildren(JSRuntime* rt);
         bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     // Dummy entries are created for jitcode generated when profiling is not turned on,
     // so that they have representation in the global table if they are on the
     // stack when profiling is enabled.
     struct DummyEntry : public BaseEntry
@@ -906,35 +906,35 @@ class JitcodeGlobalEntry
     const IonTrackedTypeVector* allTrackedTypes() {
         return ionEntry().allTrackedTypes();
     }
 
     Zone* zone() {
         return baseEntry().jitcode()->zone();
     }
 
-    template <class ShouldMarkProvider>
-    bool mark(JSTracer* trc) {
-        bool markedAny = baseEntry().markJitcode<ShouldMarkProvider>(trc);
+    template <class ShouldTraceProvider>
+    bool trace(JSTracer* trc) {
+        bool tracedAny = baseEntry().traceJitcode<ShouldTraceProvider>(trc);
         switch (kind()) {
           case Ion:
-            markedAny |= ionEntry().mark<ShouldMarkProvider>(trc);
+            tracedAny |= ionEntry().trace<ShouldTraceProvider>(trc);
             break;
           case Baseline:
-            markedAny |= baselineEntry().mark<ShouldMarkProvider>(trc);
+            tracedAny |= baselineEntry().trace<ShouldTraceProvider>(trc);
             break;
           case IonCache:
-            markedAny |= ionCacheEntry().mark<ShouldMarkProvider>(trc);
+            tracedAny |= ionCacheEntry().trace<ShouldTraceProvider>(trc);
             break;
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
-        return markedAny;
+        return tracedAny;
     }
 
     void sweepChildren(JSRuntime* rt) {
         switch (kind()) {
           case Ion:
             ionEntry().sweepChildren();
             break;
           case Baseline:
@@ -1054,18 +1054,18 @@ class JitcodeGlobalTable
     MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry, JSRuntime* rt) {
         return addEntry(JitcodeGlobalEntry(entry), rt);
     }
 
     void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
     void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt);
 
     void setAllEntriesAsExpired(JSRuntime* rt);
-    void markUnconditionally(JSTracer* trc);
-    MOZ_MUST_USE bool markIteratively(JSTracer* trc);
+    void trace(JSTracer* trc);
+    MOZ_MUST_USE bool markIteratively(GCMarker* marker);
     void sweep(JSRuntime* rt);
 
   private:
     MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry& entry, JSRuntime* rt);
 
     JitcodeGlobalEntry* lookupInternal(void* ptr);
 
     // Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1])
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -208,36 +208,36 @@ bool
 ICStub::makesGCCalls() const
 {
     if (isCacheIR_Monitored())
         return toCacheIR_Monitored()->stubInfo()->makesGCCalls();
     return NonCacheIRStubMakesGCCalls(kind());
 }
 
 void
-ICStub::markCode(JSTracer* trc, const char* name)
+ICStub::traceCode(JSTracer* trc, const char* name)
 {
     JitCode* stubJitCode = jitCode();
     TraceManuallyBarrieredEdge(trc, &stubJitCode, name);
 }
 
 void
 ICStub::updateCode(JitCode* code)
 {
     // Write barrier on the old code.
     JitCode::writeBarrierPre(jitCode());
     stubCode_ = code->raw();
 }
 
 /* static */ void
 ICStub::trace(JSTracer* trc)
 {
-    markCode(trc, "shared-stub-jitcode");
-
-    // If the stub is a monitored fallback stub, then mark the monitor ICs hanging
+    traceCode(trc, "shared-stub-jitcode");
+
+    // If the stub is a monitored fallback stub, then trace the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
     // that references the same stub chain.
     if (isMonitoredFallback()) {
         ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub();
         for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) {
             MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
             iter->trace(trc);
@@ -620,20 +620,20 @@ ICFallbackStub::unlinkStub(Zone* zone, I
         // We just have to reset its firstMonitorStub_ field to avoid a stale
         // pointer when purgeOptimizedStubs destroys all optimized monitor
         // stubs (unlinked stubs won't be updated).
         ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub();
         stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback);
     }
 
 #ifdef DEBUG
-    // Poison stub code to ensure we don't call this stub again. However, if this
-    // stub can make calls, a pointer to it may be stored in a stub frame on the
-    // stack, so we can't touch the stubCode_ or GC will crash when marking this
-    // pointer.
+    // Poison stub code to ensure we don't call this stub again. However, if
+    // this stub can make calls, a pointer to it may be stored in a stub frame
+    // on the stack, so we can't touch the stubCode_ or GC will crash when
+    // tracing this pointer.
     if (!stub->makesGCCalls())
         stub->stubCode_ = (uint8_t*)0xbad;
 #endif
 }
 
 void
 ICFallbackStub::unlinkStubsWithKind(JSContext* cx, ICStub::Kind kind)
 {
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -525,17 +525,17 @@ class ICStub
     enum Trait {
         Regular             = 0x0,
         Fallback            = 0x1,
         Monitored           = 0x2,
         MonitoredFallback   = 0x3,
         Updated             = 0x4
     };
 
-    void markCode(JSTracer* trc, const char* name);
+    void traceCode(JSTracer* trc, const char* name);
     void updateCode(JitCode* stubCode);
     void trace(JSTracer* trc);
 
     template <typename T, typename... Args>
     static T* New(JSContext* cx, ICStubSpace* space, JitCode* code, Args&&... args) {
         if (!code)
             return nullptr;
         T* result = space->allocate<T>(code, mozilla::Forward<Args>(args)...);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1216,24 +1216,28 @@ AssertValidStringPtr(JSContext* cx, JSSt
         MOZ_ASSERT(str->zone()->isAtomsZone());
     else
         MOZ_ASSERT(str->zone() == cx->zone());
 
     MOZ_ASSERT(str->isAligned());
     MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
 
     gc::AllocKind kind = str->getAllocKind();
-    if (str->isFatInline())
-        MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING);
-    else if (str->isExternal())
+    if (str->isFatInline()) {
+        MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING ||
+                   kind == gc::AllocKind::FAT_INLINE_ATOM);
+    } else if (str->isExternal()) {
         MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
-    else if (str->isAtom() || str->isFlat())
+    } else if (str->isAtom()) {
+        MOZ_ASSERT(kind == gc::AllocKind::ATOM);
+    } else if (str->isFlat()) {
         MOZ_ASSERT(kind == gc::AllocKind::STRING || kind == gc::AllocKind::FAT_INLINE_STRING);
-    else
+    } else {
         MOZ_ASSERT(kind == gc::AllocKind::STRING);
+    }
 #endif
 }
 
 void
 AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym)
 {
     // We can't closely inspect symbols from another runtime.
     if (sym->runtimeFromAnyThread() != cx->runtime()) {
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -192,17 +192,17 @@ JSRuntime::finishAtoms()
     staticStrings = nullptr;
     commonNames = nullptr;
     permanentAtoms = nullptr;
     wellKnownSymbols = nullptr;
     emptyString = nullptr;
 }
 
 void
-js::MarkAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
+js::TraceAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
 {
     JSRuntime* rt = trc->runtime();
 
     if (rt->atomsAreFinished())
         return;
 
     for (AtomSet::Enum e(rt->atoms(lock)); !e.empty(); e.popFront()) {
         const AtomStateEntry& entry = e.front();
@@ -211,21 +211,21 @@ js::MarkAtoms(JSTracer* trc, AutoLockFor
 
         JSAtom* atom = entry.asPtrUnbarriered();
         TraceRoot(trc, &atom, "interned_atom");
         MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
     }
 }
 
 void
-js::MarkPermanentAtoms(JSTracer* trc)
+js::TracePermanentAtoms(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
 
-    // Permanent atoms only need to be marked in the runtime which owns them.
+    // Permanent atoms only need to be traced in the runtime which owns them.
     if (rt->parentRuntime)
         return;
 
     // Static strings are not included in the permanent atoms table.
     if (rt->staticStrings)
         rt->staticStrings->trace(trc);
 
     if (rt->permanentAtoms) {
@@ -234,17 +234,17 @@ js::MarkPermanentAtoms(JSTracer* trc)
 
             JSAtom* atom = entry.asPtrUnbarriered();
             TraceProcessGlobalRoot(trc, atom, "permanent_table");
         }
     }
 }
 
 void
-js::MarkWellKnownSymbols(JSTracer* trc)
+js::TraceWellKnownSymbols(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
 
     if (rt->parentRuntime)
         return;
 
     if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
         for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
@@ -345,17 +345,18 @@ AtomizeAndCopyChars(ExclusiveContext* cx
     if (!flat) {
         // Grudgingly forgo last-ditch GC. The alternative would be to release
         // the lock, manually GC here, and retry from the top. If you fix this,
         // please also fix or comment the similar case in Symbol::new_.
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
-    JSAtom* atom = flat->morphAtomizedStringIntoAtom();
+    JSAtom* atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
+    MOZ_ASSERT(atom->hash() == lookup.hash);
 
     // We have held the lock since looking up p, and the operations we've done
     // since then can't GC; therefore the atoms table has not been modified and
     // p is still valid.
     if (!atoms.add(p, AtomStateEntry(atom, bool(pin)))) {
         ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
         return nullptr;
     }
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -18,24 +18,16 @@
 #include "js/GCHashTable.h"
 #include "vm/CommonPropertyNames.h"
 
 class JSAtom;
 class JSAutoByteString;
 
 namespace js {
 
-JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
-
-static MOZ_ALWAYS_INLINE js::HashNumber
-HashId(jsid id)
-{
-    return mozilla::HashGeneric(JSID_BITS(id));
-}
-
 /*
  * Return a printable, lossless char[] representation of a string-type atom.
  * The lifetime of the result matches the lifetime of bytes.
  */
 extern const char*
 AtomToPrintableString(ExclusiveContext* cx, JSAtom* atom, JSAutoByteString* bytes);
 
 class AtomStateEntry
@@ -192,23 +184,23 @@ extern const char js_with_str[];
 namespace js {
 
 class AutoLockForExclusiveAccess;
 
 /*
  * Atom tracing and garbage collection hooks.
  */
 void
-MarkAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock);
+TraceAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock);
 
 void
-MarkPermanentAtoms(JSTracer* trc);
+TracePermanentAtoms(JSTracer* trc);
 
 void
-MarkWellKnownSymbols(JSTracer* trc);
+TraceWellKnownSymbols(JSTracer* trc);
 
 /* N.B. must correspond to boolean tagging behavior. */
 enum PinningBehavior
 {
     DoNotPinAtom = false,
     PinAtom = true
 };
 
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -151,32 +151,33 @@ IdToString(JSContext* cx, jsid id)
 
     return str->ensureFlat(cx);
 }
 
 inline
 AtomHasher::Lookup::Lookup(const JSAtom* atom)
   : isLatin1(atom->hasLatin1Chars()), length(atom->length()), atom(atom)
 {
+    hash = atom->hash();
     if (isLatin1) {
         latin1Chars = atom->latin1Chars(nogc);
-        hash = mozilla::HashString(latin1Chars, length);
+        MOZ_ASSERT(mozilla::HashString(latin1Chars, length) == hash);
     } else {
         twoByteChars = atom->twoByteChars(nogc);
-        hash = mozilla::HashString(twoByteChars, length);
+        MOZ_ASSERT(mozilla::HashString(twoByteChars, length) == hash);
     }
 }
 
 inline bool
 AtomHasher::match(const AtomStateEntry& entry, const Lookup& lookup)
 {
     JSAtom* key = entry.asPtrUnbarriered();
     if (lookup.atom)
         return lookup.atom == key;
-    if (key->length() != lookup.length)
+    if (key->length() != lookup.length || key->hash() != lookup.hash)
         return false;
 
     if (key->hasLatin1Chars()) {
         const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
         if (lookup.isLatin1)
             return mozilla::PodEqual(keyChars, lookup.latin1Chars, lookup.length);
         return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
     }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -241,16 +241,23 @@ js::ReportOutOfMemory(ExclusiveContext* 
 
     /* Report the oom. */
     if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback)
         oomCallback(cx, cx->runtime()->oomCallbackData);
 
     cx->setPendingException(StringValue(cx->names().outOfMemory));
 }
 
+mozilla::GenericErrorResult<OOM&>
+js::ReportOutOfMemoryResult(ExclusiveContext* cx)
+{
+    ReportOutOfMemory(cx);
+    return cx->alreadyReportedOOM();
+}
+
 void
 js::ReportOverRecursed(JSContext* maybecx, unsigned errorNumber)
 {
 #ifdef JS_MORE_DETERMINISTIC
     /*
      * We cannot make stack depth deterministic across different
      * implementations (e.g. JIT vs. interpreter will differ in
      * their maximum stack depth).
@@ -870,16 +877,44 @@ ExclusiveContext::recoverFromOutOfMemory
         }
         return;
     }
     // Keep in sync with addPendingOutOfMemory.
     if (ParseTask* task = helperThread()->parseTask())
         task->outOfMemory = false;
 }
 
+JS::Error ExclusiveContext::reportedError;
+JS::OOM ExclusiveContext::reportedOOM;
+
+mozilla::GenericErrorResult<OOM&>
+ExclusiveContext::alreadyReportedOOM()
+{
+#ifdef DEBUG
+    if (JSContext* maybecx = maybeJSContext()) {
+        MOZ_ASSERT(maybecx->isThrowingOutOfMemory());
+    } else {
+        // Keep in sync with addPendingOutOfMemory.
+        if (ParseTask* task = helperThread()->parseTask())
+            MOZ_ASSERT(task->outOfMemory);
+    }
+#endif
+    return mozilla::MakeGenericErrorResult(reportedOOM);
+}
+
+mozilla::GenericErrorResult<JS::Error&>
+ExclusiveContext::alreadyReportedError()
+{
+#ifdef DEBUG
+    if (JSContext* maybecx = maybeJSContext())
+        MOZ_ASSERT(maybecx->isExceptionPending());
+#endif
+    return mozilla::MakeGenericErrorResult(reportedError);
+}
+
 JSContext::JSContext(JSRuntime* parentRuntime)
   : ExclusiveContext(this, &this->JSRuntime::mainThread, Context_JS, JS::ContextOptions()),
     JSRuntime(parentRuntime),
     throwing(false),
     unwrappedException_(this),
     overRecursed_(false),
     propagatingForcedReturn_(false),
     liveVolatileJitFrameIterators_(nullptr),
@@ -1027,22 +1062,22 @@ JSContext::sizeOfExcludingThis(mozilla::
      * There are other JSContext members that could be measured; the following
      * ones have been found by DMD to be worth measuring.  More stuff may be
      * added later.
      */
     return cycleDetectorSet.sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
-JSContext::mark(JSTracer* trc)
+JSContext::trace(JSTracer* trc)
 {
     if (cycleDetectorSet.initialized())
         TraceCycleDetectionSet(trc, cycleDetectorSet);
 
-    if (compartment_)
+    if (trc->isMarkingTracer() && compartment_)
         compartment_->mark();
 }
 
 void*
 ExclusiveContext::stackLimitAddressForJitCode(StackKind kind)
 {
 #ifdef JS_SIMULATOR
     return runtime_->addressOfSimulatorStackLimit();
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -8,16 +8,17 @@
 
 #ifndef jscntxt_h
 #define jscntxt_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "js/CharacterEncoding.h"
 #include "js/GCVector.h"
+#include "js/Result.h"
 #include "js/Vector.h"
 #include "vm/Caches.h"
 #include "vm/Runtime.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
@@ -309,16 +310,40 @@ class ExclusiveContext : public ContextF
     ScriptDataTable& scriptDataTable(AutoLockForExclusiveAccess& lock) {
         return runtime_->scriptDataTable(lock);
     }
 
     // Methods specific to any HelperThread for the context.
     bool addPendingCompileError(frontend::CompileError** err);
     void addPendingOverRecursed();
     void addPendingOutOfMemory();
+
+  private:
+    static JS::Error reportedError;
+    static JS::OOM reportedOOM;
+
+  public:
+    inline JS::Result<> boolToResult(bool ok);
+
+    /**
+     * Intentionally awkward signpost method that is stationed on the
+     * boundary between Result-using and non-Result-using code.
+     */
+    template <typename V, typename E>
+    bool resultToBool(JS::Result<V, E> result) {
+        return result.isOk();
+    }
+
+    template <typename V, typename E>
+    V* resultToPtr(JS::Result<V*, E> result) {
+        return result.isOk() ? result.unwrap() : nullptr;
+    }
+
+    mozilla::GenericErrorResult<JS::OOM&> alreadyReportedOOM();
+    mozilla::GenericErrorResult<JS::Error&> alreadyReportedError();
 };
 
 void ReportOverRecursed(JSContext* cx, unsigned errorNumber);
 
 } /* namespace js */
 
 struct JSContext : public js::ExclusiveContext,
                    public JSRuntime
@@ -481,17 +506,17 @@ struct JSContext : public js::ExclusiveC
         return gc.nursery;
     }
 
     void minorGC(JS::gcreason::Reason reason) {
         gc.minorGC(reason);
     }
 
   public:
-    bool isExceptionPending() {
+    bool isExceptionPending() const {
         return throwing;
     }
 
     MOZ_MUST_USE
     bool getPendingException(JS::MutableHandleValue rval);
 
     bool isThrowingOutOfMemory();
     bool isThrowingDebuggeeWouldRun();
@@ -513,30 +538,41 @@ struct JSContext : public js::ExclusiveC
     /*
      * See JS_SetTrustedPrincipals in jsapi.h.
      * Note: !cx->compartment is treated as trusted.
      */
     inline bool runningWithTrustedPrincipals() const;
 
     JS_FRIEND_API(size_t) sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
-    void mark(JSTracer* trc);
+    void trace(JSTracer* trc);
 
   private:
     /*
      * The allocation code calls the function to indicate either OOM failure
      * when p is null or that a memory pressure counter has reached some
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void* p);
 }; /* struct JSContext */
 
 namespace js {
 
+inline JS::Result<>
+ExclusiveContext::boolToResult(bool ok)
+{
+    if (MOZ_LIKELY(ok)) {
+        MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isExceptionPending());
+        MOZ_ASSERT_IF(isJSContext(), !asJSContext()->isPropagatingForcedReturn());
+        return JS::Ok();
+    }
+    return JS::Result<>(reportedError);
+}
+
 struct MOZ_RAII AutoResolving {
   public:
     enum Kind {
         LOOKUP,
         WATCH
     };
 
     AutoResolving(JSContext* cx, HandleObject obj, HandleId id, Kind kind = LOOKUP
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -45,16 +45,17 @@ using mozilla::PodArrayZero;
 
 JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options = JS::CompartmentOptions())
   : creationOptions_(options.creationOptions()),
     behaviors_(options.behaviors()),
     zone_(zone),
     runtime_(zone->runtimeFromMainThread()),
     principals_(nullptr),
     isSystem_(false),
+    isAtomsCompartment_(false),
     isSelfHosting(false),
     marked(true),
     warnedAboutExprClosure(false),
     warnedAboutForEach(false),
 #ifdef DEBUG
     firedOnNewGlobalObject(false),
 #endif
     global_(nullptr),
@@ -599,17 +600,17 @@ JSCompartment::traceOutgoingCrossCompart
 JSCompartment::traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc)
 {
     gcstats::AutoPhase ap(trc->runtime()->gc.stats, gcstats::PHASE_MARK_CCWS);
     MOZ_ASSERT(trc->runtime()->isHeapMajorCollecting());
     for (CompartmentsIter c(trc->runtime(), SkipAtoms); !c.done(); c.next()) {
         if (!c->zone()->isCollecting())
             c->traceOutgoingCrossCompartmentWrappers(trc);
     }
-    Debugger::markIncomingCrossCompartmentEdges(trc);
+    Debugger::traceIncomingCrossCompartmentEdges(trc);
 }
 
 void
 JSCompartment::trace(JSTracer* trc)
 {
     savedStacks_.trace(trc);
 
     // Atoms are always tenured.
@@ -626,38 +627,38 @@ JSCompartment::traceRoots(JSTracer* trc,
                   "on-stack object pending metadata");
     }
 
     if (!trc->runtime()->isHeapMinorCollecting()) {
         // JIT code and the global are never nursery allocated, so we only need
         // to trace them when not doing a minor collection.
 
         if (jitCompartment_)
-            jitCompartment_->mark(trc, this);
+            jitCompartment_->trace(trc, this);
 
         // If a compartment is on-stack, we mark its global so that
         // JSContext::global() remains valid.
         if (enterCompartmentDepth && global_.unbarrieredGet())
             TraceRoot(trc, global_.unsafeUnbarrieredForTracing(), "on-stack compartment global");
     }
 
     // Nothing below here needs to be treated as a root if we aren't marking
     // this zone for a collection.
     if (traceOrMark == js::gc::GCRuntime::MarkRuntime && !zone()->isCollecting())
         return;
 
     // During a GC, these are treated as weak pointers.
     if (traceOrMark == js::gc::GCRuntime::TraceRuntime) {
         if (watchpointMap)
-            watchpointMap->markAll(trc);
+            watchpointMap->trace(trc);
     }
 
     /* Mark debug scopes, if present */
     if (debugEnvs)
-        debugEnvs->mark(trc);
+        debugEnvs->trace(trc);
 
     if (lazyArrayBuffers)
         lazyArrayBuffers->trace(trc);
 
     if (objectMetadataTable)
         objectMetadataTable->trace(trc);
 
     // If code coverage is only enabled with the Debugger or the LCovOutput,
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -332,26 +332,35 @@ struct JSCompartment
         // to a group to which we do not belong anymore. For another thing,
         // we use `isSystem()` as part of the key to map compartments
         // to a `PerformanceGroup`, so if we do not unlink now, this will
         // be too late once we have updated `isSystem_`.
         performanceMonitoring.unlink();
         isSystem_ = isSystem;
     }
 
+    bool isAtomsCompartment() const {
+        return isAtomsCompartment_;
+    }
+    void setIsAtomsCompartment() {
+        isAtomsCompartment_ = true;
+    }
+
     // Used to approximate non-content code when reporting telemetry.
     inline bool isProbablySystemOrAddonCode() const {
         if (creationOptions_.addonIdOrNull())
             return true;
 
         return isSystem_;
     }
   private:
     JSPrincipals*                principals_;
     bool                         isSystem_;
+    bool                         isAtomsCompartment_;
+
   public:
     bool                         isSelfHosting;
     bool                         marked;
     bool                         warnedAboutExprClosure;
     bool                         warnedAboutForEach;
 
 #ifdef DEBUG
     bool                         firedOnNewGlobalObject;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -360,16 +360,18 @@ static const FinalizePhase BackgroundFin
         gcstats::PHASE_SWEEP_SCOPE, {
             AllocKind::SCOPE
         }
     },
     {
         gcstats::PHASE_SWEEP_STRING, {
             AllocKind::FAT_INLINE_STRING,
             AllocKind::STRING,
+            AllocKind::FAT_INLINE_ATOM,
+            AllocKind::ATOM,
             AllocKind::SYMBOL
         }
     },
     {
         gcstats::PHASE_SWEEP_SHAPE, {
             AllocKind::SHAPE,
             AllocKind::ACCESSOR_SHAPE,
             AllocKind::BASE_SHAPE,
@@ -1766,17 +1768,19 @@ static const AllocKind AllocKindsToReloc
     AllocKind::SCRIPT,
     AllocKind::LAZY_SCRIPT,
     AllocKind::SCOPE,
     AllocKind::SHAPE,
     AllocKind::ACCESSOR_SHAPE,
     AllocKind::BASE_SHAPE,
     AllocKind::FAT_INLINE_STRING,
     AllocKind::STRING,
-    AllocKind::EXTERNAL_STRING
+    AllocKind::EXTERNAL_STRING,
+    AllocKind::FAT_INLINE_ATOM,
+    AllocKind::ATOM
 };
 
 Arena*
 ArenaList::removeRemainingArenas(Arena** arenap)
 {
     // This is only ever called to remove arenas that are after the cursor, so
     // we don't need to update it.
 #ifdef DEBUG
@@ -2541,24 +2545,24 @@ GCRuntime::updatePointersToRelocatedCell
     // as much as possible.
     updateAllCellPointers(&trc, zone);
 
     // Mark roots to update them.
     {
         traceRuntimeForMajorGC(&trc, lock);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
-        Debugger::markAll(&trc);
-        Debugger::markIncomingCrossCompartmentEdges(&trc);
-
-        WeakMapBase::markAll(zone, &trc);
+        Debugger::traceAll(&trc);
+        Debugger::traceIncomingCrossCompartmentEdges(&trc);
+
+        WeakMapBase::traceZone(zone, &trc);
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
             c->trace(&trc);
             if (c->watchpointMap)
-                c->watchpointMap->markAll(&trc);
+                c->watchpointMap->trace(&trc);
         }
 
         // Mark all gray roots, making sure we call the trace callback to get the
         // current set.
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&trc, grayRootTracer.data);
     }
 
@@ -4025,17 +4029,17 @@ GCRuntime::markWeakReferences(gcstats::P
         if (!marker.isWeakMarkingTracer()) {
             for (ZoneIterT zone(rt); !zone.done(); zone.next())
                 markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
         }
         for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) {
             if (c->watchpointMap)
                 markedAny |= c->watchpointMap->markIteratively(&marker);
         }
-        markedAny |= Debugger::markAllIteratively(&marker);
+        markedAny |= Debugger::markIteratively(&marker);
         markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
 
         if (!markedAny)
             break;
 
         auto unlimited = SliceBudget::unlimited();
         MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
     }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -111,16 +111,18 @@ IsNurseryAllocable(AllocKind kind)
         false,     /* AllocKind::LAZY_SCRIPT */
         false,     /* AllocKind::SHAPE */
         false,     /* AllocKind::ACCESSOR_SHAPE */
         false,     /* AllocKind::BASE_SHAPE */
         false,     /* AllocKind::OBJECT_GROUP */
         false,     /* AllocKind::FAT_INLINE_STRING */
         false,     /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
+        false,     /* AllocKind::FAT_INLINE_ATOM */
+        false,     /* AllocKind::ATOM */
         false,     /* AllocKind::SYMBOL */
         false,     /* AllocKind::JITCODE */
         false,     /* AllocKind::SCOPE */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
     return map[size_t(kind)];
 }
 
@@ -147,16 +149,18 @@ IsBackgroundFinalized(AllocKind kind)
         true,      /* AllocKind::LAZY_SCRIPT */
         true,      /* AllocKind::SHAPE */
         true,      /* AllocKind::ACCESSOR_SHAPE */
         true,      /* AllocKind::BASE_SHAPE */
         true,      /* AllocKind::OBJECT_GROUP */
         true,      /* AllocKind::FAT_INLINE_STRING */
         true,      /* AllocKind::STRING */
         false,     /* AllocKind::EXTERNAL_STRING */
+        true,      /* AllocKind::FAT_INLINE_ATOM */
+        true,      /* AllocKind::ATOM */
         true,      /* AllocKind::SYMBOL */
         false,     /* AllocKind::JITCODE */
         true,      /* AllocKind::SCOPE */
     };
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
     return map[size_t(kind)];
 }
 
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -63,27 +63,17 @@ NativeIterator::trace(JSTracer* trc)
         guard_array[i].trace(trc);
 
     // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
     // GC removes any elements from the list, it won't remove this one.
     if (iterObj_)
         TraceManuallyBarrieredEdge(trc, &iterObj_, "iterObj");
 }
 
-struct IdHashPolicy {
-    typedef jsid Lookup;
-    static HashNumber hash(jsid id) {
-        return JSID_BITS(id);
-    }
-    static bool match(jsid id1, jsid id2) {
-        return id1 == id2;
-    }
-};
-
-typedef HashSet<jsid, IdHashPolicy> IdSet;
+typedef HashSet<jsid, DefaultHasher<jsid>> IdSet;
 
 static inline bool
 NewKeyValuePair(JSContext* cx, jsid id, const Value& val, MutableHandleValue rval)
 {
     return NewValuePair(cx, IdToValue(id), val, rval);
 }
 
 static inline bool
@@ -555,20 +545,20 @@ NewPropertyIteratorObject(JSContext* cx,
             return nullptr;
 
         const Class* clasp = &PropertyIteratorObject::class_;
         RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
                                                           ITERATOR_FINALIZE_KIND));
         if (!shape)
             return nullptr;
 
-        JSObject* obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
-                                         GetInitialHeap(GenericObject, clasp), shape, group);
-        if (!obj)
-            return nullptr;
+        JSObject* obj;
+        JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, ITERATOR_FINALIZE_KIND,
+                                                            GetInitialHeap(GenericObject, clasp),
+                                                            shape, group));
 
         PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
 
         MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
         return res;
     }
 
     Rooted<PropertyIteratorObject*> res(cx, NewBuiltinClassInstance<PropertyIteratorObject>(cx));
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -254,25 +254,25 @@ js::Throw(JSContext* cx, JSObject* obj, 
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
     }
     return false;
 }
 
 
 /*** PropertyDescriptor operations and DefineProperties ******************************************/
 
-bool
+static Result<>
 CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
 {
     if (obj && !obj->isCallable()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                   fieldName);
-        return false;
+        return cx->alreadyReportedError();
     }
-    return true;
+    return Ok();
 }
 
 bool
 js::ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
                          MutableHandle<PropertyDescriptor> desc)
 {
     // step 2
     RootedObject obj(cx, NonNullObject(cx, descval));
@@ -332,36 +332,36 @@ js::ToPropertyDescriptor(JSContext* cx, 
     // step 8
     bool hasGetOrSet;
     id = NameToId(cx->names().get);
     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
         return false;
     hasGetOrSet = found;
     if (found) {
         if (v.isObject()) {
-            if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_getter_str))
-                return false;
+            if (checkAccessors)
+                JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_getter_str));
             desc.setGetterObject(&v.toObject());
         } else if (!v.isUndefined()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                       js_getter_str);
             return false;
         }
         attrs |= JSPROP_GETTER | JSPROP_SHARED;
     }
 
     // step 9
     id = NameToId(cx->names().set);
     if (!GetPropertyIfPresent(cx, obj, id, &v, &found))
         return false;
     hasGetOrSet |= found;
     if (found) {
         if (v.isObject()) {
-            if (checkAccessors && !CheckCallable(cx, &v.toObject(), js_setter_str))
-                return false;
+            if (checkAccessors)
+                JS_TRY_OR_RETURN_FALSE(cx, CheckCallable(cx, &v.toObject(), js_setter_str));
             desc.setSetterObject(&v.toObject());
         } else if (!v.isUndefined()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                       js_setter_str);
             return false;
         }
         attrs |= JSPROP_SETTER | JSPROP_SHARED;
     }
@@ -378,28 +378,26 @@ js::ToPropertyDescriptor(JSContext* cx, 
     }
 
     desc.setAttributes(attrs);
     MOZ_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
     return true;
 }
 
-bool
+Result<>
 js::CheckPropertyDescriptorAccessors(JSContext* cx, Handle<PropertyDescriptor> desc)
 {
-    if (desc.hasGetterObject()) {
-        if (!CheckCallable(cx, desc.getterObject(), js_getter_str))
-            return false;
-    }
-    if (desc.hasSetterObject()) {
-        if (!CheckCallable(cx, desc.setterObject(), js_setter_str))
-            return false;
-    }
-    return true;
+    if (desc.hasGetterObject())
+        MOZ_TRY(CheckCallable(cx, desc.getterObject(), js_getter_str));
+
+    if (desc.hasSetterObject())
+        MOZ_TRY(CheckCallable(cx, desc.setterObject(), js_setter_str));
+
+    return Ok();
 }
 
 void
 js::CompletePropertyDescriptor(MutableHandle<PropertyDescriptor> desc)
 {
     desc.assertValid();
 
     if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
@@ -643,19 +641,18 @@ NewObject(ExclusiveContext* cx, HandleOb
                     : GetGCKindSlots(kind, clasp);
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, group->proto(), nfixed,
                                                       initialShapeFlags));
     if (!shape)
         return nullptr;
 
     gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
-    JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
-    if (!obj)
-        return nullptr;
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
 
     if (newKind == SingletonObject) {
         RootedObject nobj(cx, obj);
         if (!JSObject::setSingleton(cx, nobj))
             return nullptr;
         obj = nobj;
     }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -177,21 +177,19 @@ class JSObject : public js::gc::Cell
 
     inline js::Shape* maybeShape() const;
     inline js::Shape* ensureShape(js::ExclusiveContext* cx);
 
     /*
      * Make a non-array object with the specified initial state. This method
      * takes ownership of any extantSlots it is passed.
      */
-    static inline JSObject* create(js::ExclusiveContext* cx,
-                                   js::gc::AllocKind kind,
-                                   js::gc::InitialHeap heap,
-                                   js::HandleShape shape,
-                                   js::HandleObjectGroup group);
+    static inline JS::Result<JSObject*, JS::OOM&>
+    create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
+           js::HandleShape shape, js::HandleObjectGroup group);
 
     // Set the initial slots and elements of an object. These pointers are only
     // valid for native objects, but during initialization are set for all
     // objects. For non-native objects, these must not be dynamically allocated
     // pointers which leak when the non-native object finishes initialization.
     inline void setInitialSlotsMaybeNonNative(js::HeapSlot* slots);
     inline void setInitialElementsMaybeNonNative(js::HeapSlot* elements);
 
@@ -1191,17 +1189,17 @@ bool
 ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
                      MutableHandle<JS::PropertyDescriptor> desc);
 
 /*
  * Throw a TypeError if desc.getterObject() or setterObject() is not
  * callable. This performs exactly the checks omitted by ToPropertyDescriptor
  * when checkAccessors is false.
  */
-bool
+Result<>
 CheckPropertyDescriptorAccessors(JSContext* cx, Handle<JS::PropertyDescriptor> desc);
 
 void
 CompletePropertyDescriptor(MutableHandle<JS::PropertyDescriptor> desc);
 
 /*
  * Read property descriptors from props, as for Object.defineProperties. See
  * ES5 15.2.3.7 steps 3-5.
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -316,17 +316,17 @@ SetNewObjectMetadata(ExclusiveContext* c
         }
     }
 
     return obj;
 }
 
 } // namespace js
 
-/* static */ inline JSObject*
+/* static */ inline JS::Result<JSObject*, JS::OOM&>
 JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
                  js::HandleShape shape, js::HandleObjectGroup group)
 {
     const js::Class* clasp = group->clasp();
 
     MOZ_ASSERT(shape && group);
     MOZ_ASSERT(clasp == shape->getObjectClass());
     MOZ_ASSERT(clasp != &js::ArrayObject::class_);
@@ -372,17 +372,17 @@ JSObject::create(js::ExclusiveContext* c
     } else if (clasp->isProxy()) {
         // Proxy objects overlay the |slots| field with a ProxyValueArray.
         MOZ_ASSERT(sizeof(js::detail::ProxyValueArray) % sizeof(js::HeapSlot) == 0);
         nDynamicSlots = sizeof(js::detail::ProxyValueArray) / sizeof(js::HeapSlot);
     }
 
     JSObject* obj = js::Allocate<JSObject>(cx, kind, nDynamicSlots, heap, clasp);
     if (!obj)
-        return nullptr;
+        return cx->alreadyReportedOOM();
 
     obj->group_.init(group);
 
     // This function allocates normal objects and proxies and typed objects
     // (all with shapes), *and* it allocates objects without shapes (various
     // unboxed object classes).  Setting shape is naturally only valid for the
     // former class of objects.
     if (obj->is<js::ShapedObject>())
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -14,16 +14,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprototypes.h"
 #include "jstypes.h"
 
+#include "js/Result.h"
 #include "js/TraceKind.h"
 #include "js/TypeDecls.h"
 
 #if defined(JS_GC_ZEAL) || defined(DEBUG)
 # define JSGC_HASH_TABLE_CHECKS
 #endif
 
 namespace JS {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3637,17 +3637,17 @@ void
 JSScript::traceChildren(JSTracer* trc)
 {
     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
     // may have created it and partially initialized it with
     // JSScript::Create(), but not yet finished initializing it with
     // fullyInitFromEmitter() or fullyInitTrivial().
 
     MOZ_ASSERT_IF(trc->isMarkingTracer() &&
-                  static_cast<GCMarker*>(trc)->shouldCheckCompartments(),
+                  GCMarker::fromTracer(trc)->shouldCheckCompartments(),
                   zone()->isCollecting());
 
     if (scriptData())
         scriptData()->traceChildren(trc);
 
     if (ScopeArray* scopearray = scopes())
         TraceRange(trc, scopearray->length, scopearray->vector, "scopes");
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -32,17 +32,17 @@
 #include "jsutil.h"
 
 #include "builtin/Intl.h"
 #include "builtin/RegExp.h"
 #include "jit/InlinableNatives.h"
 #include "js/Conversions.h"
 #include "js/UniquePtr.h"
 #if ENABLE_INTL_API
-#include "unicode/unorm.h"
+#include "unicode/unorm2.h"
 #endif
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Opcodes.h"
 #include "vm/Printer.h"
 #include "vm/RegExpObject.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringBuffer.h"
@@ -926,89 +926,151 @@ js::str_localeCompare(JSContext* cx, uns
         return false;
 
     args.rval().setInt32(result);
     return true;
 }
 #endif
 
 #if EXPOSE_INTL_API
-/* ES6 20140210 draft 21.1.3.12. */
+// ES2017 draft rev 45e890512fd77add72cc0ee742785f9f6f6482de
+// 21.1.3.12 String.prototype.normalize ( [ form ] )
 bool
 js::str_normalize(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    // Steps 1-3.
+    // Steps 1-2.
     RootedString str(cx, ToStringForStringFunction(cx, args.thisv()));
     if (!str)
         return false;
 
-    // Step 4.
-    UNormalizationMode form;
+    enum NormalizationForm {
+        NFC, NFD, NFKC, NFKD
+    };
+
+    NormalizationForm form;
     if (!args.hasDefined(0)) {
-        form = UNORM_NFC;
+        // Step 3.
+        form = NFC;
     } else {
-        // Steps 5-6.
+        // Step 4.
         RootedLinearString formStr(cx, ArgToRootedString(cx, args, 0));
         if (!formStr)
             return false;
 
-        // Step 7.
+        // Step 5.
         if (EqualStrings(formStr, cx->names().NFC)) {
-            form = UNORM_NFC;
+            form = NFC;
         } else if (EqualStrings(formStr, cx->names().NFD)) {
-            form = UNORM_NFD;
+            form = NFD;
         } else if (EqualStrings(formStr, cx->names().NFKC)) {
-            form = UNORM_NFKC;
+            form = NFKC;
         } else if (EqualStrings(formStr, cx->names().NFKD)) {
-            form = UNORM_NFKD;
+            form = NFKD;
         } else {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INVALID_NORMALIZE_FORM);
             return false;
         }
     }
 
-    // Step 8.
+    JSLinearString* linear = str->ensureLinear(cx);
+    if (!linear)
+        return false;
+
+    // Latin1 strings are already in Normalization Form C.
+    if (form == NFC && linear->hasLatin1Chars()) {
+        // Step 7.
+        args.rval().setString(str);
+        return true;
+    }
+
+    // Step 6.
     AutoStableStringChars stableChars(cx);
-    if (!str->ensureFlat(cx) || !stableChars.initTwoByte(cx, str))
+    if (!stableChars.initTwoByte(cx, linear))
         return false;
 
+    mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
+
+    // The unorm2_getXXXInstance() methods return a shared instance which must
+    // not be deleted.
+    UErrorCode status = U_ZERO_ERROR;
+    const UNormalizer2* normalizer;
+    if (form == NFC) {
+        normalizer = unorm2_getNFCInstance(&status);
+    } else if (form == NFD) {
+        normalizer = unorm2_getNFDInstance(&status);
+    } else if (form == NFKC) {
+        normalizer = unorm2_getNFKCInstance(&status);
+    } else {
+        MOZ_ASSERT(form == NFKD);
+        normalizer = unorm2_getNFKDInstance(&status);
+    }
+    if (U_FAILURE(status)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+        return false;
+    }
+
+    int32_t spanLength = unorm2_spanQuickCheckYes(normalizer,
+                                                  Char16ToUChar(srcChars.begin().get()),
+                                                  srcChars.length(), &status);
+    if (U_FAILURE(status)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
+        return false;
+    }
+    MOZ_ASSERT(0 <= spanLength && size_t(spanLength) <= srcChars.length());
+
+    // Return if the input string is already normalized.
+    if (size_t(spanLength) == srcChars.length()) {
+        // Step 7.
+        args.rval().setString(str);
+        return true;
+    }
+
     static const size_t INLINE_CAPACITY = 32;
 
-    const UChar* srcChars = Char16ToUChar(stableChars.twoByteRange().begin().get());
-    int32_t srcLen = AssertedCast<int32_t>(str->length());
     Vector<char16_t, INLINE_CAPACITY> chars(cx);
-    if (!chars.resize(INLINE_CAPACITY))
+    if (!chars.resize(Max(INLINE_CAPACITY, srcChars.length())))
         return false;
 
-    UErrorCode status = U_ZERO_ERROR;
-    int32_t size = unorm_normalize(srcChars, srcLen, form, 0,
-                                   Char16ToUChar(chars.begin()), INLINE_CAPACITY,
-                                   &status);
+    // Copy the already normalized prefix.
+    if (spanLength > 0)
+        PodCopy(chars.begin(), srcChars.begin().get(), size_t(spanLength));
+
+    mozilla::RangedPtr<const char16_t> remainingStart = srcChars.begin() + spanLength;
+    size_t remainingLength = srcChars.length() - size_t(spanLength);
+
+    int32_t size = unorm2_normalizeSecondAndAppend(normalizer, Char16ToUChar(chars.begin()),
+                                                   spanLength, chars.length(),
+                                                   Char16ToUChar(remainingStart.get()),
+                                                   remainingLength, &status);
     if (status == U_BUFFER_OVERFLOW_ERROR) {
+        MOZ_ASSERT(size >= 0);
         if (!chars.resize(size))
             return false;
         status = U_ZERO_ERROR;
 #ifdef DEBUG
         int32_t finalSize =
 #endif
-        unorm_normalize(srcChars, srcLen, form, 0,
-                        Char16ToUChar(chars.begin()), size,
-                        &status);
-        MOZ_ASSERT(size == finalSize || U_FAILURE(status), "unorm_normalize behaved inconsistently");
+        unorm2_normalizeSecondAndAppend(normalizer, Char16ToUChar(chars.begin()), spanLength,
+                                        chars.length(), Char16ToUChar(remainingStart.get()),
+                                        remainingLength, &status);
+        MOZ_ASSERT_IF(!U_FAILURE(status), size == finalSize);
     }
-    if (U_FAILURE(status))
+    if (U_FAILURE(status)) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
-
+    }
+
+    MOZ_ASSERT(size >= 0);
     JSString* ns = NewStringCopyN<CanGC>(cx, chars.begin(), size);
     if (!ns)
         return false;
 
-    // Step 9.
+    // Step 7.
     args.rval().setString(ns);
     return true;
 }
 #endif
 
 bool
 js::str_charAt(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -140,52 +140,52 @@ WatchpointMap::triggerWatchpoint(JSConte
     // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
     JS::ExposeObjectToActiveJS(closure);
 
     /* Call the handler. */
     return handler(cx, obj, id, old, vp.address(), closure);
 }
 
 bool
-WatchpointMap::markIteratively(JSTracer* trc)
+WatchpointMap::markIteratively(GCMarker* marker)
 {
     bool marked = false;
     for (Map::Enum e(map); !e.empty(); e.popFront()) {
         Map::Entry& entry = e.front();
         JSObject* priorKeyObj = entry.key().object;
         jsid priorKeyId(entry.key().id.get());
         bool objectIsLive =
             IsMarked(const_cast<PreBarrieredObject*>(&entry.key().object));
         if (objectIsLive || entry.value().held) {
             if (!objectIsLive) {
-                TraceEdge(trc, const_cast<PreBarrieredObject*>(&entry.key().object),
+                TraceEdge(marker, const_cast<PreBarrieredObject*>(&entry.key().object),
                            "held Watchpoint object");
                 marked = true;
             }
 
             MOZ_ASSERT(JSID_IS_STRING(priorKeyId) ||
                        JSID_IS_INT(priorKeyId) ||
                        JSID_IS_SYMBOL(priorKeyId));
-            TraceEdge(trc, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
+            TraceEdge(marker, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
 
             if (entry.value().closure && !IsMarked(&entry.value().closure)) {
-                TraceEdge(trc, &entry.value().closure, "Watchpoint::closure");
+                TraceEdge(marker, &entry.value().closure, "Watchpoint::closure");
                 marked = true;
             }
 
             /* We will sweep this entry in sweepAll if !objectIsLive. */
             if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
                 e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
         }
     }
     return marked;
 }
 
 void
-WatchpointMap::markAll(JSTracer* trc)
+WatchpointMap::trace(JSTracer* trc)
 {
     for (Map::Enum e(map); !e.empty(); e.popFront()) {
         Map::Entry& entry = e.front();
         JSObject* object = entry.key().object;
         jsid id = entry.key().id;
         JSObject* priorObject = object;
         jsid priorId = id;
         MOZ_ASSERT(JSID_IS_STRING(priorId) || JSID_IS_INT(priorId) || JSID_IS_SYMBOL(priorId));
@@ -194,17 +194,17 @@ WatchpointMap::markAll(JSTracer* trc)
         TraceManuallyBarrieredEdge(trc, &id, "WatchKey::id");
         TraceEdge(trc, &entry.value().closure, "Watchpoint::closure");
 
         if (priorObject != object || priorId != id)
             e.rekeyFront(WatchKey(object, id));
     }
 }
 
-void
+/* static */ void
 WatchpointMap::sweepAll(JSRuntime* rt)
 {
     for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         if (WatchpointMap* wpmap = c->watchpointMap)
             wpmap->sweep();
     }
 }
 
--- a/js/src/jswatchpoint.h
+++ b/js/src/jswatchpoint.h
@@ -9,16 +9,17 @@
 
 #include "jsalloc.h"
 
 #include "gc/Barrier.h"
 #include "js/HashTable.h"
 
 namespace js {
 
+class GCMarker;
 struct WeakMapTracer;
 
 struct WatchKey {
     WatchKey() {}
     WatchKey(JSObject* obj, jsid id) : object(obj), id(id) {}
     WatchKey(const WatchKey& key) : object(key.object.get()), id(key.id.get()) {}
 
     // These are traced unconditionally during minor GC, so do not require
@@ -68,18 +69,19 @@ class WatchpointMap {
                JSWatchPointHandler handler, HandleObject closure);
     void unwatch(JSObject* obj, jsid id,
                  JSWatchPointHandler* handlerp, JSObject** closurep);
     void unwatchObject(JSObject* obj);
     void clear();
 
     bool triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
 
-    bool markIteratively(JSTracer* trc);
-    void markAll(JSTracer* trc);
+    bool markIteratively(GCMarker* marker);
+    void trace(JSTracer* trc);
+
     static void sweepAll(JSRuntime* rt);
     void sweep();
 
     static void traceAll(WeakMapTracer* trc);
     void trace(WeakMapTracer* trc);
 
   private:
     Map map;
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -38,31 +38,31 @@ WeakMapBase::~WeakMapBase()
 void
 WeakMapBase::unmarkZone(JS::Zone* zone)
 {
     for (WeakMapBase* m : zone->gcWeakMapList)
         m->marked = false;
 }
 
 void
-WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
+WeakMapBase::traceZone(JS::Zone* zone, JSTracer* tracer)
 {
     MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
     for (WeakMapBase* m : zone->gcWeakMapList) {
         m->trace(tracer);
         TraceNullableEdge(tracer, &m->memberOf, "memberOf");
     }
 }
 
 bool
-WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
+WeakMapBase::markZoneIteratively(JS::Zone* zone, GCMarker* marker)
 {
     bool markedAny = false;
     for (WeakMapBase* m : zone->gcWeakMapList) {
-        if (m->marked && m->traceEntries(tracer))
+        if (m->marked && m->markIteratively(marker))
             markedAny = true;
     }
     return markedAny;
 }
 
 bool
 WeakMapBase::findInterZoneEdges(JS::Zone* zone)
 {
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -15,16 +15,17 @@
 #include "jsobj.h"
 
 #include "gc/Marking.h"
 #include "gc/StoreBuffer.h"
 #include "js/HashTable.h"
 
 namespace js {
 
+class GCMarker;
 class WeakMapBase;
 
 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
 // a key is collected, the table entry disappears, dropping its reference to the value.
 //
 // More precisely:
 //
 //     A WeakMap entry is live if and only if both the WeakMap and the entry's key
@@ -49,23 +50,23 @@ class WeakMapBase : public mozilla::Link
     virtual ~WeakMapBase();
 
     // Garbage collector entry points.
 
     // Unmark all weak maps in a zone.
     static void unmarkZone(JS::Zone* zone);
 
     // Mark all the weakmaps in a zone.
-    static void markAll(JS::Zone* zone, JSTracer* tracer);
+    static void traceZone(JS::Zone* zone, JSTracer* tracer);
 
     // Check all weak maps in a zone that have been marked as live in this garbage
     // collection, and mark the values of all entries that have become strong references
     // to them. Return true if we marked any new values, indicating that we need to make
     // another pass. In other words, mark my marked maps' marked members' mid-collection.
-    static bool markZoneIteratively(JS::Zone* zone, JSTracer* tracer);
+    static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);
 
     // Add zone edges for weakmaps with key delegates in a different zone.
     static bool findInterZoneEdges(JS::Zone* zone);
 
     // Sweep the weak maps in a zone, removing dead weak maps and removing
     // entries of live weak maps whose keys are dead.
     static void sweepZone(JS::Zone* zone);
 
@@ -84,19 +85,19 @@ class WeakMapBase : public mozilla::Link
     virtual void trace(JSTracer* tracer) = 0;
     virtual bool findZoneEdges() = 0;
     virtual void sweep() = 0;
     virtual void traceMappings(WeakMapTracer* tracer) = 0;
     virtual void finish() = 0;
 
     // Any weakmap key types that want to participate in the non-iterative
     // ephemeron marking must override this method.
-    virtual void traceEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
+    virtual void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
 
-    virtual bool traceEntries(JSTracer* trc) = 0;
+    virtual bool markIteratively(GCMarker* marker) = 0;
 
   protected:
     // Object that this weak map is part of, if any.
     GCPtrObject memberOf;
 
     // Zone containing this weak map.
     JS::Zone* zone;
 
@@ -170,115 +171,110 @@ class WeakMap : public HashMap<Key, Valu
 
     // Trace a WeakMap entry based on 'markedCell' getting marked, where
     // 'origKey' is the key in the weakmap. These will probably be the same,
     // but can be different eg when markedCell is a delegate for origKey.
     //
     // This implementation does not use 'markedCell'; it looks up origKey and
     // checks the mark bits on everything it cares about, one of which will be
     // markedCell. But a subclass might use it to optimize the liveness check.
-    void traceEntry(JSTracer* trc, gc::Cell* markedCell, JS::GCCellPtr origKey) override
+    void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr origKey) override
     {
         MOZ_ASSERT(marked);
 
         // If this cast fails, then you're instantiating the WeakMap with a
         // Lookup that can't be constructed from a Cell*. The WeakKeyTable
         // mechanism is indexed with a GCCellPtr, so that won't work.
         Ptr p = Base::lookup(static_cast<Lookup>(origKey.asCell()));
         MOZ_ASSERT(p.found());
 
         Key key(p->key());
         MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
         if (gc::IsMarked(&key)) {
-            TraceEdge(trc, &p->value(), "ephemeron value");
+            TraceEdge(marker, &p->value(), "ephemeron value");
         } else if (keyNeedsMark(key)) {
-            TraceEdge(trc, &p->value(), "WeakMap ephemeron value");
-            TraceEdge(trc, &key, "proxy-preserved WeakMap ephemeron key");
+            TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
+            TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
             MOZ_ASSERT(key == p->key()); // No moving
         }
         key.unsafeSet(nullptr); // Prevent destructor from running barriers.
     }
 
     void trace(JSTracer* trc) override {
         MOZ_ASSERT(isInList());
 
-        if (trc->isMarkingTracer())
+        if (trc->isMarkingTracer()) {
+            MOZ_ASSERT(trc->weakMapAction() == ExpandWeakMaps);
             marked = true;
+            (void) markIteratively(GCMarker::fromTracer(trc));
+            return;
+        }
 
         if (trc->weakMapAction() == DoNotTraceWeakMaps)
             return;
 
-        if (!trc->isMarkingTracer()) {
-            // Trace keys only if weakMapAction() says to.
-            if (trc->weakMapAction() == TraceWeakMapKeysValues) {
-                for (Enum e(*this); !e.empty(); e.popFront())
-                    TraceEdge(trc, &e.front().mutableKey(), "WeakMap entry key");
-            }
-
-            // Always trace all values (unless weakMapAction() is
-            // DoNotTraceWeakMaps).
-            for (Range r = Base::all(); !r.empty(); r.popFront())
-                TraceEdge(trc, &r.front().value(), "WeakMap entry value");
-
-            return;
+        // Trace keys only if weakMapAction() says to.
+        if (trc->weakMapAction() == TraceWeakMapKeysValues) {
+            for (Enum e(*this); !e.empty(); e.popFront())
+                TraceEdge(trc, &e.front().mutableKey(), "WeakMap entry key");
         }
 
-        // Marking tracer
-        MOZ_ASSERT(trc->weakMapAction() == ExpandWeakMaps);
-        (void) traceEntries(trc);
+        // Always trace all values (unless weakMapAction() is
+        // DoNotTraceWeakMaps).
+        for (Range r = Base::all(); !r.empty(); r.popFront())
+            TraceEdge(trc, &r.front().value(), "WeakMap entry value");
     }
 
   protected:
-    static void addWeakEntry(JSTracer* trc, JS::GCCellPtr key, gc::WeakMarkable markable)
+    static void addWeakEntry(GCMarker* marker, JS::GCCellPtr key, gc::WeakMarkable markable)
     {
-        GCMarker& marker = *static_cast<GCMarker*>(trc);
         Zone* zone = key.asCell()->asTenured().zone();
 
         auto p = zone->gcWeakKeys.get(key);
         if (p) {
             gc::WeakEntryVector& weakEntries = p->value;
             if (!weakEntries.append(Move(markable)))
-                marker.abortLinearWeakMarking();
+                marker->abortLinearWeakMarking();
         } else {
             gc::WeakEntryVector weakEntries;
             MOZ_ALWAYS_TRUE(weakEntries.append(Move(markable)));
             if (!zone->gcWeakKeys.put(JS::GCCellPtr(key), Move(weakEntries)))
-                marker.abortLinearWeakMarking();
+                marker->abortLinearWeakMarking();
         }
     }
 
-    bool traceEntries(JSTracer* trc) override {
+    bool markIteratively(GCMarker* marker) override {
         MOZ_ASSERT(marked);
 
         bool markedAny = false;
 
         for (Enum e(*this); !e.empty(); e.popFront()) {
             // If the entry is live, ensure its key and value are marked.
             bool keyIsMarked = gc::IsMarked(&e.front().mutableKey());
             if (!keyIsMarked && keyNeedsMark(e.front().key())) {
-                TraceEdge(trc, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
+                TraceEdge(marker, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
                 keyIsMarked = true;
                 markedAny = true;
             }
 
             if (keyIsMarked) {
                 if (!gc::IsMarked(&e.front().value())) {
-                    TraceEdge(trc, &e.front().value(), "WeakMap entry value");
+                    TraceEdge(marker, &e.front().value(), "WeakMap entry value");
                     markedAny = true;
                 }
-            } else if (trc->isWeakMarkingTracer()) {
+            } else if (marker->isWeakMarkingTracer()) {
                 // Entry is not yet known to be live. Record this weakmap and
                 // the lookup key in the list of weak keys. Also record the
                 // delegate, if any, because marking the delegate also marks
                 // the entry.
                 JS::GCCellPtr weakKey(extractUnbarriered(e.front().key()));
                 gc::WeakMarkable markable(this, weakKey);
-                addWeakEntry(trc, weakKey, markable);
+                addWeakEntry(marker, weakKey, markable);
                 if (JSObject* delegate = getDelegate(e.front().key()))
-                    addWeakEntry(trc, JS::GCCellPtr(delegate), markable);
+                    addWeakEntry(marker, JS::GCCellPtr(delegate), markable);
             }
         }
 
         return markedAny;
     }
 
     JSObject* getDelegate(JSObject* key) const {
         JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -129,16 +129,17 @@ EXPORTS.js += [
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Proxy.h',
     '../public/Realm.h',
     '../public/RequiredDefines.h',
+    '../public/Result.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/SweepingAPI.h',
     '../public/TraceKind.h',
     '../public/TracingAPI.h',
     '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -223,19 +223,18 @@ ArgumentsObject::createTemplateObject(JS
         return nullptr;
 
     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
                                                       FINALIZE_KIND, BaseShape::INDEXED));
     if (!shape)
         return nullptr;
 
     AutoSetNewObjectMetadata metadata(cx);
-    JSObject* base = JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group);
-    if (!base)
-        return nullptr;
+    JSObject* base;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, base, JSObject::create(cx, FINALIZE_KIND, gc::TenuredHeap, shape, group));
 
     ArgumentsObject* obj = &base->as<js::ArgumentsObject>();
     obj->initFixedSlot(ArgumentsObject::DATA_SLOT, PrivateValue(nullptr));
     return obj;
 }
 
 ArgumentsObject*
 JSCompartment::maybeArgumentsTemplateObject(bool mapped) const
@@ -279,19 +278,18 @@ ArgumentsObject::create(JSContext* cx, H
 
     Rooted<ArgumentsObject*> obj(cx);
     ArgumentsData* data = nullptr;
     {
         // The copyArgs call below can allocate objects, so add this block scope
         // to make sure we set the metadata for this arguments object first.
         AutoSetNewObjectMetadata metadata(cx);
 
-        JSObject* base = JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group);
-        if (!base)
-            return nullptr;
+        JSObject* base;
+        JS_TRY_VAR_OR_RETURN_NULL(cx, base, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, group));
         obj = &base->as<ArgumentsObject>();
 
         data =
             reinterpret_cast<ArgumentsData*>(AllocateObjectBuffer<uint8_t>(cx, obj, numBytes));
         if (!data) {
             // Make the object safe for GC.
             obj->initFixedSlot(DATA_SLOT, PrivateValue(nullptr));
             return nullptr;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -61,16 +61,17 @@ using mozilla::Some;
 using mozilla::Nothing;
 using mozilla::Variant;
 using mozilla::AsVariant;
 
 
 /*** Forward declarations, ClassOps and Classes **************************************************/
 
 static void DebuggerFrame_finalize(FreeOp* fop, JSObject* obj);
+static void DebuggerFrame_trace(JSTracer* trc, JSObject* obj);
 static void DebuggerEnv_trace(JSTracer* trc, JSObject* obj);
 static void DebuggerObject_trace(JSTracer* trc, JSObject* obj);
 static void DebuggerScript_trace(JSTracer* trc, JSObject* obj);
 static void DebuggerSource_trace(JSTracer* trc, JSObject* obj);
 
 enum {
     JSSLOT_DEBUGFRAME_OWNER,
     JSSLOT_DEBUGFRAME_ARGUMENTS,
@@ -86,17 +87,17 @@ const ClassOps DebuggerFrame::classOps_ 
     nullptr,    /* setProperty */
     nullptr,    /* enumerate   */
     nullptr,    /* resolve     */
     nullptr,    /* mayResolve  */
     DebuggerFrame_finalize,
     nullptr,    /* call        */
     nullptr,    /* hasInstance */
     nullptr,    /* construct   */
-    nullptr,    /* trace   */
+    DebuggerFrame_trace
 };
 
 const Class DebuggerFrame::class_ = {
     "Frame",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT) |
     JSCLASS_BACKGROUND_FINALIZE,
     &DebuggerFrame::classOps_
@@ -2058,32 +2059,32 @@ Debugger::onSingleStep(JSContext* cx, Mu
         }
         MOZ_ASSERT(stepperCount == trappingScript->stepModeCount());
     }
 #endif
 
     // Call onStep for frames that have the handler set.
     for (size_t i = 0; i < frames.length(); i++) {
         HandleDebuggerFrame frame = frames[i];
-        if (frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined())
+        OnStepHandler* handler = frame->onStepHandler();
+        if (!handler)
             continue;
 
         Debugger* dbg = Debugger::fromChildJSObject(frame);
         EnterDebuggeeNoExecute nx(cx, *dbg);
 
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, dbg->object);
 
-        RootedValue fval(cx, frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER));
-        RootedValue rval(cx);
-        bool ok = js::Call(cx, fval, frame, &rval);
-        JSTrapStatus st = dbg->processHandlerResult(ac, ok, rval, iter.abstractFramePtr(),
-                                                    iter.pc(), vp);
-        if (st != JSTRAP_CONTINUE)
-            return st;
+        JSTrapStatus status = JSTRAP_CONTINUE;
+        bool success = handler->onStep(cx, frame, status, vp);
+        status = dbg->processParsedHandlerResult(ac, iter.abstractFramePtr(), iter.pc(), success,
+                                                 status, vp);
+        if (status != JSTRAP_CONTINUE)
+            return status;
     }
 
     vp.setUndefined();
     if (exceptionPending)
         cx->setPendingException(exception);
     return JSTRAP_CONTINUE;
 }
 
@@ -2903,24 +2904,24 @@ Debugger::removeAllocationsTrackingForAl
     allocationsLog.clear();
 }
 
 
 
 /*** Debugger JSObjects **************************************************************************/
 
 void
-Debugger::markCrossCompartmentEdges(JSTracer* trc)
-{
-    objects.markCrossCompartmentEdges<DebuggerObject_trace>(trc);
-    environments.markCrossCompartmentEdges<DebuggerEnv_trace>(trc);
-    scripts.markCrossCompartmentEdges<DebuggerScript_trace>(trc);
-    sources.markCrossCompartmentEdges<DebuggerSource_trace>(trc);
-    wasmInstanceScripts.markCrossCompartmentEdges<DebuggerScript_trace>(trc);
-    wasmInstanceSources.markCrossCompartmentEdges<DebuggerSource_trace>(trc);
+Debugger::traceCrossCompartmentEdges(JSTracer* trc)
+{
+    objects.traceCrossCompartmentEdges<DebuggerObject_trace>(trc);
+    environments.traceCrossCompartmentEdges<DebuggerEnv_trace>(trc);
+    scripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
+    sources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
+    wasmInstanceScripts.traceCrossCompartmentEdges<DebuggerScript_trace>(trc);
+    wasmInstanceSources.traceCrossCompartmentEdges<DebuggerSource_trace>(trc);
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
  * However, during zone GC, we have to do something about cross-compartment
@@ -2938,52 +2939,52 @@ Debugger::markCrossCompartmentEdges(JSTr
  *
  * This happens during the initial mark phase, not iterative marking, because
  * all the edges being reported here are strong references.
  *
  * This method is also used during compacting GC to update cross compartment
  * pointers in zones that are not currently being compacted.
  */
 /* static */ void
-Debugger::markIncomingCrossCompartmentEdges(JSTracer* trc)
+Debugger::traceIncomingCrossCompartmentEdges(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     gc::State state = rt->gc.state();
     MOZ_ASSERT(state == gc::State::MarkRoots || state == gc::State::Compact);
 
     for (Debugger* dbg : rt->debuggerList) {
         Zone* zone = MaybeForwarded(dbg->object.get())->zone();
         if ((state == gc::State::MarkRoots && !zone->isCollecting()) ||
             (state == gc::State::Compact && !zone->isGCCompacting()))
         {
-            dbg->markCrossCompartmentEdges(trc);
+            dbg->traceCrossCompartmentEdges(trc);
         }
     }
 }
 
 /*
  * This method has two tasks:
  *   1. Mark Debugger objects that are unreachable except for debugger hooks that
  *      may yet be called.
  *   2. Mark breakpoint handlers.
  *
  * This happens during the iterative part of the GC mark phase. This method
  * returns true if it has to mark anything; GC calls it repeatedly until it
  * returns false.
  */
 /* static */ bool
-Debugger::markAllIteratively(GCMarker* trc)
+Debugger::markIteratively(GCMarker* marker)
 {
     bool markedAny = false;
 
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
-    JSRuntime* rt = trc->runtime();
+    JSRuntime* rt = marker->runtime();
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (c->isDebuggee()) {
             GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
             if (!IsMarkedUnbarriered(&global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
@@ -3005,49 +3006,49 @@ Debugger::markAllIteratively(GCMarker* t
                     continue;
 
                 bool dbgMarked = IsMarked(&dbgobj);
                 if (!dbgMarked && dbg->hasAnyLiveHooks()) {
                     /*
                      * obj could be reachable only via its live, enabled
                      * debugger hooks, which may yet be called.
                      */
-                    TraceEdge(trc, &dbgobj, "enabled Debugger");
+                    TraceEdge(marker, &dbgobj, "enabled Debugger");
                     markedAny = true;
                     dbgMarked = true;
                 }
 
                 if (dbgMarked) {
                     /* Search for breakpoints to mark. */
                     for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) {
                         if (IsMarkedUnbarriered(&bp->site->script)) {
                             /*
                              * The debugger and the script are both live.
                              * Therefore the breakpoint handler is live.
                              */
                             if (!IsMarked(&bp->getHandlerRef())) {
-                                TraceEdge(trc, &bp->getHandlerRef(), "breakpoint handler");
+                                TraceEdge(marker, &bp->getHandlerRef(), "breakpoint handler");
                                 markedAny = true;
                             }
                         }
                     }
                 }
             }
         }
     }
     return markedAny;
 }
 
 /*
- * Mark all debugger-owned GC things unconditionally. This is used by the minor
+ * Trace all debugger-owned GC things unconditionally. This is used by the minor
  * GC: the minor GC cannot apply the weak constraints of the full GC because it
  * visits only part of the heap.
  */
 /* static */ void
-Debugger::markAll(JSTracer* trc)
+Debugger::traceAll(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     for (Debugger* dbg : rt->debuggerList) {
         for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
             TraceManuallyBarrieredEdge(trc, e.mutableFront().unsafeGet(), "Global Object");
 
         GCPtrNativeObject& dbgobj = dbg->toJSObjectRef();
         TraceEdge(trc, &dbgobj, "Debugger Object");
@@ -7214,16 +7215,53 @@ static const JSPropertySpec DebuggerSour
 
 static const JSFunctionSpec DebuggerSource_methods[] = {
     JS_FS_END
 };
 
 
 /*** Debugger.Frame ******************************************************************************/
 
+ScriptedOnStepHandler::ScriptedOnStepHandler(JSObject* object)
+  : object_(object)
+{
+    MOZ_ASSERT(object_->isCallable());
+}
+
+JSObject*
+ScriptedOnStepHandler::object() const
+{
+    return object_;
+}
+
+void
+ScriptedOnStepHandler::drop()
+{
+    this->~ScriptedOnStepHandler();
+    js_free(this);
+}
+
+void
+ScriptedOnStepHandler::trace(JSTracer* tracer)
+{
+    TraceEdge(tracer, &object_, "OnStepHandlerFunction.object");
+}
+
+bool
+ScriptedOnStepHandler::onStep(JSContext* cx, HandleDebuggerFrame frame, JSTrapStatus& statusp,
+                              MutableHandleValue vp)
+{
+    RootedValue fval(cx, ObjectValue(*object_));
+    RootedValue rval(cx);
+    if (!js::Call(cx, fval, frame, &rval))
+        return false;
+
+    return ParseResumptionValue(cx, rval, statusp, vp);
+};
+
 /* static */ NativeObject*
 DebuggerFrame::initClass(JSContext* cx, HandleObject dbgCtor, HandleObject obj)
 {
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
 
     return InitClass(cx, dbgCtor, objProto, &class_, construct, 0, properties_,
                      methods_, nullptr, nullptr);
@@ -7449,16 +7487,55 @@ DebuggerFrame::getImplementation(HandleD
     if (referent.isBaselineFrame())
         return DebuggerFrameImplementation::Baseline;
     else if (referent.isRematerializedFrame())
         return DebuggerFrameImplementation::Ion;
     return DebuggerFrameImplementation::Interpreter;
 }
 
 /*
+ * If succesful, transfers the ownership of the given `handler` to this
+ * Debugger.Frame. Note that on failure, the ownership of `handler` is not
+ * transferred, and the caller is responsible for cleaning it up.
+ */
+/* static */ bool
+DebuggerFrame::setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame, OnStepHandler* handler)
+{
+    MOZ_ASSERT(frame->isLive());
+
+    AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
+
+    OnStepHandler* prior = frame->onStepHandler();
+    if (prior && handler != prior) {
+        prior->drop();
+    }
+
+    if (handler && !prior) {
+        // Single stepping toggled off->on.
+        AutoCompartment ac(cx, referent.environmentChain());
+        // Ensure observability *before* incrementing the step mode
+        // count. Calling this function after calling incrementStepModeCount
+        // will make it a no-op.
+        Debugger* dbg = frame->owner();
+        if (!dbg->ensureExecutionObservabilityOfScript(cx, referent.script()))
+            return false;
+        if (!referent.script()->incrementStepModeCount(cx))
+            return false;
+    } else if (!handler && prior) {
+        // Single stepping toggled on->off.
+        referent.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());
+    }
+
+    /* Now that the step mode switch has succeeded, we can install the handler. */
+    frame->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
+                           handler ? PrivateValue(handler) : UndefinedValue());
+    return true;
+}
+
+/*
  * Evaluate |chars[0..length-1]| in the environment |env|, treating that
  * source as appearing starting at |lineno| in |filename|. Store the return
  * value in |*rval|. Use |thisv| as the 'this' value.
  *
  * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
  * must be either |frame|'s DebugScopeObject, or some extension of that
  * environment; either way, |frame|'s scope is where newly declared variables
  * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
@@ -7622,16 +7699,23 @@ DebuggerFrame::eval(JSContext* cx, Handl
 }
 
 /* statuc */ bool
 DebuggerFrame::isLive() const
 {
     return !!getPrivate();
 }
 
+OnStepHandler*
+DebuggerFrame::onStepHandler() const
+{
+    Value value = getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
+    return value.isUndefined() ? nullptr : static_cast<OnStepHandler*>(value.toPrivate());
+}
+
 static bool
 DebuggerFrame_requireLive(JSContext* cx, HandleDebuggerFrame frame)
 {
     if (!frame->isLive()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE,
                                   "Debugger.Frame");
         return false;
     }
@@ -7688,16 +7772,27 @@ DebuggerFrame_maybeDecrementFrameScriptS
         frame.script()->decrementStepModeCount(fop);
 }
 
 static void
 DebuggerFrame_finalize(FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(fop->maybeOffMainThread());
     DebuggerFrame_freeScriptFrameIterData(fop, obj);
+    OnStepHandler* handler = obj->as<DebuggerFrame>().onStepHandler();
+    if (handler)
+       handler->drop();
+}
+
+static void
+DebuggerFrame_trace(JSTracer* trc, JSObject* obj)
+{
+    OnStepHandler* handler = obj->as<DebuggerFrame>().onStepHandler();
+    if (handler)
+        handler->trace(trc);
 }
 
 static DebuggerFrame*
 DebuggerFrame_checkThis(JSContext* cx, const CallArgs& args, const char* fnname, bool checkLive)
 {
     JSObject* thisobj = NonNullObject(cx, args.thisv());
     if (!thisobj)
         return nullptr;
@@ -8101,57 +8196,51 @@ DebuggerFrame::liveGetter(JSContext* cx,
 }
 
 static bool
 IsValidHook(const Value& v)
 {
     return v.isUndefined() || (v.isObject() && v.toObject().isCallable());
 }
 
-static bool
-DebuggerFrame_getOnStep(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_FRAME(cx, argc, vp, "get onStep", args, thisobj, frame);
-    (void) frame;  // Silence GCC warning
-    RootedValue handler(cx, thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER));
-    MOZ_ASSERT(IsValidHook(handler));
-    args.rval().set(handler);
-    return true;
-}
-
-static bool
-DebuggerFrame_setOnStep(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_FRAME(cx, argc, vp, "set onStep", args, thisobj, frame);
+/* static */ bool
+DebuggerFrame::onStepGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGGER_FRAME(cx, argc, vp, "get onStep", args, frame);
+
+    OnStepHandler* handler = frame->onStepHandler();
+    RootedValue value(cx, handler ? ObjectOrNullValue(handler->object()) : UndefinedValue());
+    MOZ_ASSERT(IsValidHook(value));
+    args.rval().set(value);
+    return true;
+}
+
+/* static */ bool
+DebuggerFrame::onStepSetter(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGGER_FRAME(cx, argc, vp, "set onStep", args, frame);
     if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1))
         return false;
     if (!IsValidHook(args[0])) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
         return false;
     }
 
-    Value prior = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
-    if (!args[0].isUndefined() && prior.isUndefined()) {
-        // Single stepping toggled off->on.
-        AutoCompartment ac(cx, frame.environmentChain());
-        // Ensure observability *before* incrementing the step mode
-        // count. Calling this function after calling incrementStepModeCount
-        // will make it a no-op.
-        Debugger* dbg = Debugger::fromChildJSObject(thisobj);
-        if (!dbg->ensureExecutionObservabilityOfScript(cx, frame.script()))
-            return false;
-        if (!frame.script()->incrementStepModeCount(cx))
-            return false;
-    } else if (args[0].isUndefined() && !prior.isUndefined()) {
-        // Single stepping toggled on->off.
-        frame.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp());
-    }
-
-    /* Now that the step mode switch has succeeded, we can install the handler. */
-    thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER, args[0]);
+    ScriptedOnStepHandler* handler = nullptr;
+    if (!args[0].isUndefined()) {
+        handler = cx->new_<ScriptedOnStepHandler>(&args[0].toObject());
+        if (!handler)
+            return false;
+    }
+
+    if (!DebuggerFrame::setOnStepHandler(cx, frame, handler)) {
+        handler->drop();
+        return false;
+    }
+
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 DebuggerFrame_getOnPop(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_FRAME(cx, argc, vp, "get onPop", args, thisobj, frame);
@@ -8250,17 +8339,17 @@ const JSPropertySpec DebuggerFrame::prop
     JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
     JS_PSG("live", DebuggerFrame::liveGetter, 0),
     JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
     JS_PSG("older", DebuggerFrame::olderGetter, 0),
     JS_PSG("script", DebuggerFrame_getScript, 0),
     JS_PSG("this", DebuggerFrame::thisGetter, 0),
     JS_PSG("type", DebuggerFrame::typeGetter, 0),
     JS_PSG("implementation", DebuggerFrame::implementationGetter, 0),
-    JS_PSGS("onStep", DebuggerFrame_getOnStep, DebuggerFrame_setOnStep, 0),
+    JS_PSGS("onStep", DebuggerFrame::onStepGetter, DebuggerFrame::onStepSetter, 0),
     JS_PSGS("onPop", DebuggerFrame_getOnPop, DebuggerFrame_setOnPop, 0),
     JS_PS_END
 };
 
 const JSFunctionSpec DebuggerFrame::methods_[] = {
     JS_FN("eval", DebuggerFrame::evalMethod, 1, 0),
     JS_FN("evalWithBindings", DebuggerFrame::evalWithBindingsMethod, 1, 0),
     JS_FS_END
@@ -9927,18 +10016,17 @@ DebuggerObject::defineProperty(JSContext
                                Handle<PropertyDescriptor> desc_)
 {
     RootedObject referent(cx, object->referent());
     Debugger* dbg = object->owner();
 
     Rooted<PropertyDescriptor> desc(cx, desc_);
     if (!dbg->unwrapPropertyDescriptor(cx, referent, &desc))
         return false;
-    if (!CheckPropertyDescriptorAccessors(cx, desc))
-        return false;
+    JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, desc));
 
     Maybe<AutoCompartment> ac;
     ac.emplace(cx, referent);
     if (!cx->compartment()->wrap(cx, &desc))
         return false;
 
     ErrorCopier ec(ac);
     if (!DefineProperty(cx, referent, id, desc))
@@ -9956,18 +10044,17 @@ DebuggerObject::defineProperties(JSConte
     Debugger* dbg = object->owner();
 
     Rooted<PropertyDescriptorVector> descs(cx, PropertyDescriptorVector(cx));
     if (!descs.append(descs_.begin(), descs_.end()))
         return false;
     for (size_t i = 0; i < descs.length(); i++) {
         if (!dbg->unwrapPropertyDescriptor(cx, referent, descs[i]))
             return false;
-        if (!CheckPropertyDescriptorAccessors(cx, descs[i]))
-            return false;
+        JS_TRY_OR_RETURN_FALSE(cx, CheckPropertyDescriptorAccessors(cx, descs[i]));
     }
 
     Maybe<AutoCompartment> ac;
     ac.emplace(cx, referent);
     for (size_t i = 0; i < descs.length(); i++) {
         if (!cx->compartment()->wrap(cx, descs[i]))
             return false;
     }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -31,20 +31,22 @@
 enum JSTrapStatus {
     JSTRAP_ERROR,
     JSTRAP_CONTINUE,
     JSTRAP_RETURN,
     JSTRAP_THROW,
     JSTRAP_LIMIT
 };
 
+
 namespace js {
 
 class Breakpoint;
 class DebuggerMemory;
+class ScriptedOnStepHandler;
 class WasmInstanceObject;
 
 typedef HashSet<ReadBarrieredGlobalObject,
                 MovableCellHasher<ReadBarrieredGlobalObject>,
                 RuntimeAllocPolicy> WeakGlobalObjectSet;
 
 /*
  * A weakmap from GC thing keys to JSObject values that supports the keys being
@@ -133,17 +135,17 @@ class DebuggerWeakMap : private WeakMap<
     void remove(const Lookup& l) {
         MOZ_ASSERT(Base::has(l));
         Base::remove(l);
         decZoneCount(l->zone());
     }
 
   public:
     template <void (traceValueEdges)(JSTracer*, JSObject*)>
-    void markCrossCompartmentEdges(JSTracer* tracer) {
+    void traceCrossCompartmentEdges(JSTracer* tracer) {
         for (Enum e(*static_cast<Base*>(this)); !e.empty(); e.popFront()) {
             traceValueEdges(tracer, e.front().value());
             Key key = e.front().key();
             TraceEdge(tracer, &key, "Debugger WeakMap key");
             if (key != e.front().key())
                 e.rekeyFront(key);
             key.unsafeSet(nullptr);
         }
@@ -245,16 +247,17 @@ typedef mozilla::Variant<JSScript*, Wasm
 // Either a ScriptSourceObject, for ordinary JS, or a WasmInstanceObject,
 // denoting the synthesized source of a wasm module.
 typedef mozilla::Variant<ScriptSourceObject*, WasmInstanceObject*> DebuggerSourceReferent;
 
 class Debugger : private mozilla::LinkedListElement<Debugger>
 {
     friend class Breakpoint;
     friend class DebuggerMemory;
+    friend class ScriptedOnStepHandler;
     friend class SavedStacks;
     friend class mozilla::LinkedListElement<Debugger>;
     friend class mozilla::LinkedList<Debugger>;
     friend bool (::JS_DefineDebuggerObject)(JSContext* cx, JS::HandleObject obj);
     friend bool (::JS::dbg::IsDebugger)(JSObject&);
     friend bool (::JS::dbg::GetDebuggeeGlobals)(JSContext*, JSObject&, AutoObjectVector&);
     friend void JS::dbg::onNewPromise(JSContext* cx, HandleObject promise);
     friend void JS::dbg::onPromiseSettled(JSContext* cx, HandleObject promise);
@@ -574,17 +577,17 @@ class Debugger : private mozilla::Linked
                                 const mozilla::Maybe<HandleValue>& maybeThis, HandleValue rval,
                                 JSTrapStatus& statusp, MutableHandleValue vp);
 
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     static void finalize(FreeOp* fop, JSObject* obj);
-    void markCrossCompartmentEdges(JSTracer* tracer);
+    void traceCrossCompartmentEdges(JSTracer* tracer);
 
     static const ClassOps classOps_;
 
   public:
     static const Class class_;
 
   private:
     static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args, Debugger& dbg, Hook which);
@@ -807,23 +810,23 @@ class Debugger : private mozilla::Linked
      *   * it is in the middle of dispatching an event (the event dispatching
      *     code roots it in this case); OR
      *   * it is enabled, and it is debugging at least one live compartment,
      *     and at least one of the following is true:
      *       - it has a debugger hook installed
      *       - it has a breakpoint set on a live script
      *       - it has a watchpoint set on a live object.
      *
-     * Debugger::markAllIteratively handles the last case. If it finds any
-     * Debugger objects that are definitely live but not yet marked, it marks
-     * them and returns true. If not, it returns false.
+     * Debugger::markIteratively handles the last case. If it finds any Debugger
+     * objects that are definitely live but not yet marked, it marks them and
+     * returns true. If not, it returns false.
      */
-    static void markIncomingCrossCompartmentEdges(JSTracer* tracer);
-    static MOZ_MUST_USE bool markAllIteratively(GCMarker* trc);
-    static void markAll(JSTracer* trc);
+    static void traceIncomingCrossCompartmentEdges(JSTracer* tracer);
+    static MOZ_MUST_USE bool markIteratively(GCMarker* marker);
+    static void traceAll(JSTracer* trc);
     static void sweepAll(FreeOp* fop);
     static void detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global);
     static void findZoneEdges(JS::Zone* v, gc::ZoneComponentFinder& finder);
 
     // Checks it the current compartment is allowed to execute code.
     static inline MOZ_MUST_USE bool checkNoExecute(JSContext* cx, HandleScript script);
 
     /*
@@ -1149,18 +1152,85 @@ enum class DebuggerFrameType {
 };
 
 enum class DebuggerFrameImplementation {
     Interpreter,
     Baseline,
     Ion
 };
 
+/*
+ * A Handler represents a reference to a handler function. These handler
+ * functions are called by the Debugger API to notify the user of certain
+ * events. For each event type, we define a separate subclass of Handler. This
+ * allows users to define a single reference to an object that implements
+ * multiple handlers, by inheriting from the appropriate subclasses.
+ *
+ * A Handler can be stored on a reflection object, in which case the reflection
+ * object becomes responsible for managing the lifetime of the Handler. To aid
+ * with this, the Handler base class defines several methods, which are to be
+ * called by the reflection object at the appropriate time (see below).
+ */
+struct Handler {
+    virtual ~Handler() {}
+
+    /*
+     * If the Handler is a reference to a callable JSObject, this method returns
+     * the latter. This allows the Handler to be used from JS. Otherwise, this
+     * method returns nullptr.
+     */
+    virtual JSObject* object() const = 0;
+
+    /*
+     * Drops the reference to the handler. This method will be called by the
+     * reflection object on which the reference is stored when the former is
+     * finalized, or the latter replaced.
+     */
+    virtual void drop() = 0;
+
+    /*
+     * Traces the reference to the handler. This method will be called
+     * by the reflection object on which the reference is stored whenever the
+     * former is traced.
+     */
+    virtual void trace(JSTracer* tracer) = 0;
+};
+
+/*
+ * An OnStepHandler represents a handler function that is called when a small
+ * amount of progress is made in a frame.
+ */
+struct OnStepHandler : Handler {
+    /*
+     * If we have made a small amount of progress in a frame, this method is
+     * called with the frame as argument. If succesful, this method should
+     * return true, with `statusp` and `vp` set to a resumption value
+     * specifiying how execution should continue.
+     */
+    virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame, JSTrapStatus& statusp,
+                        MutableHandleValue vp) = 0;
+};
+
+class ScriptedOnStepHandler final : public OnStepHandler {
+  public:
+    explicit ScriptedOnStepHandler(JSObject* object);
+    virtual JSObject* object() const override;
+    virtual void drop() override;
+    virtual void trace(JSTracer* tracer) override;
+    virtual bool onStep(JSContext* cx, HandleDebuggerFrame frame, JSTrapStatus& statusp,
+                        MutableHandleValue vp) override;
+
+  private:
+    HeapPtr<JSObject*> object_;
+};
+
 class DebuggerFrame : public NativeObject
 {
+    friend class ScriptedOnStepHandler;
+
   public:
     enum {
         OWNER_SLOT
     };
 
     static const unsigned RESERVED_SLOTS = 1;
 
     static const Class class_;
@@ -1178,23 +1248,26 @@ class DebuggerFrame : public NativeObjec
     static bool getIsGenerator(HandleDebuggerFrame frame);
     static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result);
     static MOZ_MUST_USE bool getOlder(JSContext* cx, HandleDebuggerFrame frame,
                                       MutableHandleDebuggerFrame result);
     static MOZ_MUST_USE bool getThis(JSContext* cx, HandleDebuggerFrame frame,
                                      MutableHandleValue result);
     static DebuggerFrameType getType(HandleDebuggerFrame frame);
     static DebuggerFrameImplementation getImplementation(HandleDebuggerFrame frame);
+    static MOZ_MUST_USE bool setOnStepHandler(JSContext* cx, HandleDebuggerFrame frame,
+                                              OnStepHandler* handler);
 
     static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
                                   mozilla::Range<const char16_t> chars, HandleObject bindings,
                                   const EvalOptions& options, JSTrapStatus& status,
                                   MutableHandleValue value);
 
     bool isLive() const;
+    OnStepHandler* onStepHandler() const;
 
   private:
     static const ClassOps classOps_;
 
     static const JSPropertySpec properties_[];
     static const JSFunctionSpec methods_[];
 
     static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
@@ -1208,16 +1281,18 @@ class DebuggerFrame : public NativeObjec
     static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool typeGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool implementationGetter(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool onStepGetter(JSContext* cx, unsigned argc, Value* vp);
+    static MOZ_MUST_USE bool onStepSetter(JSContext* cx, unsigned argc, Value* vp);
 
     static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc, Value* vp);
 
     Debugger* owner() const;
 };
 
 class DebuggerObject : public NativeObject
@@ -1446,17 +1521,17 @@ class BreakpointSite {
  * site's list.
  *
  * GC rules:
  *   - script is live, breakpoint exists, and debugger is enabled
  *      ==> debugger is live
  *   - script is live, breakpoint exists, and debugger is live
  *      ==> retain the breakpoint and the handler object is live
  *
- * Debugger::markAllIteratively implements these two rules. It uses
+ * Debugger::markIteratively implements these two rules. It uses
  * Debugger::hasAnyLiveHooks to check for rule 1.
  *
  * Nothing else causes a breakpoint to be retained, so if its script or
  * debugger is collected, the breakpoint is destroyed during GC sweep phase,
  * even if the debugger compartment isn't being GC'd. This is implemented in
  * Zone::sweepBreakpoints.
  */
 class Breakpoint {
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -137,36 +137,35 @@ CallObject::create(JSContext* cx, Handle
     MOZ_ASSERT(!group->singleton(),
                "passed a singleton group to create() (use createSingleton() "
                "instead)");
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
-    JSObject* obj = JSObject::create(cx, kind, gc::DefaultHeap, shape, group);
-    if (!obj)
-        return nullptr;
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::DefaultHeap, shape, group));
 
     return &obj->as<CallObject>();
 }
 
 CallObject*
 CallObject::createSingleton(JSContext* cx, HandleShape shape)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &CallObject::class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
     RootedObjectGroup group(cx, ObjectGroup::lazySingletonGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
-    RootedObject obj(cx, JSObject::create(cx, kind, gc::TenuredHeap, shape, group));
-    if (!obj)
-        return nullptr;
+
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, gc::TenuredHeap, shape, group));
 
     MOZ_ASSERT(obj->isSingleton(),
                "group created inline above must be a singleton");
 
     return &obj->as<CallObject>();
 }
 
 /*
@@ -185,19 +184,18 @@ CallObject::createTemplateObject(JSConte
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
-    JSObject* obj = JSObject::create(cx, kind, heap, shape, group);
-    if (!obj)
-        return nullptr;
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
 
     CallObject* callObj = &obj->as<CallObject>();
     callObj->initEnclosingEnvironment(enclosing);
 
     if (scope->hasParameterExprs()) {
         // If there are parameter expressions, all parameters are lexical and
         // have TDZ.
         for (BindingIter bi(script->bodyScope()); bi; bi++) {
@@ -317,24 +315,23 @@ VarEnvironmentObject::create(JSContext* 
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
-    NativeObject* obj = MaybeNativeObject(JSObject::create(cx, kind, heap, shape, group));
-    if (!obj)
-        return nullptr;
-
-    MOZ_ASSERT(!obj->inDictionaryMode());
-    MOZ_ASSERT(obj->isDelegate());
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, heap, shape, group));
 
     VarEnvironmentObject* env = &obj->as<VarEnvironmentObject>();
+    MOZ_ASSERT(!env->inDictionaryMode());
+    MOZ_ASSERT(env->isDelegate());
+
     env->initEnclosingEnvironment(enclosing);
 
     return env;
 }
 
 /* static */ VarEnvironmentObject*
 VarEnvironmentObject::create(JSContext* cx, HandleScope scope, AbstractFramePtr frame)
 {
@@ -434,19 +431,18 @@ ModuleEnvironmentObject::create(Exclusiv
     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(kind, &class_));
     kind = gc::GetBackgroundAllocKind(kind);
 
-    JSObject* obj = JSObject::create(cx, kind, TenuredHeap, shape, group);
-    if (!obj)
-        return nullptr;
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, kind, TenuredHeap, shape, group));
 
     RootedModuleEnvironmentObject env(cx, &obj->as<ModuleEnvironmentObject>());
 
     env->initReservedSlot(MODULE_SLOT, ObjectValue(*module));
     if (!JSObject::setSingleton(cx, env))
         return nullptr;
 
     // Initialize this early so that we can manipulate the env object without
@@ -840,25 +836,24 @@ LexicalEnvironmentObject::createTemplate
     RootedObjectGroup group(cx,
         ObjectGroup::defaultNewGroup(cx, &LexicalEnvironmentObject::class_, TaggedProto(nullptr)));
     if (!group)
         return nullptr;
 
     gc::AllocKind allocKind = gc::GetGCObjectKind(shape->numFixedSlots());
     MOZ_ASSERT(CanBeFinalizedInBackground(allocKind, &LexicalEnvironmentObject::class_));
     allocKind = GetBackgroundAllocKind(allocKind);
-    RootedNativeObject obj(cx,
-        MaybeNativeObject(JSObject::create(cx, allocKind, heap, shape, group)));
-    if (!obj)
-        return nullptr;
-
-    MOZ_ASSERT(!obj->inDictionaryMode());
-    MOZ_ASSERT(obj->isDelegate());
+
+    JSObject* obj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, obj, JSObject::create(cx, allocKind, heap, shape, group));
 
     LexicalEnvironmentObject* env = &obj->as<LexicalEnvironmentObject>();
+    MOZ_ASSERT(!env->inDictionaryMode());
+    MOZ_ASSERT(env->isDelegate());
+
     if (enclosing)
         env->initEnclosingEnvironment(enclosing);
 
     return env;
 }
 
 /* static */ LexicalEnvironmentObject*
 LexicalEnvironmentObject::createTemplateObject(JSContext* cx, Handle<LexicalScope*> scope,
@@ -2282,17 +2277,17 @@ DebugEnvironments::~DebugEnvironments()
 
 bool
 DebugEnvironments::init()
 {
     return proxiedEnvs.init() && missingEnvs.init() && liveEnvs.init();
 }
 
 void
-DebugEnvironments::mark(JSTracer* trc)
+DebugEnvironments::trace(JSTracer* trc)
 {
     proxiedEnvs.trace(trc);
 }
 
 void
 DebugEnvironments::sweep(JSRuntime* rt)
 {
     /*
@@ -2811,17 +2806,17 @@ DebugEnvironments::forwardLiveFrame(JSCo
     for (LiveEnvironmentMap::Enum e(envs->liveEnvs); !e.empty(); e.popFront()) {
         LiveEnvironmentVal& val = e.front().value();
         if (val.frame() == from)
             val.updateFrame(to);
     }
 }
 
 /* static */ void
-DebugEnvironments::markLiveFrame(JSTracer* trc, AbstractFramePtr frame)
+DebugEnvironments::traceLiveFrame(JSTracer* trc, AbstractFramePtr frame)
 {
     for (MissingEnvironmentMap::Enum e(missingEnvs); !e.empty(); e.popFront()) {
         if (e.front().key().frame() == frame)
             TraceEdge(trc, &e.front().value(), "debug-env-live-frame-missing-env");
     }
 }
 
 /*****************************************************************************/
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -922,26 +922,26 @@ class DebugEnvironments
     bool init();
 
     static DebugEnvironments* ensureCompartmentData(JSContext* cx);
 
     template <typename Environment, typename Scope>
     static void onPopGeneric(JSContext* cx, const EnvironmentIter& ei);
 
   public:
-    void mark(JSTracer* trc);
+    void trace(JSTracer* trc);
     void sweep(JSRuntime* rt);
     void finish();
 #ifdef JS_GC_ZEAL
     void checkHashTablesAfterMovingGC(JSRuntime* rt);
 #endif
 
     // If a live frame has a synthesized entry in missingEnvs, make sure it's not
     // collected.
-    void markLiveFrame(JSTracer* trc, AbstractFramePtr frame);
+    void traceLiveFrame(JSTracer* trc, AbstractFramePtr frame);
 
     static DebugEnvironmentProxy* hasDebugEnvironment(JSContext* cx, EnvironmentObject& env);
     static bool addDebugEnvironment(JSContext* cx, Handle<EnvironmentObject*> env,
                                     Handle<DebugEnvironmentProxy*> debugEnv);
 
     static DebugEnvironmentProxy* hasDebugEnvironment(JSContext* cx, const EnvironmentIter& ei);
     static bool addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
                                     Handle<DebugEnvironmentProxy*> debugEnv);
--- a/js/src/vm/NativeObject-inl.h
+++ b/js/src/vm/NativeObject-inl.h
@@ -246,19 +246,19 @@ NativeObject::getDenseOrTypedArrayElemen
 /* static */ inline NativeObject*
 NativeObject::copy(ExclusiveContext* cx, gc::AllocKind kind, gc::InitialHeap heap,
                    HandleNativeObject templateObject)
 {
     RootedShape shape(cx, templateObject->lastProperty());
     RootedObjectGroup group(cx, templateObject->group());
     MOZ_ASSERT(!templateObject->denseElementsAreCopyOnWrite());
 
-    JSObject* baseObj = create(cx, kind, heap, shape, group);
-    if (!baseObj)
-        return nullptr;
+    JSObject* baseObj;
+    JS_TRY_VAR_OR_RETURN_NULL(cx, baseObj, create(cx, kind, heap, shape, group));
+
     NativeObject* obj = &baseObj->as<NativeObject>();
 
     size_t span = shape->slotSpan();
     if (span) {
         uint32_t numFixed = templateObject->numFixedSlots();
         const Value* fixed = &templateObject->getSlot(0);
         // Only copy elements which are registered in the shape, even if the
         // number of fixed slots is larger.
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -1118,17 +1118,17 @@ struct ObjectGroupCompartment::PlainObje
         uint32_t nproperties;
 
         Lookup(IdValuePair* properties, uint32_t nproperties)
           : properties(properties), nproperties(nproperties)
         {}
     };
 
     static inline HashNumber hash(const Lookup& lookup) {
-        return (HashNumber) (JSID_BITS(lookup.properties[lookup.nproperties - 1].id) ^
+        return (HashNumber) (HashId(lookup.properties[lookup.nproperties - 1].id) ^
                              lookup.nproperties);
     }
 
     static inline bool match(const PlainObjectKey& v, const Lookup& lookup) {
         if (lookup.nproperties != v.nproperties)
             return false;
         for (size_t i = 0; i < lookup.nproperties; i++) {
             if (lookup.properties[i].id != v.properties[i])
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -237,17 +237,17 @@ js::ForOfPIC::Chain::eraseChain()
         stub = next;
     }
     stubs_ = nullptr;
 }
 
 
 // Trace the pointers stored directly on the stub.
 void
-js::ForOfPIC::Chain::mark(JSTracer* trc)
+js::ForOfPIC::Chain::trace(JSTracer* trc)
 {
     if (!initialized_ || disabled_)
         return;
 
     TraceEdge(trc, &arrayProto_, "ForOfPIC Array.prototype.");
     TraceEdge(trc, &arrayIteratorProto_, "ForOfPIC ArrayIterator.prototype.");
 
     TraceEdge(trc, &arrayProtoShape_, "ForOfPIC Array.prototype shape.");
@@ -280,17 +280,17 @@ ForOfPIC_finalize(FreeOp* fop, JSObject*
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
         chain->sweep(fop);
 }
 
 static void
 ForOfPIC_traceObject(JSTracer* trc, JSObject* obj)
 {
     if (ForOfPIC::Chain* chain = ForOfPIC::fromJSObject(&obj->as<NativeObject>()))
-        chain->mark(trc);
+        chain->trace(trc);
 }
 
 static const ClassOps ForOfPICClassOps = {
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, ForOfPIC_finalize,
     nullptr,              /* call        */
     nullptr,              /* hasInstance */
     nullptr,              /* construct   */
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -230,17 +230,17 @@ struct ForOfPIC
         bool isArrayStateStillSane();
 
         // Check if ArrayIterator.next is still optimizable.
         inline bool isArrayNextStillSane() {
             return (arrayIteratorProto_->lastProperty() == arrayIteratorProtoShape_) &&
                 (arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == canonicalNextFunc_);
         }
 
-        void mark(JSTracer* trc);
+        void trace(JSTracer* trc);
         void sweep(FreeOp* fop);
 
       private:
         // Get a matching optimized stub for the given object.
         Stub* getMatchingStub(JSObject* obj);
 
         // Check if the given object is for-of optimizable with this PIC.
         bool isOptimizableArray(JSObject* obj);
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -27,17 +27,17 @@ resc_finalize(FreeOp* fop, JSObject* obj
     fop->delete_(res);
 }
 
 static void
 resc_trace(JSTracer* trc, JSObject* obj)
 {
     void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
     if (pdata)
-        static_cast<RegExpStatics*>(pdata)->mark(trc);
+        static_cast<RegExpStatics*>(pdata)->trace(trc);
 }
 
 static const ClassOps RegExpStaticsObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -82,17 +82,17 @@ class RegExpStatics
     const MatchPairs& getMatches() const {
         /* Safe: only used by String methods, which do not set lazy mode. */
         MOZ_ASSERT(!pendingLazyEvaluation);
         return matches;
     }
 
     JSString* getPendingInput() const { return pendingInput; }
 
-    void mark(JSTracer* trc) {
+    void trace(JSTracer* trc) {
         /*
          * Changes to this function must also be reflected in
          * RegExpStatics::AutoRooter::trace().
          */
         TraceNullableEdge(trc, &matchesInput, "res->matchesInput");
         TraceNullableEdge(trc, &lazySource, "res->lazySource");
         TraceNullableEdge(trc, &pendingInput, "res->pendingInput");
     }
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -311,16 +311,17 @@ JSRuntime::init(uint32_t maxbytes, uint3
         return false;
 
     if (!gc.zones.append(atomsZone.get()))
         return false;
     if (!atomsZone->compartments.append(atomsCompartment.get()))
         return false;
 
     atomsCompartment->setIsSystem(true);
+    atomsCompartment->setIsAtomsCompartment();
 
     atomsZone.forget();
     this->atomsCompartment_ = atomsCompartment.forget();
 
     if (!symbolRegistry_.init())
         return false;
 
     if (!scriptDataTable_.init())
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -83,16 +83,20 @@ class Simulator;
 }
 #endif
 
 namespace js {
 
 extern MOZ_COLD void
 ReportOutOfMemory(ExclusiveContext* cx);
 
+/* Different signature because the return type has MOZ_MUST_USE_TYPE. */
+extern MOZ_COLD mozilla::GenericErrorResult<OOM&>
+ReportOutOfMemoryResult(ExclusiveContext* cx);
+
 extern MOZ_COLD void
 ReportAllocationOverflow(ExclusiveContext* maybecx);
 
 extern MOZ_COLD void
 ReportOverRecursed(ExclusiveContext* cx);
 
 class Activation;
 class ActivationIterator;
@@ -770,17 +774,17 @@ struct JSRuntime : public JS::shadow::Ru
     //-------------------------------------------------------------------------
 
     bool hasInitializedSelfHosting() const {
         return selfHostingGlobal_;
     }
 
     bool initSelfHosting(JSContext* cx);
     void finishSelfHosting();
-    void markSelfHostingGlobal(JSTracer* trc);
+    void traceSelfHostingGlobal(JSTracer* trc);
     bool isSelfHostingGlobal(JSObject* global) {
         return global == selfHostingGlobal_;
     }
     bool isSelfHostingCompartment(JSCompartment* comp) const;
     bool isSelfHostingZone(const JS::Zone* zone) const;
     bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName,
                                            js::HandleAtom name, unsigned nargs,
                                            js::HandleObject proto,
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2716,17 +2716,17 @@ JSRuntime::initSelfHosting(JSContext* cx
 
 void
 JSRuntime::finishSelfHosting()
 {
     selfHostingGlobal_ = nullptr;
 }
 
 void
-JSRuntime::markSelfHostingGlobal(JSTracer* trc)
+JSRuntime::traceSelfHostingGlobal(JSTracer* trc)
 {
     if (selfHostingGlobal_ && !parentRuntime)
         TraceRoot(trc, &selfHostingGlobal_, "self-hosting global");
 }
 
 bool
 JSRuntime::isSelfHostingCompartment(JSCompartment* comp) const
 {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1318,17 +1318,17 @@ struct StackShape
     }
 
     HashNumber hash() const {
         HashNumber hash = uintptr_t(base);
 
         /* Accumulate from least to most random so the low bits are most random. */
         hash = mozilla::RotateLeft(hash, 4) ^ attrs;
         hash = mozilla::RotateLeft(hash, 4) ^ slot_;
-        hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
+        hash = mozilla::RotateLeft(hash, 4) ^ HashId(propid);
         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawGetter);
         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawSetter);
         return hash;
     }
 
     // Traceable implementation.
     static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
     void trace(JSTracer* trc);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -367,69 +367,69 @@ InterpreterFrame::trace(JSTracer* trc, V
         // need to fix up the callee pointer before we use it below, under
         // numFormalArgs() and script().
         TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
 
         // Trace arguments.
         unsigned argc = Max(numActualArgs(), numFormalArgs());
         TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
     } else {
-        // Mark newTarget.
+        // Trace newTarget.
         TraceRoot(trc, ((Value*)this) - 1, "stack newTarget");
     }
 
     JSScript* script = this->script();
     size_t nfixed = script->nfixed();
     size_t nlivefixed = script->calculateLiveFixed(pc);
 
     if (nfixed == nlivefixed) {
         // All locals are live.
         traceValues(trc, 0, sp - slots());
     } else {
-        // Mark operand stack.
+        // Trace operand stack.
         traceValues(trc, nfixed, sp - slots());
 
         // Clear dead block-scoped locals.
         while (nfixed > nlivefixed)
             unaliasedLocal(--nfixed).setUndefined();
 
-        // Mark live locals.
+        // Trace live locals.
         traceValues(trc, 0, nlivefixed);
     }
 
     if (script->compartment()->debugEnvs)
-        script->compartment()->debugEnvs->markLiveFrame(trc, this);
+        script->compartment()->debugEnvs->traceLiveFrame(trc, this);
 
     if (trc->isMarkingTracer())
         script->compartment()->zone()->active = true;
 }
 
 void
 InterpreterFrame::traceValues(JSTracer* trc, unsigned start, unsigned end)
 {
     if (start < end)
         TraceRootRange(trc, end - start, slots() + start, "vm_stack");
 }
 
 static void
-MarkInterpreterActivation(JSTracer* trc, InterpreterActivation* act)
+TraceInterpreterActivation(JSTracer* trc, InterpreterActivation* act)
 {
     for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
         InterpreterFrame* fp = frames.frame();
         fp->trace(trc, frames.sp(), frames.pc());
     }
 }
 
 void
-js::MarkInterpreterActivations(JSRuntime* rt, JSTracer* trc)
+js::TraceInterpreterActivations(JSRuntime* rt, JSTracer* trc)
 {
     for (ActivationIterator iter(rt); !iter.done(); ++iter) {
         Activation* act = iter.activation();
         if (act->isInterpreter())
-            MarkInterpreterActivation(trc, act->asInterpreter());
+            TraceInterpreterActivation(trc, act->asInterpreter());
     }
 }
 
 /*****************************************************************************/
 
 // Unlike the other methods of this class, this method is defined here so that
 // we don't have to #include jsautooplen.h in vm/Stack.h.
 void
@@ -1569,17 +1569,17 @@ jit::JitActivation::removeRematerialized
         return;
     if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
         for (uint32_t i = 0; i < p->value().length(); i++)
             Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
     }
 }
 
 void
-jit::JitActivation::markRematerializedFrames(JSTracer* trc)
+jit::JitActivation::traceRematerializedFrames(JSTracer* trc)
 {
     if (!rematerializedFrames_)
         return;
     for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
         e.front().value().trace(trc);
 }
 
 bool
@@ -1610,17 +1610,17 @@ jit::JitActivation::removeIonFrameRecove
     RInstructionResults* elem = maybeIonFrameRecovery(fp);
     if (!elem)
         return;
 
     ionRecovery_.erase(elem);
 }
 
 void
-jit::JitActivation::markIonRecovery(JSTracer* trc)
+jit::JitActivation::traceIonRecovery(JSTracer* trc)
 {
     for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); it++)
         it->trace(trc);
 }
 
 WasmActivation::WasmActivation(JSContext* cx)
   : Activation(cx, Wasm),
     entrySP_(nullptr),
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -910,17 +910,17 @@ class InterpreterStack
 
     inline void purge(JSRuntime* rt);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return allocator_.sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-void MarkInterpreterActivations(JSRuntime* rt, JSTracer* trc);
+void TraceInterpreterActivations(JSRuntime* rt, JSTracer* trc);
 
 /*****************************************************************************/
 
 /** Base class for all function call args. */
 class AnyInvokeArgs : public JS::CallArgs
 {
 };
 
@@ -1534,30 +1534,30 @@ class JitActivation : public Activation
 
     bool hasRematerializedFrame(uint8_t* top, size_t inlineDepth = 0) {
         return !!lookupRematerializedFrame(top, inlineDepth);
     }
 
     // Remove a previous rematerialization by fp.
     void removeRematerializedFrame(uint8_t* top);
 
-    void markRematerializedFrames(JSTracer* trc);
+    void traceRematerializedFrames(JSTracer* trc);
 
 
     // Register the results of on Ion frame recovery.
     bool registerIonFrameRecovery(RInstructionResults&& results);
 
     // Return the pointer to the Ion frame recovery, if it is already registered.
     RInstructionResults* maybeIonFrameRecovery(JitFrameLayout* fp);
 
     // If an Ion frame recovery exists for the |fp| frame exists, then remove it
     // from the activation.
     void removeIonFrameRecovery(JitFrameLayout* fp);
 
-    void markIonRecovery(JSTracer* trc);
+    void traceIonRecovery(JSTracer* trc);
 
     // Return the bailout information if it is registered.
     const BailoutFrameInfo* bailoutData() const { return bailoutData_; }
 
     // Register the bailout data when it is constructed.
     void setBailoutData(BailoutFrameInfo* bailoutData);
 
     // Unregister the bailout data when the frame is reconstructed.
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -8,16 +8,17 @@
 #define vm_String_inl_h
 
 #include "vm/String.h"
 
 #include "mozilla/PodOperations.h"
 #include "mozilla/Range.h"
 
 #include "jscntxt.h"
+#include "jscompartment.h"
 
 #include "gc/Allocator.h"
 #include "gc/Marking.h"
 
 namespace js {
 
 // Allocate a thin inline string if possible, and a fat inline string if not.
 template <AllowGC allowGC, typename CharT>
@@ -215,17 +216,21 @@ template <js::AllowGC allowGC, typename 
 MOZ_ALWAYS_INLINE JSFlatString*
 JSFlatString::new_(js::ExclusiveContext* cx, const CharT* chars, size_t length)
 {
     MOZ_ASSERT(chars[length] == CharT(0));
 
     if (!validateLength(cx, length))
         return nullptr;
 
-    JSFlatString* str = static_cast<JSFlatString*>(js::Allocate<JSString, allowGC>(cx));
+    JSFlatString* str;
+    if (cx->compartment()->isAtomsCompartment())
+        str = js::Allocate<js::NormalAtom, allowGC>(cx);
+    else
+        str = static_cast<JSFlatString*>(js::Allocate<JSString, allowGC>(cx));
     if (!str)
         return nullptr;
 
     str->init(chars, length);
     return str;
 }
 
 inline js::PropertyName*
@@ -242,23 +247,29 @@ JSFlatString::toPropertyName(JSContext* 
         return nullptr;
     return atom->asPropertyName();
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSThinInlineString*
 JSThinInlineString::new_(js::ExclusiveContext* cx)
 {
+    if (cx->compartment()->isAtomsCompartment())
+        return (JSThinInlineString*)(js::Allocate<js::NormalAtom, allowGC>(cx));
+
     return static_cast<JSThinInlineString*>(js::Allocate<JSString, allowGC>(cx));
 }
 
 template <js::AllowGC allowGC>
 MOZ_ALWAYS_INLINE JSFatInlineString*
 JSFatInlineString::new_(js::ExclusiveContext* cx)
 {
+    if (cx->compartment()->isAtomsCompartment())
+        return (JSFatInlineString*)(js::Allocate<js::FatInlineAtom, allowGC>(cx));
+
     return js::Allocate<JSFatInlineString, allowGC>(cx);
 }
 
 template<>
 MOZ_ALWAYS_INLINE JS::Latin1Char*
 JSThinInlineString::init<JS::Latin1Char>(size_t length)
 {
     MOZ_ASSERT(lengthFits<JS::Latin1Char>(length));
@@ -346,27 +357,29 @@ js::StaticStrings::getLength2(char16_t c
     return length2StaticTable[index];
 }
 
 MOZ_ALWAYS_INLINE void
 JSString::finalize(js::FreeOp* fop)
 {
     /* FatInline strings are in a different arena. */
     MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
+    MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
 
     if (isFlat())
         asFlat().finalize(fop);
     else
         MOZ_ASSERT(isDependent() || isRope());
 }
 
 inline void
 JSFlatString::finalize(js::FreeOp* fop)
 {
     MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_STRING);
+    MOZ_ASSERT(getAllocKind() != js::gc::AllocKind::FAT_INLINE_ATOM);
 
     if (!isInline())
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSFatInlineString::finalize(js::FreeOp* fop)
 {
@@ -376,16 +389,18 @@ JSFatInlineString::finalize(js::FreeOp* 
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSAtom::finalize(js::FreeOp* fop)
 {
     MOZ_ASSERT(JSString::isAtom());
     MOZ_ASSERT(JSString::isFlat());
+    MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::ATOM ||
+               getAllocKind() == js::gc::AllocKind::FAT_INLINE_ATOM);
 
     if (!isInline())
         fop->free_(nonInlineCharsRaw());
 }
 
 inline void
 JSExternalString::finalize(js::FreeOp* fop)
 {
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -68,18 +68,22 @@ JSString::sizeOfExcludingThis(mozilla::M
     return flat.hasLatin1Chars()
            ? mallocSizeOf(flat.rawLatin1Chars())
            : mallocSizeOf(flat.rawTwoByteChars());
 }
 
 JS::ubi::Node::Size
 JS::ubi::Concrete<JSString>::size(mozilla::MallocSizeOf mallocSizeOf) const
 {
-    JSString &str = get();
-    size_t size = str.isFatInline() ? sizeof(JSFatInlineString) : sizeof(JSString);
+    JSString& str = get();
+    size_t size;
+    if (str.isAtom())
+        size = str.isFatInline() ? sizeof(js::FatInlineAtom) : sizeof(js::NormalAtom);
+    else
+        size = str.isFatInline() ? sizeof(JSFatInlineString) : sizeof(JSString);
 
     // We can't use mallocSizeof on things in the nursery. At the moment,
     // strings are never in the nursery, but that may change.
     MOZ_ASSERT(!IsInsideNursery(&str));
     size += str.sizeOfExcludingThis(mallocSizeOf);
 
     return size;
 }
@@ -814,25 +818,27 @@ StaticStrings::init(JSContext* cx)
 
     using Latin1Range = mozilla::Range<const Latin1Char>;
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
         Latin1Char buffer[] = { Latin1Char(i), '\0' };
         JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 1));
         if (!s)
             return false;
-        unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
+        HashNumber hash = mozilla::HashString(buffer, 1);
+        unitStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
     }
 
     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
         Latin1Char buffer[] = { FROM_SMALL_CHAR(i >> 6), FROM_SMALL_CHAR(i & 0x3F), '\0' };
         JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 2));
         if (!s)
             return false;
-        length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
+        HashNumber hash = mozilla::HashString(buffer, 2);
+        length2StaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
     }
 
     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
         if (i < 10) {
             intStaticTable[i] = unitStaticTable[i + '0'];
         } else if (i < 100) {
             size_t index = ((size_t)TO_SMALL_CHAR((i / 10) + '0') << 6) +
                 TO_SMALL_CHAR((i % 10) + '0');
@@ -840,17 +846,18 @@ StaticStrings::init(JSContext* cx)
         } else {
             Latin1Char buffer[] = { Latin1Char('0' + (i / 100)),
                                     Latin1Char('0' + ((i / 10) % 10)),
                                     Latin1Char('0' + (i % 10)),
                                     '\0' };
             JSFlatString* s = NewInlineString<NoGC>(cx, Latin1Range(buffer, 3));
             if (!s)
                 return false;
-            intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom();
+            HashNumber hash = mozilla::HashString(buffer, 3);
+            intStaticTable[i] = s->morphAtomizedStringIntoPermanentAtom(hash);
         }
     }
 
     return true;
 }
 
 void
 StaticStrings::trace(JSTracer* trc)
@@ -1314,17 +1321,16 @@ template JSFlatString*
 NewStringCopyN<NoGC>(ExclusiveContext* cx, const char16_t* s, size_t n);
 
 template JSFlatString*
 NewStringCopyN<CanGC>(ExclusiveContext* cx, const Latin1Char* s, size_t n);
 
 template JSFlatString*
 NewStringCopyN<NoGC>(ExclusiveContext* cx, const Latin1Char* s, size_t n);
 
-
 template <js::AllowGC allowGC>
 JSFlatString*
 NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8)
 {
     JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
     if (encoding == JS::SmallestEncoding::ASCII)
         return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -118,32 +118,35 @@ static const size_t UINT32_CHAR_BUFFER_L
  *  |  +-- JSUndependedString   original dependent base / -
  *  |  |
  *  |  +-- JSInlineString (abstract)    - / chars stored in header
  *  |      |
  *  |      +-- JSThinInlineString       - / header is normal
  *  |      |
  *  |      +-- JSFatInlineString        - / header is fat
  *  |
- * JSAtom                       - / string equality === pointer equality
+ * JSAtom (abstract)            - / string equality === pointer equality
+ *  |  |
+ *  |  +-- js::NormalAtom       - JSFlatString + atom hash code
+ *  |  |
+ *  |  +-- js::FatInlineAtom    - JSFatInlineString + atom hash code
  *  |
  * js::PropertyName             - / chars don't contain an index (uint32_t)
  *
  * Classes marked with (abstract) above are not literally C++ Abstract Base
  * Classes (since there are no virtual functions, pure or not, in this
  * hierarchy), but have the same meaning: there are no strings with this type as
  * its most-derived type.
  *
  * Atoms can additionally be permanent, i.e. unable to be collected, and can
  * be combined with other string types to create additional most-derived types
  * that satisfy the invariants of more than one of the abovementioned
- * most-derived types:
- *  - InlineAtom     = JSInlineString     + JSAtom (atom with inline chars, abstract)
- *  - ThinInlineAtom = JSThinInlineString + JSAtom (atom with inline chars)
- *  - FatInlineAtom  = JSFatInlineString  + JSAtom (atom with (more) inline chars)
+ * most-derived types. Furthermore, each atom stores a hash number (based on its
+ * chars). This hash number is used as key in the atoms table and when the atom
+ * is used as key in a JS Map/Set.
  *
  * Derived string types can be queried from ancestor types via isX() and
  * retrieved with asX() debug-only-checked casts.
  *
  * The ensureX() operations mutate 'this' in place to effectively the type to be
  * at least X (e.g., ensureLinear will change a JSRope to be a JSFlatString).
  */
 
@@ -764,24 +767,18 @@ class JSFlatString : public JSLinearStri
      * this method.
      */
     inline js::PropertyName* toPropertyName(JSContext* cx);
 
     /*
      * Once a JSFlatString sub-class has been added to the atom state, this
      * operation changes the string to the JSAtom type, in place.
      */
-    MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoAtom() {
-        d.u1.flags |= ATOM_BIT;
-        return &asAtom();
-    }
-    MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoPermanentAtom() {
-        d.u1.flags |= PERMANENT_ATOM_MASK;
-        return &asAtom();
-    }
+    MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoAtom(js::HashNumber hash);
+    MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoPermanentAtom(js::HashNumber hash);
 
     inline void finalize(js::FreeOp* fop);
 
 #ifdef DEBUG
     void dumpRepresentation(FILE* fp, int indent) const;
 #endif
 };
 
@@ -983,27 +980,107 @@ class JSAtom : public JSFlatString
     }
 
     // Transform this atom into a permanent atom. This is only done during
     // initialization of the runtime.
     MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
         d.u1.flags |= PERMANENT_ATOM_MASK;
     }
 
+    inline js::HashNumber hash() const;
+    inline void initHash(js::HashNumber hash);
+
 #ifdef DEBUG
     void dump(FILE* fp);
     void dump();
 #endif
 };
 
 static_assert(sizeof(JSAtom) == sizeof(JSString),
               "string subclasses must be binary-compatible with JSString");
 
 namespace js {
 
+class NormalAtom : public JSAtom
+{
+  protected: // Silence Clang unused-field warning.
+    HashNumber hash_;
+    uint32_t padding_; // Ensure the size is a multiple of gc::CellSize.
+
+  public:
+    HashNumber hash() const {
+        return hash_;
+    }
+    void initHash(HashNumber hash) {
+        hash_ = hash;
+    }
+};
+
+static_assert(sizeof(NormalAtom) == sizeof(JSString) + sizeof(uint64_t),
+              "NormalAtom must have size of a string + HashNumber, "
+              "aligned to gc::CellSize");
+
+class FatInlineAtom : public JSAtom
+{
+  protected: // Silence Clang unused-field warning.
+    char inlineStorage_[sizeof(JSFatInlineString) - sizeof(JSString)];
+    HashNumber hash_;
+    uint32_t padding_; // Ensure the size is a multiple of gc::CellSize.
+
+  public:
+    HashNumber hash() const {
+        return hash_;
+    }
+    void initHash(HashNumber hash) {
+        hash_ = hash;
+    }
+};
+
+static_assert(sizeof(FatInlineAtom) == sizeof(JSFatInlineString) + sizeof(uint64_t),
+              "FatInlineAtom must have size of a fat inline string + HashNumber, "
+              "aligned to gc::CellSize");
+
+} // namespace js
+
+inline js::HashNumber
+JSAtom::hash() const
+{
+    if (isFatInline())
+        return static_cast<const js::FatInlineAtom*>(this)->hash();
+    return static_cast<const js::NormalAtom*>(this)->hash();
+}
+
+inline void
+JSAtom::initHash(js::HashNumber hash)
+{
+    if (isFatInline())
+        return static_cast<js::FatInlineAtom*>(this)->initHash(hash);
+    return static_cast<js::NormalAtom*>(this)->initHash(hash);
+}
+
+MOZ_ALWAYS_INLINE JSAtom*
+JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
+{
+    d.u1.flags |= ATOM_BIT;
+    JSAtom* atom = &asAtom();
+    atom->initHash(hash);
+    return atom;
+}
+
+MOZ_ALWAYS_INLINE JSAtom*
+JSFlatString::morphAtomizedStringIntoPermanentAtom(js::HashNumber hash)
+{
+    d.u1.flags |= PERMANENT_ATOM_MASK;
+    JSAtom* atom = &asAtom();
+    atom->initHash(hash);
+    return atom;
+}
+
+namespace js {
+
 class StaticStrings
 {
   private:
     /* Bigger chars cannot be in a length-2 string. */
     static const size_t SMALL_CHAR_LIMIT    = 128U;
     static const size_t NUM_SMALL_CHARS     = 64U;
 
     JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS];
@@ -1216,16 +1293,38 @@ NewStringCopyUTF8N(JSContext* cx, const 
 
 template <js::AllowGC allowGC>
 inline JSFlatString*
 NewStringCopyUTF8Z(JSContext* cx, const JS::ConstUTF8CharsZ utf8)
 {
     return NewStringCopyUTF8N<allowGC>(cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str())));
 }
 
+JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
+
+static MOZ_ALWAYS_INLINE js::HashNumber
+HashId(jsid id)
+{
+    if (MOZ_LIKELY(JSID_IS_ATOM(id)))
+        return JSID_TO_ATOM(id)->hash();
+    return mozilla::HashGeneric(JSID_BITS(id));
+}
+
+template <>
+struct DefaultHasher<jsid>
+{
+    typedef jsid Lookup;
+    static HashNumber hash(jsid id) {
+        return HashId(id);
+    }
+    static bool match(jsid id1, jsid id2) {
+        return id1 == id2;
+    }
+};
+
 } /* namespace js */
 
 // Addon IDs are interned atoms which are never destroyed. This detail is
 // not exposed outside the API.
 class JSAddonId : public JSAtom
 {};
 
 MOZ_ALWAYS_INLINE bool
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -4865,28 +4865,28 @@ BaseCompiler::endBlock(ExprType type)
 
     // Save the value.
     AnyReg r;
     if (!deadCode_)
         r = popJoinRegUnlessVoid(type);
 
     // Leave the block.
     popStackOnBlockExit(block.framePushed);
+    popValueStackTo(block.stackSize);
 
     // Bind after cleanup: branches out will have popped the stack.
     if (block.label->used()) {
         masm.bind(block.label);
         // No value was provided by the fallthrough but the branch out will
         // have stored one in joinReg, so capture that.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
-    popValueStackTo(block.stackSize);
     popControl();
 
     // Retain the value stored in joinReg by all paths, if there are any.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
@@ -4918,18 +4918,18 @@ BaseCompiler::endLoop(ExprType type)
 {
     Control& block = controlItem(0);
 
     AnyReg r;
     if (!deadCode_)
         r = popJoinRegUnlessVoid(type);
 
     popStackOnBlockExit(block.framePushed);
-
     popValueStackTo(block.stackSize);
+
     popControl();
 
     // Retain the value stored in joinReg by all paths.
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 // The bodies of the "then" and "else" arms can be arbitrary sequences
@@ -4979,26 +4979,26 @@ BaseCompiler::emitIf()
 }
 
 void
 BaseCompiler::endIfThen()
 {
     Control& ifThen = controlItem(0);
 
     popStackOnBlockExit(ifThen.framePushed);
+    popValueStackTo(ifThen.stackSize);
 
     if (ifThen.otherLabel->used())
         masm.bind(ifThen.otherLabel);
 
     if (ifThen.label->used())
         masm.bind(ifThen.label);
 
     deadCode_ = ifThen.deadOnArrival;
 
-    popValueStackTo(ifThen.stackSize);
     popControl();
 }
 
 bool
 BaseCompiler::emitElse()
 {
     ExprType thenType;
     Nothing unused_thenValue;
@@ -5013,27 +5013,26 @@ BaseCompiler::emitElse()
 
     ifThenElse.deadThenBranch = deadCode_;
 
     AnyReg r;
     if (!deadCode_)
         r = popJoinRegUnlessVoid(thenType);
 
     popStackOnBlockExit(ifThenElse.framePushed);
+    popValueStackTo(ifThenElse.stackSize);
 
     if (!deadCode_)
         masm.jump(ifThenElse.label);
 
     if (ifThenElse.otherLabel->used())
         masm.bind(ifThenElse.otherLabel);
 
     // Reset to the "else" branch.
 
-    popValueStackTo(ifThenElse.stackSize);
-
     if (!deadCode_)
         freeJoinRegUnlessVoid(r);
 
     deadCode_ = ifThenElse.deadOnArrival;
 
     return true;
 }
 
@@ -5049,32 +5048,32 @@ BaseCompiler::endIfThenElse(ExprType typ
     // we want to find there.  The "then" arm has the same constraint.
 
     AnyReg r;
 
     if (!deadCode_)
         r = popJoinRegUnlessVoid(type);
 
     popStackOnBlockExit(ifThenElse.framePushed);
+    popValueStackTo(ifThenElse.stackSize);
 
     if (ifThenElse.label->used())
         masm.bind(ifThenElse.label);
 
     bool joinLive = !ifThenElse.deadOnArrival &&
                     (!ifThenElse.deadThenBranch || !deadCode_ || ifThenElse.label->bound());
 
     if (joinLive) {
         // No value was provided by the "then" path but capture the one
         // provided by the "else" path.
         if (deadCode_)
             r = captureJoinRegUnlessVoid(type);
         deadCode_ = false;
     }
 
-    popValueStackTo(ifThenElse.stackSize);
     popControl();
 
     if (!deadCode_)
         pushJoinRegUnlessVoid(r);
 }
 
 bool
 BaseCompiler::emitEnd()
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -624,16 +624,21 @@ class MOZ_STACK_CLASS OpIter : private P
                                      Value* falseValue,
                                      Value* condition);
     MOZ_MUST_USE bool readSimdCtor();
     MOZ_MUST_USE bool readSimdCtorArg(ValType elementType, uint32_t numElements, uint32_t argIndex,
                                       Value* arg);
     MOZ_MUST_USE bool readSimdCtorArgsEnd(uint32_t numElements);
     MOZ_MUST_USE bool readSimdCtorReturn(ValType simdType);
 
+    // At a location where readOp is allowed, peek at the next opcode
+    // without consuming it or updating any internal state.
+    // Never fails: returns uint16_t(Op::Limit) if it can't read.
+    uint16_t peekOp();
+
     // ------------------------------------------------------------------------
     // Stack management.
 
     // Set the result value of the current top-of-value-stack expression.
     void setResult(Value value) {
         if (MOZ_LIKELY(reachable_))
             valueStack_.back().setValue(value);
     }
@@ -854,16 +859,35 @@ OpIter<Policy>::readOp(uint16_t* op)
     }
 
     op_ = Op(*op);  // debug-only
 
     return true;
 }
 
 template <typename Policy>
+inline uint16_t
+OpIter<Policy>::peekOp()
+{
+    const uint8_t* pos = d_.currentPosition();
+    uint16_t op;
+
+    if (Validate) {
+        if (MOZ_UNLIKELY(!d_.readOp(&op)))
+            op = uint16_t(Op::Limit);
+    } else {
+        op = uint16_t(d_.uncheckedReadOp());
+    }
+
+    d_.rollbackPosition(pos);
+
+    return op;
+}
+
+template <typename Policy>
 inline bool
 OpIter<Policy>::readFunctionStart(ExprType ret)
 {
     MOZ_ASSERT(valueStack_.empty());
     MOZ_ASSERT(controlStack_.empty());
     MOZ_ASSERT(Op(op_) == Op::Limit);
     MOZ_ASSERT(reachable_);
 
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -37,21 +37,26 @@ using mozilla::IsNegativeZero;
 
 struct WasmRenderContext
 {
     JSContext* cx;
     AstModule* module;
     WasmPrintBuffer& buffer;
     GeneratedSourceMap* maybeSourceMap;
     uint32_t indent;
-
     uint32_t currentFuncIndex;
 
-    WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer, GeneratedSourceMap* sourceMap)
-      : cx(cx), module(module), buffer(buffer), maybeSourceMap(sourceMap), indent(0), currentFuncIndex(0)
+    WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer,
+                      GeneratedSourceMap* sourceMap)
+      : cx(cx),
+        module(module),
+        buffer(buffer),
+        maybeSourceMap(sourceMap),
+        indent(0),
+        currentFuncIndex(0)
     {}
 
     StringBuffer& sb() { return buffer.stringBuffer(); }
 };
 
 /*****************************************************************************/
 // utilities
 
@@ -423,57 +428,75 @@ RenderSetGlobal(WasmRenderContext& c, As
 
     MAP_AST_EXPR(c, sg);
     if (!c.buffer.append("set_global "))
         return false;
     return RenderRef(c, sg.global());
 }
 
 static bool
-RenderExprList(WasmRenderContext& c, const AstExprVector& exprs)
+RenderExprList(WasmRenderContext& c, const AstExprVector& exprs, uint32_t startAt = 0)
 {
-    for (uint32_t i = 0; i < exprs.length(); i++) {
+    for (uint32_t i = startAt; i < exprs.length(); i++) {
         if (!RenderExpr(c, *exprs[i]))
             return false;
     }
     return true;
 }
 
 static bool
-RenderBlock(WasmRenderContext& c, AstBlock& block)
+RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false)
 {
-    if (!RenderIndent(c))
+    if (!isInline && !RenderIndent(c))
         return false;
 
     MAP_AST_EXPR(c, block);
     if (block.op() == Op::Block) {
         if (!c.buffer.append("block"))
             return false;
     } else if (block.op() == Op::Loop) {
         if (!c.buffer.append("loop"))
             return false;
     } else {
         return Fail(c, "unexpected block kind");
     }
 
     if (!RenderBlockNameAndSignature(c, block.name(), block.type()))
         return false;
 
+    uint32_t startAtSubExpr = 0;
+
+    // If there is a stack of blocks, print them all inline.
+    if (block.op() == Op::Block &&
+        block.exprs().length() &&
+        block.exprs()[0]->kind() == AstExprKind::Block &&
+        block.exprs()[0]->as<AstBlock>().op() == Op::Block)
+    {
+        if (!c.buffer.append(' '))
+            return false;
+
+        // Render the first inner expr (block) at the same indent level, but
+        // next instructions one level further.
+        if (!RenderBlock(c, block.exprs()[0]->as<AstBlock>(), /* isInline */ true))
+            return false;
+
+        startAtSubExpr = 1;
+    }
+
     if (!c.buffer.append('\n'))
         return false;
 
     c.indent++;
-    if (!RenderExprList(c, block.exprs()))
+    if (!RenderExprList(c, block.exprs(), startAtSubExpr))
         return false;
     c.indent--;
 
-    if (!RenderIndent(c))
-        return false;
-
-    return c.buffer.append("end");
+    return RenderIndent(c) &&
+           c.buffer.append("end ") &&
+           RenderName(c, block.name());
 }
 
 static bool
 RenderFirst(WasmRenderContext& c, AstFirst& first)
 {
     return RenderExprList(c, first.exprs());
 }
 
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -368,17 +368,19 @@ static bool
 IsWasmLetter(char16_t c)
 {
     return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
 }
 
 static bool
 IsNameAfterDollar(char16_t c)
 {
-    return IsWasmLetter(c) || IsWasmDigit(c) || c == '_' || c == '$' || c == '-' || c == '.';
+    return IsWasmLetter(c) ||
+           IsWasmDigit(c) ||
+           c == '_' || c == '$' || c == '-' || c == '.' || c == '>';
 }
 
 static bool
 IsHexDigit(char c, uint8_t* value)
 {
     if (c >= '0' && c <= '9') {
         *value = c - '0';
         return true;
@@ -1557,16 +1559,38 @@ ParseBlockSignature(WasmParseContext& c,
     if (c.ts.getIf(WasmToken::ValueType, &token))
         *type = ToExprType(token.valueType());
     else
         *type = ExprType::Void;
 
     return true;
 }
 
+static bool
+MaybeMatchName(WasmParseContext& c, const AstName& name)
+{
+    WasmToken tok;
+    if (c.ts.getIf(WasmToken::Name, &tok)) {
+        AstName otherName = tok.name();
+        if (otherName.empty())
+            return true;
+
+        if (name.empty()) {
+            c.ts.generateError(tok, "end name without a start name", c.error);
+            return false;
+        }
+
+        if (otherName != name) {
+            c.ts.generateError(tok, "start/end names don't match", c.error);
+            return false;
+        }
+    }
+    return true;
+}
+
 static AstBlock*
 ParseBlock(WasmParseContext& c, Op op, bool inParens)
 {
     AstExprVector exprs(c.lifo);
 
     AstName name = c.ts.getIfName();
 
     // Compatibility syntax sugar: If a second label is present, we'll wrap
@@ -1585,16 +1609,18 @@ ParseBlock(WasmParseContext& c, Op op, b
         return nullptr;
 
     if (!ParseExprList(c, &exprs, inParens))
         return nullptr;
 
     if (!inParens) {
         if (!c.ts.match(WasmToken::End, c.error))
             return nullptr;
+        if (!MaybeMatchName(c, name))
+            return nullptr;
     }
 
     AstBlock* result = new(c.lifo) AstBlock(op, type, name, Move(exprs));
 
     if (op == Op::Loop && !otherName.empty()) {
         if (!exprs.append(result))
             return nullptr;
         result = new(c.lifo) AstBlock(Op::Block, type, otherName, Move(exprs));
@@ -2191,29 +2217,33 @@ ParseIf(WasmParseContext& c, bool inPare
     if (inParens) {
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
     AstExprVector elseExprs(c.lifo);
     if (!inParens || c.ts.getIf(WasmToken::OpenParen)) {
         if (c.ts.getIf(WasmToken::Else)) {
+            if (!MaybeMatchName(c, name))
+                return nullptr;
             if (!ParseExprList(c, &elseExprs, inParens))
                 return nullptr;
         } else if (inParens) {
             AstExpr* elseBranch = ParseExprInsideParens(c);
             if (!elseBranch || !elseExprs.append(elseBranch))
                 return nullptr;
         }
         if (inParens) {
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
             if (!c.ts.match(WasmToken::End, c.error))
                 return nullptr;
+            if (!MaybeMatchName(c, name))
+                return nullptr;
         }
     }
 
     return new(c.lifo) AstIf(type, cond, name, Move(thenExprs), Move(elseExprs));
 }
 
 static bool
 ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, uint32_t* alignLog2, AstExpr** base,
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -335,16 +335,20 @@ class Decoder
         MOZ_ASSERT(cur_ <= end_);
         return cur_ == end_;
     }
 
     size_t bytesRemain() const {
         MOZ_ASSERT(end_ >= cur_);
         return size_t(end_ - cur_);
     }
+    // pos must be a value previously returned from currentPosition.
+    void rollbackPosition(const uint8_t* pos) {
+        cur_ = pos;
+    }
     const uint8_t* currentPosition() const {
         return cur_;
     }
     size_t currentOffset() const {
         return cur_ - beg_;
     }
     const uint8_t* begin() const {
         return beg_;
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -313,16 +313,20 @@ struct LayoutDevicePixel {
   static LayoutDeviceIntRect FromAppUnitsToNearest(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntRect::FromUnknownRect(aRect.ToNearestPixels(aAppUnitsPerDevPixel));
   }
 
   static LayoutDeviceIntRect FromAppUnitsToInside(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntRect::FromUnknownRect(aRect.ToInsidePixels(aAppUnitsPerDevPixel));
   }
 
+  static LayoutDeviceIntRect FromAppUnitsToOutside(const nsRect& aRect, nscoord aAppUnitsPerDevPixel) {
+    return LayoutDeviceIntRect::FromUnknownRect(aRect.ToOutsidePixels(aAppUnitsPerDevPixel));
+  }
+
   static LayoutDeviceIntSize FromAppUnitsRounded(const nsSize& aSize, nscoord aAppUnitsPerDevPixel) {
     return LayoutDeviceIntSize(
       NSAppUnitsToIntPixels(aSize.width, aAppUnitsPerDevPixel),
       NSAppUnitsToIntPixels(aSize.height, aAppUnitsPerDevPixel));
   }
 
   static nsPoint ToAppUnits(const LayoutDeviceIntPoint& aPoint, nscoord aAppUnitsPerDevPixel) {
     return nsPoint(aPoint.x * aAppUnitsPerDevPixel,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -7068,17 +7068,18 @@ nsLayoutUtils::SurfaceFromElement(nsIIma
     return result;
   }
 
   uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS;
 
   uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME)
                         ? (uint32_t) imgIContainer::FRAME_FIRST
                         : (uint32_t) imgIContainer::FRAME_CURRENT;
-  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE;
+  uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE
+                      | imgIContainer::FLAG_ASYNC_NOTIFY;
   if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION)
     frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION;
   if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) {
     frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
     result.mIsPremultiplied = false;
   }
 
   int32_t imgWidth, imgHeight;
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -30,16 +30,17 @@
 #endif
 
 //#define DEBUG_CANVAS_FOCUS
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
 using namespace mozilla::gfx;
+using namespace mozilla::layers;
 
 nsCanvasFrame*
 NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsCanvasFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
@@ -254,16 +255,48 @@ nsDisplayCanvasBackgroundColor::Paint(ns
     DrawTarget* drawTarget = aCtx->GetDrawTarget();
     int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
     Rect devPxRect =
       NSRectToSnappedRect(bgClipRect, appUnitsPerDevPixel, *drawTarget);
     drawTarget->FillRect(devPxRect, ColorPattern(ToDeviceColor(mColor)));
   }
 }
 
+already_AddRefed<Layer>
+nsDisplayCanvasBackgroundColor::BuildLayer(nsDisplayListBuilder* aBuilder,
+                                           LayerManager* aManager,
+                                           const ContainerLayerParameters& aContainerParameters)
+{
+  if (NS_GET_A(mColor) == 0) {
+    return nullptr;
+  }
+
+  RefPtr<ColorLayer> layer = static_cast<ColorLayer*>
+    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (!layer) {
+    layer = aManager->CreateColorLayer();
+    if (!layer) {
+      return nullptr;
+    }
+  }
+  layer->SetColor(ToDeviceColor(mColor));
+
+  nsCanvasFrame* frame = static_cast<nsCanvasFrame*>(mFrame);
+  nsPoint offset = ToReferenceFrame();
+  nsRect bgClipRect = frame->CanvasArea() + offset;
+
+  int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+
+  layer->SetBounds(bgClipRect.ToNearestPixels(appUnitsPerDevPixel));
+  layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
+                                                      aContainerParameters.mOffset.y, 0));
+
+  return layer.forget();
+}
+
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayCanvasBackgroundColor::WriteDebugInfo(std::stringstream& aStream)
 {
   aStream << " (rgba "
           << (int)NS_GET_R(mColor) << ","
           << (int)NS_GET_G(mColor) << ","
           << (int)NS_GET_B(mColor) << ","
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -165,16 +165,28 @@ public:
   }
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override
   {
     // We need to override so we don't consider border-radius.
     aOutFrames->AppendElement(mFrame);
   }
 
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override
+  {
+    if (ForceActiveLayers()) {
+      return mozilla::LAYER_ACTIVE;
+    }
+    return mozilla::LAYER_NONE;
+  }
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override;
 
   void SetExtraBackgroundColor(nscolor aColor)
   {
     mColor = aColor;
   }
 
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1568,52 +1568,54 @@ nsDisplayImage::GetDestRect()
   return imageFrame->PredictedDestRect(frameContentBox);
 }
 
 LayerState
 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder,
                               LayerManager* aManager,
                               const ContainerLayerParameters& aParameters)
 {
-  bool animated = false;
-  if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
-      mImage->GetType() != imgIContainer::TYPE_RASTER ||
-      NS_FAILED(mImage->GetAnimated(&animated)) ||
-      !animated) {
-    if (!aManager->IsCompositingCheap() ||
-        !nsLayoutUtils::GPUImageScalingEnabled()) {
-      return LAYER_NONE;
-    }
-  }
-
-  if (!animated) {
-    int32_t imageWidth;
-    int32_t imageHeight;
-    mImage->GetWidth(&imageWidth);
-    mImage->GetHeight(&imageHeight);
-
-    NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
-
-    const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
-    const LayoutDeviceRect destRect =
-      LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
-    const LayerRect destLayerRect = destRect * aParameters.Scale();
-
-    // Calculate the scaling factor for the frame.
-    const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
-                                  destLayerRect.height / imageHeight);
-
-    // If we are not scaling at all, no point in separating this into a layer.
-    if (scale.width == 1.0f && scale.height == 1.0f) {
-      return LAYER_NONE;
+  if (!nsDisplayItem::ForceActiveLayers()) {
+    bool animated = false;
+    if (!nsLayoutUtils::AnimatedImageLayersEnabled() ||
+        mImage->GetType() != imgIContainer::TYPE_RASTER ||
+        NS_FAILED(mImage->GetAnimated(&animated)) ||
+        !animated) {
+      if (!aManager->IsCompositingCheap() ||
+          !nsLayoutUtils::GPUImageScalingEnabled()) {
+        return LAYER_NONE;
+      }
     }
 
-    // If the target size is pretty small, no point in using a layer.
-    if (destLayerRect.width * destLayerRect.height < 64 * 64) {
-      return LAYER_NONE;
+    if (!animated) {
+      int32_t imageWidth;
+      int32_t imageHeight;
+      mImage->GetWidth(&imageWidth);
+      mImage->GetHeight(&imageHeight);
+
+      NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!");
+
+      const int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel();
+      const LayoutDeviceRect destRect =
+        LayoutDeviceRect::FromAppUnits(GetDestRect(), factor);
+      const LayerRect destLayerRect = destRect * aParameters.Scale();
+
+      // Calculate the scaling factor for the frame.
+      const gfxSize scale = gfxSize(destLayerRect.width / imageWidth,
+                                    destLayerRect.height / imageHeight);
+
+      // If we are not scaling at all, no point in separating this into a layer.
+      if (scale.width == 1.0f && scale.height == 1.0f) {
+        return LAYER_NONE;
+      }
+
+      // If the target size is pretty small, no point in using a layer.
+      if (destLayerRect.width * destLayerRect.height < 64 * 64) {
+        return LAYER_NONE;
+      }
     }
   }
 
   uint32_t flags = aBuilder->ShouldSyncDecodeImages()
                  ? imgIContainer::FLAG_SYNC_DECODE
                  : imgIContainer::FLAG_NONE;
 
   if (!mImage->IsImageContainerAvailable(aManager, flags)) {
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Unused.h"
+#include "mozilla/PodOperations.h"
 
 #include "nsCOMPtr.h"
 #include "nsBlockFrame.h"
 #include "nsFontMetrics.h"
 #include "nsSplittableFrame.h"
 #include "nsLineLayout.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
@@ -61,16 +62,17 @@
 #include "nsCSSRendering.h"
 #include "nsContentUtils.h"
 #include "nsLineBreaker.h"
 #include "nsIWordBreaker.h"
 #include "nsGenericDOMDataNode.h"
 #include "nsIFrameInlines.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
+#include "mozilla/layers/LayersMessages.h"
 
 #include <algorithm>
 #include <limits>
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 #include "nsPrintfCString.h"
@@ -93,16 +95,17 @@
 
 #ifdef DrawText
 #undef DrawText
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
+using namespace mozilla::layers;
 
 struct TabWidth {
   TabWidth(uint32_t aOffset, uint32_t aWidth)
     : mOffset(aOffset), mWidth(float(aWidth))
   { }
 
   uint32_t mOffset; // DOM offset relative to the current frame's offset.
   float    mWidth;  // extra space to be added at this position (in app units)
@@ -4759,44 +4762,42 @@ nsTextFrame::CharacterDataChanged(Charac
   }
 
   return NS_OK;
 }
 
 class nsDisplayText : public nsCharClipDisplayItem {
 public:
   nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
-                Maybe<bool> aIsSelected) :
-    nsCharClipDisplayItem(aBuilder, aFrame),
-    mOpacity(1.0f),
-    mDisableSubpixelAA(false) {
-    mIsFrameSelected = aIsSelected;
-    MOZ_COUNT_CTOR(nsDisplayText);
-  }
+                Maybe<bool> aIsSelected);
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayText() {
     MOZ_COUNT_DTOR(nsDisplayText);
   }
 #endif
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) override {
     *aSnap = false;
-    nsRect temp = mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
-    // Bug 748228
-    temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
-    return temp;
+    return mBounds;
   }
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState,
                        nsTArray<nsIFrame*> *aOutFrames) override {
+    MOZ_ASSERT(mMergedFrames.IsEmpty());
     if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
       aOutFrames->AppendElement(mFrame);
     }
   }
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override;
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override;
   NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
 
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
   {
     if (gfxPlatform::GetPlatform()->RespectsFontStyleSmoothing()) {
       // On OS X, web authors can turn off subpixel text rendering using the
@@ -4817,16 +4818,18 @@ public:
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) override;
 
   virtual void DisableComponentAlpha() override {
     mDisableSubpixelAA = true;
   }
 
+  void RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording = false);
+
   bool CanApplyOpacity() const override
   {
     nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
     if (f->IsSelected()) {
       return false;
     }
 
     const nsStyleText* textStyle = f->StyleText();
@@ -4849,16 +4852,72 @@ public:
   {
     NS_ASSERTION(CanApplyOpacity(), "ApplyOpacity should be allowed");
     mOpacity = aOpacity;
     if (aClip) {
       IntersectClip(aBuilder, *aClip);
     }
   }
 
+  void WriteDebugInfo(std::stringstream& aStream) override
+  {
+#ifdef DEBUG
+    aStream << " (\"";
+
+    nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
+    nsCString buf;
+    int32_t totalContentLength;
+    f->ToCString(buf, &totalContentLength);
+
+    for (nsTextFrame* f : mMergedFrames) {
+      f->ToCString(buf, &totalContentLength);
+    }
+    aStream << buf.get() << "\")";
+#endif
+  }
+
+  void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) override
+  {
+    aFrames->AppendElements(mMergedFrames);
+  }
+
+  bool TryMerge(nsDisplayItem* aItem) override {
+    if (aItem->GetType() != TYPE_TEXT)
+      return false;
+    if (aItem->GetClip() != GetClip())
+      return false;
+    if (aItem->ScrollClip() != ScrollClip())
+      return false;
+
+    nsDisplayText* other = static_cast<nsDisplayText*>(aItem);
+    if (!mFont || !other->mFont || mFont != other->mFont) {
+      return false;
+    }
+    if (mOpacity != other->mOpacity) {
+      return false;
+    }
+
+    mBounds.UnionRect(mBounds, other->mBounds);
+    mVisibleRect.UnionRect(mVisibleRect, other->mVisibleRect);
+    mMergedFrames.AppendElement(static_cast<nsTextFrame*>(other->mFrame));
+    mMergedFrames.AppendElements(mozilla::Move(other->mMergedFrames));
+
+    for (GlyphArray& g : other->mGlyphs) {
+      GlyphArray* append = mGlyphs.AppendElement();
+      append->color() = g.color();
+      append->glyphs().SwapElements(g.glyphs());
+    }
+    return true;
+}
+
+  RefPtr<ScaledFont> mFont;
+  nsTArray<GlyphArray> mGlyphs;
+  nsTArray<nsTextFrame*> mMergedFrames;
+  nsRect mBounds;
+
   float mOpacity;
   bool mDisableSubpixelAA;
 };
 
 class nsDisplayTextGeometry : public nsCharClipGeometry
 {
 public:
   nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)
@@ -4912,65 +4971,152 @@ NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(Te
 
 static float
 GetTextCombineScaleFactor(nsTextFrame* aFrame)
 {
   float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty());
   return factor ? factor : 1.0f;
 }
 
+nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
+                             Maybe<bool> aIsSelected)
+  : nsCharClipDisplayItem(aBuilder, aFrame)
+  , mOpacity(1.0f)
+  , mDisableSubpixelAA(false)
+{
+  MOZ_COUNT_CTOR(nsDisplayText);
+  mIsFrameSelected = aIsSelected;
+
+  mBounds = mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
+    // Bug 748228
+  mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
+
+  if (gfxPrefs::LayersAllowTextLayers()) {
+    RefPtr<DrawTargetCapture> capture =
+      gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreateCaptureDT(IntSize());
+    RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(capture);
+
+    // TODO: Paint() checks mDisableSubpixelAA, we should too.
+    RenderToContext(captureCtx, aBuilder, true);
+
+    // TODO: Ideally we'd re-use captureCtx in Paint() if we couldn't build
+    // a layer here. We have to deal with the problem that the ScreenReferenceDrawTarget
+    // might not be compatible with the DT used for layer rendering.
+
+    GlyphArray* g = mGlyphs.AppendElement();
+    std::vector<Glyph> glyphs;
+    Color color;
+    if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)) {
+      mFont = nullptr;
+      mGlyphs.Clear();
+    } else {
+      g->glyphs().SetLength(glyphs.size());
+      PodCopy(g->glyphs().begin(), glyphs.data(), glyphs.size());
+      g->color() = color;
+    }
+  }
+}
+
+LayerState
+nsDisplayText::GetLayerState(nsDisplayListBuilder* aBuilder,
+                             LayerManager* aManager,
+                             const ContainerLayerParameters& aParameters)
+{
+  if (mFont) {
+    return mozilla::LAYER_INACTIVE;
+  }
+  MOZ_ASSERT(mMergedFrames.IsEmpty());
+  return mozilla::LAYER_NONE;
+}
+
 void
 nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) {
   PROFILER_LABEL("nsDisplayText", "Paint",
     js::ProfileEntry::Category::GRAPHICS);
 
+  MOZ_ASSERT(mMergedFrames.IsEmpty());
+
+  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
+                                                    mDisableSubpixelAA);
+  RenderToContext(aCtx->ThebesContext(), aBuilder);
+}
+
+already_AddRefed<layers::Layer>
+nsDisplayText::BuildLayer(nsDisplayListBuilder* aBuilder,
+                          LayerManager* aManager,
+                          const ContainerLayerParameters& aContainerParameters)
+{
+  // We should have all the glyphs recorded now, build
+  // the TextLayer.
+  RefPtr<layers::TextLayer> layer = static_cast<layers::TextLayer*>
+    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (!layer) {
+    layer = aManager->CreateTextLayer();
+  }
+
+  layer->SetGlyphs(Move(mGlyphs));
+  layer->SetScaledFont(mFont);
+
+  auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
+  bool dummy;
+  const LayoutDeviceIntRect destBounds =
+          LayoutDeviceIntRect::FromAppUnitsToOutside(GetBounds(aBuilder, &dummy), A2D);
+  layer->SetBounds(IntRect(destBounds.x, destBounds.y, destBounds.width, destBounds.height));
+
+  layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
+                                                      aContainerParameters.mOffset.y, 0));
+  return layer.forget();
+}
+
+void
+nsDisplayText::RenderToContext(gfxContext* aCtx, nsDisplayListBuilder* aBuilder, bool aIsRecording)
+{
+  nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
+
   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
   // antialiased pixels beyond the measured text extents.
   // This is temporary until we do this in the actual calculation of text extents.
   auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect extraVisible =
     LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
   extraVisible.Inflate(1);
-  nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
-
-  DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
-                                                    mDisableSubpixelAA);
-  gfxContext* ctx = aCtx->ThebesContext();
-  gfxContextAutoSaveRestore save(ctx);
+
+  gfxContextAutoSaveRestore save(aCtx);
 
   gfxRect pixelVisible(extraVisible.x, extraVisible.y,
                        extraVisible.width, extraVisible.height);
   pixelVisible.Inflate(2);
   pixelVisible.RoundOut();
 
   if (!aBuilder->IsForGenerateGlyphMask() &&
-      !aBuilder->IsForPaintingSelectionBG()) {
-    ctx->NewPath();
-    ctx->Rectangle(pixelVisible);
-    ctx->Clip();
+      !aBuilder->IsForPaintingSelectionBG() &&
+      !aIsRecording) {
+    aCtx->NewPath();
+    aCtx->Rectangle(pixelVisible);
+    aCtx->Clip();
   }
 
   NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
   NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
 
   nsPoint framePt = ToReferenceFrame();
   if (f->StyleContext()->IsTextCombined()) {
     float scaleFactor = GetTextCombineScaleFactor(f);
     if (scaleFactor != 1.0f) {
       // Setup matrix to compress text for text-combine-upright if
       // necessary. This is done here because we want selection be
       // compressed at the same time as text.
       gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
-      gfxMatrix mat = ctx->CurrentMatrix()
+      gfxMatrix mat = aCtx->CurrentMatrix()
         .Translate(pt).Scale(scaleFactor, 1.0).Translate(-pt);
-      ctx->SetMatrix(mat);
-    }
-  }
-  nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
+      aCtx->SetMatrix(mat);
+    }
+  }
+  nsTextFrame::PaintTextParams params(aCtx);
   params.framePt = gfxPoint(framePt.x, framePt.y);
   params.dirtyRect = extraVisible;
 
   if (aBuilder->IsForGenerateGlyphMask()) {
     MOZ_ASSERT(!aBuilder->IsForPaintingSelectionBG());
     params.state = nsTextFrame::PaintTextParams::GenerateTextMask;
   } else if (aBuilder->IsForPaintingSelectionBG()) {
     params.state = nsTextFrame::PaintTextParams::PaintTextBGColor;
--- a/layout/mathml/nsMathMLmtableFrame.cpp
+++ b/layout/mathml/nsMathMLmtableFrame.cpp
@@ -296,17 +296,17 @@ public:
   }
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
   {
     *aSnap = true;
     nsStyleBorder styleBorder = *mFrame->StyleBorder();
     nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
     ApplyBorderToStyle(frame, styleBorder);
-    nsRect bounds = CalculateBounds(styleBorder);
+    nsRect bounds = CalculateBounds(styleBorder).GetBounds();
     nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
     bounds.Inflate(overflow);
     return bounds;
   }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override
   {
     nsStyleBorder styleBorder = *mFrame->StyleBorder();
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -1112,16 +1112,24 @@ public:
   nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap = false) const
   {
     if (aSnap && mSnappingEnabled) {
       return ScaleToNearestPixels(aRect);
     }
     return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
                                       mAppUnitsPerDevPixel);
   }
+  nsIntRegion ScaleToOutsidePixels(const nsRegion& aRegion, bool aSnap = false) const
+  {
+    if (aSnap && mSnappingEnabled) {
+      return ScaleRegionToNearestPixels(aRegion);
+    }
+    return aRegion.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
+                                        mAppUnitsPerDevPixel);
+  }
   nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap = false) const
   {
     if (aSnap && mSnappingEnabled) {
       return ScaleToNearestPixels(aRect);
     }
     return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
                                      mAppUnitsPerDevPixel);
   }
@@ -2774,18 +2782,27 @@ PaintedLayerDataNode::FindPaintedLayerFo
       // will be hoisted out into their own layer.
       // For performance reasons, we check the intersection with the bounds
       // of the event-regions.
       if (!mTree.ContState().IsInInactiveLayer() &&
           (data.mScaledHitRegionBounds.Intersects(aVisibleRect) ||
            data.mScaledMaybeHitRegionBounds.Intersects(aVisibleRect))) {
         break;
       }
+      // If the visible region intersects with the current layer then we
+      // can't possibly use any of the layers below it, so stop the search
+      // now.
+      //
+      // If we're trying to minimize painted layer size and we don't
+      // intersect the current visible region, then make sure we don't
+      // use this painted layer.
       if (visibleRegion.Intersects(aVisibleRect)) {
         break;
+      } else if (gfxPrefs::LayoutSmallerPaintedLayers()) {
+        lowestUsableLayer = nullptr;
       }
     }
     if (lowestUsableLayer) {
       return lowestUsableLayer;
     }
   }
   return mPaintedLayerDataStack.AppendElement(aNewPaintedLayerCallback());
 }
@@ -4319,16 +4336,24 @@ ContainerState::ProcessDisplayItems(nsDi
             SetupMaskLayer(ownLayer, layerClip);
           }
         }
       } else if (item->GetType() == nsDisplayItem::TYPE_MASK) {
         nsDisplayMask* maskItem = static_cast<nsDisplayMask*>(item);
         SetupMaskLayerForCSSMask(ownLayer, maskItem);
       }
 
+      // Convert the visible rect to a region and give the item
+      // a chance to try restrict it further.
+      nsIntRegion itemVisibleRegion = itemVisibleRect;
+      nsRegion tightBounds = item->GetTightBounds(mBuilder, &snap);
+      if (!tightBounds.IsEmpty()) {
+        itemVisibleRegion.AndWith(ScaleToOutsidePixels(tightBounds, snap));
+      }
+
       ContainerLayer* oldContainer = ownLayer->GetParent();
       if (oldContainer && oldContainer != mContainerLayer) {
         oldContainer->RemoveChild(ownLayer);
       }
       NS_ASSERTION(FindIndexOfLayerIn(mNewChildLayers, ownLayer) < 0,
                    "Layer already in list???");
 
       NewLayerEntry* newLayerEntry = mNewChildLayers.AppendElement();
@@ -4357,30 +4382,30 @@ ContainerState::ProcessDisplayItems(nsDi
               item->Frame()->Combines3DTransformWithAncestors() ||
               item->Frame()->HasPerspective()))) {
           // Give untransformed visible region as outer visible region
           // to avoid failure caused by singular transforms.
           newLayerEntry->mUntransformedVisibleRegion = true;
           newLayerEntry->mVisibleRegion =
             item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel);
         } else {
-          newLayerEntry->mVisibleRegion = itemVisibleRect;
+          newLayerEntry->mVisibleRegion = itemVisibleRegion;
         }
         newLayerEntry->mOpaqueRegion = ComputeOpaqueRect(item,
           animatedGeometryRoot, layerClip, aList,
           &newLayerEntry->mHideAllLayersBelow,
           &newLayerEntry->mOpaqueForAnimatedGeometryRootParent);
       } else {
         bool useChildrenVisible =
           itemType == nsDisplayItem::TYPE_TRANSFORM &&
           (item->Frame()->IsPreserve3DLeaf() ||
            item->Frame()->HasPerspective());
         const nsIntRegion &visible = useChildrenVisible ?
           item->GetVisibleRectForChildren().ToOutsidePixels(mAppUnitsPerDevPixel):
-          itemVisibleRect;
+          itemVisibleRegion;
 
         SetOuterVisibleRegionForLayer(ownLayer, visible,
             layerContentsVisibleRect.width >= 0 ? &layerContentsVisibleRect : nullptr,
             useChildrenVisible);
       }
       if (itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER) {
         nsDisplayScrollInfoLayer* scrollItem = static_cast<nsDisplayScrollInfoLayer*>(item);
         newLayerEntry->mOpaqueForAnimatedGeometryRootParent = false;
@@ -4671,16 +4696,23 @@ FrameLayerBuilder::AddPaintedDisplayItem
         tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
         return;
       }
 
       bool snap;
       nsRect visibleRect =
         aItem->GetVisibleRect().Intersect(aItem->GetBounds(mDisplayListBuilder, &snap));
       nsIntRegion rgn = visibleRect.ToOutsidePixels(paintedData->mAppUnitsPerDevPixel);
+
+      // Convert the visible rect to a region and give the item
+      // a chance to try restrict it further.
+      nsRegion tightBounds = aItem->GetTightBounds(mDisplayListBuilder, &snap);
+      if (!tightBounds.IsEmpty()) {
+        rgn.AndWith(tightBounds.ToOutsidePixels(paintedData->mAppUnitsPerDevPixel));
+      }
       SetOuterVisibleRegion(tmpLayer, &rgn);
 
       // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
       // stored in layerBuilder. Manually add it now.
       if (mRetainingManager) {
 #ifdef DEBUG_DISPLAY_ITEM_DATA
         LayerManagerData* parentLmd = static_cast<LayerManagerData*>
           (layer->Manager()->GetUserData(&gLayerManagerUserData));
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -668,24 +668,152 @@ nsCSSRendering::PaintBorder(nsPresContex
 
   nsStyleBorder newStyleBorder(*styleBorder);
 
   NS_FOR_CSS_SIDES(side) {
     nscolor color = aStyleContext->GetVisitedDependentColor(
       nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
     newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
   }
-  DrawResult result =
-    PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
-                               aDirtyRect, aBorderArea, newStyleBorder,
-                               aStyleContext, aFlags, aSkipSides);
-
-  return result;
+  return PaintBorderWithStyleBorder(aPresContext, aRenderingContext, aForFrame,
+                                    aDirtyRect, aBorderArea, newStyleBorder,
+                                    aStyleContext, aFlags, aSkipSides);
+}
+
+Maybe<nsCSSBorderRenderer>
+nsCSSRendering::CreateBorderRenderer(nsPresContext* aPresContext,
+                                     DrawTarget* aDrawTarget,
+                                     nsIFrame* aForFrame,
+                                     const nsRect& aDirtyRect,
+                                     const nsRect& aBorderArea,
+                                     nsStyleContext* aStyleContext,
+                                     Sides aSkipSides)
+{
+  nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
+  const nsStyleBorder *styleBorder = aStyleContext->StyleBorder();
+  // Don't check RelevantLinkVisited here, since we want to take the
+  // same amount of time whether or not it's true.
+  if (!styleIfVisited) {
+    return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
+                                               aForFrame, aDirtyRect,
+                                               aBorderArea, *styleBorder,
+                                               aStyleContext, aSkipSides);
+  }
+
+  nsStyleBorder newStyleBorder(*styleBorder);
+
+  NS_FOR_CSS_SIDES(side) {
+    nscolor color = aStyleContext->GetVisitedDependentColor(
+      nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[side]);
+    newStyleBorder.mBorderColor[side] = StyleComplexColor::FromColor(color);
+  }
+  return CreateBorderRendererWithStyleBorder(aPresContext, aDrawTarget,
+                                             aForFrame, aDirtyRect, aBorderArea,
+                                             newStyleBorder, aStyleContext,
+                                             aSkipSides);
 }
 
+nsCSSBorderRenderer
+ConstructBorderRenderer(nsPresContext* aPresContext,
+                        nsStyleContext* aStyleContext,
+                        DrawTarget* aDrawTarget,
+                        nsIFrame* aForFrame,
+                        const nsRect& aDirtyRect,
+                        const nsRect& aBorderArea,
+                        const nsStyleBorder& aStyleBorder,
+                        Sides aSkipSides,
+                        bool* aNeedsClip)
+{
+  nsMargin border = aStyleBorder.GetComputedBorder();
+
+  // Get our style context's color struct.
+  const nsStyleColor* ourColor = aStyleContext->StyleColor();
+
+  // In NavQuirks mode we want to use the parent's context as a starting point
+  // for determining the background color.
+  bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
+  nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame(aForFrame, quirks);
+  nsStyleContext* bgContext = bgFrame->StyleContext();
+  nscolor bgColor =
+    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
+
+  // Compute the outermost boundary of the area that might be painted.
+  // Same coordinate space as aBorderArea & aBGClipRect.
+  nsRect joinedBorderArea =
+    ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
+  RectCornerRadii bgRadii;
+  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
+
+  PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x, joinedBorderArea.y,
+     joinedBorderArea.width, joinedBorderArea.height);
+
+  // start drawing
+  if (::IsBoxDecorationSlice(aStyleBorder)) {
+    if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
+      // No need for a clip, just skip the sides we don't want.
+      border.ApplySkipSides(aSkipSides);
+    } else {
+      // We're drawing borders around the joined continuation boxes so we need
+      // to clip that to the slice that we want for this frame.
+      *aNeedsClip = true;
+    }
+  } else {
+    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
+               "Should use aBorderArea for box-decoration-break:clone");
+    MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty() ||
+               IS_TRUE_OVERFLOW_CONTAINER(aForFrame),
+               "Should not skip sides for box-decoration-break:clone except "
+               "::first-letter/line continuations or other frame types that "
+               "don't have borders but those shouldn't reach this point. "
+               "Overflow containers do reach this point though.");
+    border.ApplySkipSides(aSkipSides);
+  }
+
+  // Convert to dev pixels.
+  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
+  Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, twipsPerPixel);
+  Float borderWidths[4] = { Float(border.top / twipsPerPixel),
+                                   Float(border.right / twipsPerPixel),
+                                   Float(border.bottom / twipsPerPixel),
+                                   Float(border.left / twipsPerPixel) };
+  Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
+
+  uint8_t borderStyles[4];
+  nscolor borderColors[4];
+  nsBorderColors* compositeColors[4];
+
+  // pull out styles, colors, composite colors
+  NS_FOR_CSS_SIDES (i) {
+    borderStyles[i] = aStyleBorder.GetBorderStyle(i);
+    borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
+    aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
+  }
+
+  PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
+
+  nsIDocument* document = nullptr;
+  nsIContent* content = aForFrame->GetContent();
+  if (content) {
+    document = content->OwnerDoc();
+  }
+
+  return nsCSSBorderRenderer(aPresContext,
+                             document,
+                             aDrawTarget,
+                             dirtyRect,
+                             joinedBorderAreaPx,
+                             borderStyles,
+                             borderWidths,
+                             bgRadii,
+                             borderColors,
+                             compositeColors,
+                             bgColor);
+}
+
+
 DrawResult
 nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext,
                                            nsRenderingContext& aRenderingContext,
                                            nsIFrame* aForFrame,
                                            const nsRect& aDirtyRect,
                                            const nsRect& aBorderArea,
                                            const nsStyleBorder& aStyleBorder,
                                            nsStyleContext* aStyleContext,
@@ -719,129 +847,99 @@ nsCSSRendering::PaintBorderWithStyleBord
 
   // If we had a border-image, but it wasn't loaded, then we should return
   // DrawResult::NOT_READY; we'll want to try again if we do a paint with sync
   // decoding enabled.
   if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
     result = DrawResult::NOT_READY;
   }
 
-  // Get our style context's color struct.
-  const nsStyleColor* ourColor = aStyleContext->StyleColor();
-
-  // In NavQuirks mode we want to use the parent's context as a starting point
-  // for determining the background color.
-  bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
-  nsIFrame* bgFrame = FindNonTransparentBackgroundFrame(aForFrame, quirks);
-  nsStyleContext* bgContext = bgFrame->StyleContext();
-  nscolor bgColor =
-    bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
-
   nsMargin border = aStyleBorder.GetComputedBorder();
   if (0 == border.left && 0 == border.right &&
       0 == border.top  && 0 == border.bottom) {
     // Empty border area
     return result;
   }
 
-  // Compute the outermost boundary of the area that might be painted.
-  // Same coordinate space as aBorderArea & aBGClipRect.
-  nsRect joinedBorderArea =
-    ::BoxDecorationRectForBorder(aForFrame, aBorderArea, aSkipSides, &aStyleBorder);
-  RectCornerRadii bgRadii;
-  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
-
-  PrintAsFormatString(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x, joinedBorderArea.y,
-     joinedBorderArea.width, joinedBorderArea.height);
-
-  // start drawing
-  bool needToPopClip = false;
-
-  if (::IsBoxDecorationSlice(aStyleBorder)) {
-    if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
-      // No need for a clip, just skip the sides we don't want.
-      border.ApplySkipSides(aSkipSides);
-    } else {
-      // We're drawing borders around the joined continuation boxes so we need
-      // to clip that to the slice that we want for this frame.
-      aDrawTarget.PushClipRect(
+  bool needsClip = false;
+  nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
+                                                   aStyleContext,
+                                                   &aDrawTarget,
+                                                   aForFrame,
+                                                   aDirtyRect,
+                                                   aBorderArea,
+                                                   aStyleBorder,
+                                                   aSkipSides,
+                                                   &needsClip);
+
+  if (needsClip) {
+    aDrawTarget.PushClipRect(
         NSRectToSnappedRect(aBorderArea,
                             aForFrame->PresContext()->AppUnitsPerDevPixel(),
                             aDrawTarget));
-      needToPopClip = true;
-    }
-  } else {
-    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
-               "Should use aBorderArea for box-decoration-break:clone");
-    MOZ_ASSERT(aForFrame->GetSkipSides().IsEmpty() ||
-               IS_TRUE_OVERFLOW_CONTAINER(aForFrame),
-               "Should not skip sides for box-decoration-break:clone except "
-               "::first-letter/line continuations or other frame types that "
-               "don't have borders but those shouldn't reach this point. "
-               "Overflow containers do reach this point though.");
-    border.ApplySkipSides(aSkipSides);
-  }
-
-  // Convert to dev pixels.
-  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-  Rect joinedBorderAreaPx = NSRectToRect(joinedBorderArea, twipsPerPixel);
-  Float borderWidths[4] = { Float(border.top / twipsPerPixel),
-                            Float(border.right / twipsPerPixel),
-                            Float(border.bottom / twipsPerPixel),
-                            Float(border.left / twipsPerPixel) };
-  Rect dirtyRect = NSRectToRect(aDirtyRect, twipsPerPixel);
-
-  uint8_t borderStyles[4];
-  nscolor borderColors[4];
-  nsBorderColors *compositeColors[4];
-
-  // pull out styles, colors, composite colors
-  NS_FOR_CSS_SIDES (i) {
-    borderStyles[i] = aStyleBorder.GetBorderStyle(i);
-    borderColors[i] = ourColor->CalcComplexColor(aStyleBorder.mBorderColor[i]);
-    aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
-  }
-
-  PrintAsFormatString(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
-  //PrintAsFormatString ("bgRadii: %f %f %f %f\n", bgRadii[0], bgRadii[1], bgRadii[2], bgRadii[3]);
-
-#if 0
-  // this will draw a transparent red backround underneath the border area
-  ColorPattern color(ToDeviceColor(Color(1.f, 0.f, 0.f, 0.5f)));
-  aDrawTarget.FillRect(joinedBorderAreaPx, color);
-#endif
-
-  nsIDocument* document = nullptr;
-  nsIContent* content = aForFrame->GetContent();
-  if (content) {
-    document = content->OwnerDoc();
-  }
-
-  nsCSSBorderRenderer br(aPresContext,
-                         document,
-                         &aDrawTarget,
-                         dirtyRect,
-                         joinedBorderAreaPx,
-                         borderStyles,
-                         borderWidths,
-                         bgRadii,
-                         borderColors,
-                         compositeColors,
-                         bgColor);
+  }
+
   br.DrawBorders();
 
-  if (needToPopClip) {
+  if (needsClip) {
     aDrawTarget.PopClip();
   }
 
   PrintAsStringNewline();
 
   return result;
 }
 
+Maybe<nsCSSBorderRenderer>
+nsCSSRendering::CreateBorderRendererWithStyleBorder(nsPresContext* aPresContext,
+                                                    DrawTarget* aDrawTarget,
+                                                    nsIFrame* aForFrame,
+                                                    const nsRect& aDirtyRect,
+                                                    const nsRect& aBorderArea,
+                                                    const nsStyleBorder& aStyleBorder,
+                                                    nsStyleContext* aStyleContext,
+                                                    Sides aSkipSides)
+{
+  const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
+  if (displayData->mAppearance) {
+    nsITheme *theme = aPresContext->GetTheme();
+    if (theme &&
+        theme->ThemeSupportsWidget(aPresContext, aForFrame,
+                                   displayData->mAppearance)) {
+      return Nothing();
+    }
+  }
+
+  if (aStyleBorder.mBorderImageSource.GetType() != eStyleImageType_Null) {
+    return Nothing();
+  }
+
+  nsMargin border = aStyleBorder.GetComputedBorder();
+  if (0 == border.left && 0 == border.right &&
+      0 == border.top  && 0 == border.bottom) {
+    // Empty border area
+    return Nothing();
+  }
+
+  bool needsClip = false;
+  nsCSSBorderRenderer br = ConstructBorderRenderer(aPresContext,
+                                                   aStyleContext,
+                                                   aDrawTarget,
+                                                   aForFrame,
+                                                   aDirtyRect,
+                                                   aBorderArea,
+                                                   aStyleBorder,
+                                                   aSkipSides,
+                                                   &needsClip);
+  if (needsClip) {
+    return Nothing();
+  }
+  return Some(br);
+}
+
 static nsRect
 GetOutlineInnerRect(nsIFrame* aFrame)
 {
   nsRect* savedOutlineInnerRect =
     aFrame->Properties().Get(nsIFrame::OutlineInnerRectProperty());
   if (savedOutlineInnerRect)
     return *savedOutlineInnerRect;
   NS_NOTREACHED("we should have saved a frame property");
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -12,16 +12,17 @@
 #include "gfxContext.h"
 #include "imgIContainer.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsLayoutUtils.h"
 #include "nsStyleStruct.h"
 #include "nsIFrame.h"
+#include "nsCSSRenderingBorders.h"
 
 class gfxDrawable;
 class nsStyleContext;
 class nsPresContext;
 class nsRenderingContext;
 
 namespace mozilla {
 
@@ -411,16 +412,35 @@ struct nsCSSRendering {
                                                nsIFrame* aForFrame,
                                                const nsRect& aDirtyRect,
                                                const nsRect& aBorderArea,
                                                const nsStyleBorder& aBorderStyle,
                                                nsStyleContext* aStyleContext,
                                                mozilla::PaintBorderFlags aFlags,
                                                Sides aSkipSides = Sides());
 
+  static mozilla::Maybe<nsCSSBorderRenderer>
+  CreateBorderRenderer(nsPresContext* aPresContext,
+                       DrawTarget* aDrawTarget,
+                       nsIFrame* aForFrame,
+                       const nsRect& aDirtyRect,
+                       const nsRect& aBorderArea,
+                       nsStyleContext* aStyleContext,
+                       Sides aSkipSides = Sides());
+
+  static mozilla::Maybe<nsCSSBorderRenderer>
+  CreateBorderRendererWithStyleBorder(nsPresContext* aPresContext,
+                                      DrawTarget* aDrawTarget,
+                                      nsIFrame* aForFrame,
+                                      const nsRect& aDirtyRect,
+                                      const nsRect& aBorderArea,
+                                      const nsStyleBorder& aBorderStyle,
+                                      nsStyleContext* aStyleContext,
+                                      Sides aSkipSides = Sides());
+
 
   /**
    * Render the outline for an element using css rendering rules
    * for borders.
    */
   static void PaintOutline(nsPresContext* aPresContext,
                           nsRenderingContext& aRenderingContext,
                           nsIFrame* aForFrame,
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -101,18 +101,18 @@ CheckFourFloatsEqual(const Float *vals, 
           vals[3] == k);
 }
 
 static bool
 IsZeroSize(const Size& sz) {
   return sz.width == 0.0 || sz.height == 0.0;
 }
 
-static bool
-AllCornersZeroSize(const RectCornerRadii& corners) {
+/* static */ bool
+nsCSSBorderRenderer::AllCornersZeroSize(const RectCornerRadii& corners) {
   return IsZeroSize(corners[NS_CORNER_TOP_LEFT]) &&
     IsZeroSize(corners[NS_CORNER_TOP_RIGHT]) &&
     IsZeroSize(corners[NS_CORNER_BOTTOM_RIGHT]) &&
     IsZeroSize(corners[NS_CORNER_BOTTOM_LEFT]);
 }
 
 static mozilla::Side
 GetHorizontalSide(mozilla::css::Corner aCorner)
@@ -177,26 +177,27 @@ nsCSSBorderRenderer::nsCSSBorderRenderer
                                          const nscolor* aBorderColors,
                                          nsBorderColors* const* aCompositeColors,
                                          nscolor aBackgroundColor)
   : mPresContext(aPresContext),
     mDocument(aDocument),
     mDrawTarget(aDrawTarget),
     mDirtyRect(aDirtyRect),
     mOuterRect(aOuterRect),
-    mBorderStyles(aBorderStyles),
-    mBorderWidths(aBorderWidths),
     mBorderRadii(aBorderRadii),
-    mBorderColors(aBorderColors),
-    mCompositeColors(aCompositeColors),
     mBackgroundColor(aBackgroundColor)
 {
-  if (!mCompositeColors) {
+  PodCopy(mBorderStyles, aBorderStyles, 4);
+  PodCopy(mBorderWidths, aBorderWidths, 4);
+  PodCopy(mBorderColors, aBorderColors, 4);
+  if (aCompositeColors) {
+    PodCopy(mCompositeColors, aCompositeColors, 4);
+  } else {
     static nsBorderColors * const noColors[4] = { nullptr };
-    mCompositeColors = &noColors[0];
+    PodCopy(mCompositeColors, noColors, 4);
   }
 
   mInnerRect = mOuterRect;
   mInnerRect.Deflate(
       Margin(mBorderStyles[0] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[0] : 0,
              mBorderStyles[1] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[1] : 0,
              mBorderStyles[2] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[2] : 0,
              mBorderStyles[3] != NS_STYLE_BORDER_STYLE_NONE ? mBorderWidths[3] : 0));
@@ -266,17 +267,17 @@ ComputeBorderCornerDimensions(const Floa
                               const RectCornerRadii& aRadii,
                               RectCornerRadii* aDimsRet)
 {
   Float leftWidth = aBorderWidths[eSideLeft];
   Float topWidth = aBorderWidths[eSideTop];
   Float rightWidth = aBorderWidths[eSideRight];
   Float bottomWidth = aBorderWidths[eSideBottom];
 
-  if (AllCornersZeroSize(aRadii)) {
+  if (nsCSSBorderRenderer::AllCornersZeroSize(aRadii)) {
     // These will always be in pixel units from CSS
     (*aDimsRet)[C_TL] = Size(leftWidth, topWidth);
     (*aDimsRet)[C_TR] = Size(rightWidth, topWidth);
     (*aDimsRet)[C_BR] = Size(rightWidth, bottomWidth);
     (*aDimsRet)[C_BL] = Size(leftWidth, bottomWidth);
   } else {
     // Always round up to whole pixels for the corners; it's safe to
     // make the corners bigger than necessary, and this way we ensure
--- a/layout/painting/nsCSSRenderingBorders.h
+++ b/layout/painting/nsCSSRenderingBorders.h
@@ -11,19 +11,21 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/BezierUtils.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/RefPtr.h"
 #include "nsColor.h"
 #include "nsCOMPtr.h"
 #include "nsStyleConsts.h"
+#include "nsStyleStruct.h"
 #include "nsPresContext.h"
 
 struct nsBorderColors;
+class nsDisplayBorder;
 
 namespace mozilla {
 namespace gfx {
 class GradientStops;
 } // namespace gfx
 } // namespace mozilla
 
 // define this to enable a bunch of debug dump info
@@ -70,16 +72,18 @@ class nsCSSBorderRenderer final
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Path Path;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::Rect Rect;
   typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
   typedef mozilla::gfx::StrokeOptions StrokeOptions;
 
+  friend class nsDisplayBorder;
+
 public:
 
   nsCSSBorderRenderer(nsPresContext* aPresContext,
                       const nsIDocument* aDocument,
                       DrawTarget* aDrawTarget,
                       const Rect& aDirtyRect,
                       Rect& aOuterRect,
                       const uint8_t* aBorderStyles,
@@ -100,40 +104,42 @@ public:
   // Given aRadii as the border radii for a rectangle, compute the
   // appropriate radii for another rectangle *outside* that rectangle
   // by increasing the radii, except keeping sharp corners sharp.
   // Used for spread box-shadows
   static void ComputeOuterRadii(const RectCornerRadii& aRadii,
                                 const Float* aBorderSizes,
                                 RectCornerRadii* aOuterRadiiRet);
 
+  static bool AllCornersZeroSize(const RectCornerRadii& corners);
+
 private:
 
   RectCornerRadii mBorderCornerDimensions;
 
   // Target document to report warning
   nsPresContext* mPresContext;
   const nsIDocument* mDocument;
 
   // destination DrawTarget and dirty rect
   DrawTarget* mDrawTarget;
-  const Rect& mDirtyRect;
+  const Rect mDirtyRect;
 
   // the rectangle of the outside and the inside of the border
   Rect mOuterRect;
   Rect mInnerRect;
 
   // the style and size of the border
-  const uint8_t* mBorderStyles;
-  const Float* mBorderWidths;
+  uint8_t mBorderStyles[4];
+  Float mBorderWidths[4];
   RectCornerRadii mBorderRadii;
 
   // colors
-  const nscolor* mBorderColors;
-  nsBorderColors* const* mCompositeColors;
+  nscolor mBorderColors[4];
+  nsBorderColors* mCompositeColors[4];
 
   // the background color
   nscolor mBackgroundColor;
 
   // calculated values
   bool mOneUnitBorder;
   bool mNoBorderRadius;
   bool mAvoidStroke;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -31,16 +31,17 @@
 #include "gfxMatrix.h"
 #include "gfxPrefs.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsIFrameInlines.h"
 #include "nsThemeConstants.h"
+#include "BorderConsts.h"
 #include "LayerTreeInvalidation.h"
 
 #include "imgIContainer.h"
 #include "BasicLayers.h"
 #include "nsBoxFrame.h"
 #include "nsViewportFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "nsSVGEffects.h"
@@ -2901,16 +2902,20 @@ nsDisplayBackgroundImage::GetImage()
   nsCOMPtr<imgIContainer> image = mImage;
   return image.forget();
 }
 
 nsDisplayBackgroundImage::ImageLayerization
 nsDisplayBackgroundImage::ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder,
                                                LayerManager* aManager)
 {
+  if (ForceActiveLayers()) {
+    return WHENEVER_POSSIBLE;
+  }
+
   nsIFrame* backgroundStyleFrame = nsCSSRendering::FindBackgroundStyleFrame(mFrame);
   if (ActiveLayerTracker::IsBackgroundPositionAnimated(aBuilder,
                                                        backgroundStyleFrame)) {
     return WHENEVER_POSSIBLE;
   }
 
   if (nsLayoutUtils::AnimatedImageLayersEnabled() && mBackgroundStyle) {
     const nsStyleImageLayers::Layer &layer = mBackgroundStyle->mImage.mLayers[mLayer];
@@ -3556,16 +3561,54 @@ nsDisplayBackgroundColor::ApplyOpacity(n
 }
 
 bool
 nsDisplayBackgroundColor::CanApplyOpacity() const
 {
   return true;
 }
 
+LayerState
+nsDisplayBackgroundColor::GetLayerState(nsDisplayListBuilder* aBuilder,
+                                        LayerManager* aManager,
+                                        const ContainerLayerParameters& aParameters)
+{
+  uint8_t clip = mBackgroundStyle->mImage.mLayers[0].mClip;
+  if (!ForceActiveLayers() || clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) {
+    return LAYER_NONE;
+  }
+  return LAYER_ACTIVE;
+}
+
+already_AddRefed<Layer>
+nsDisplayBackgroundColor::BuildLayer(nsDisplayListBuilder* aBuilder,
+                                     LayerManager* aManager,
+                                     const ContainerLayerParameters& aContainerParameters)
+{
+  if (mColor == Color()) {
+    return nullptr;
+  }
+
+  RefPtr<ColorLayer> layer = static_cast<ColorLayer*>
+    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (!layer) {
+    layer = aManager->CreateColorLayer();
+    if (!layer)
+      return nullptr;
+  }
+  layer->SetColor(mColor);
+
+  int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
+  layer->SetBounds(mBackgroundRect.ToNearestPixels(appUnitsPerDevPixel));
+  layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
+                                                      aContainerParameters.mOffset.y, 0));
+
+  return layer.forget();
+}
+
 void
 nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder,
                                 nsRenderingContext* aCtx)
 {
   if (mColor == Color()) {
     return;
   }
 
@@ -3958,17 +4001,17 @@ nsDisplayCaret::Paint(nsDisplayListBuild
   mCaret->PaintCaret(*aCtx->GetDrawTarget(), mFrame, ToReferenceFrame());
 }
 
 nsDisplayBorder::nsDisplayBorder(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
   : nsDisplayItem(aBuilder, aFrame)
 {
   MOZ_COUNT_CTOR(nsDisplayBorder);
 
-  mBounds = CalculateBounds(*mFrame->StyleBorder());
+  mBounds = CalculateBounds(*mFrame->StyleBorder()).GetBounds();
 }
 
 bool
 nsDisplayBorder::IsInvisibleInRect(const nsRect& aRect)
 {
   nsRect paddingRect = mFrame->GetPaddingRect() - mFrame->GetPosition() +
     ToReferenceFrame();
   const nsStyleBorder *styleBorder;
@@ -4010,16 +4053,90 @@ nsDisplayBorder::ComputeInvalidationRegi
   }
 
   if (aBuilder->ShouldSyncDecodeImages() &&
       geometry->ShouldInvalidateToSyncDecodeImages()) {
     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 }
 
+LayerState
+nsDisplayBorder::GetLayerState(nsDisplayListBuilder* aBuilder,
+                               LayerManager* aManager,
+                               const ContainerLayerParameters& aParameters)
+{
+  if (!gfxPrefs::LayersAllowBorderLayers()) {
+    return LAYER_NONE;
+  }
+
+  nsPoint offset = ToReferenceFrame();
+  Maybe<nsCSSBorderRenderer> br =
+    nsCSSRendering::CreateBorderRenderer(mFrame->PresContext(),
+                                         nullptr,
+                                         mFrame,
+                                         nsRect(),
+                                         nsRect(offset, mFrame->GetSize()),
+                                         mFrame->StyleContext(),
+                                         mFrame->GetSkipSides());
+  if (!br) {
+    return LAYER_NONE;
+  }
+
+  bool hasCompositeColors;
+  if (!br->AllBordersSolid(&hasCompositeColors) || hasCompositeColors) {
+    return LAYER_NONE;
+  }
+
+  // We don't support this yet as we don't copy the values to
+  // the layer, and BasicBorderLayer doesn't support it yet.
+  if (!br->mNoBorderRadius) {
+    return LAYER_NONE;
+  }
+
+  // We copy these values correctly to the layer, but BasicBorderLayer
+  // won't render them
+  if (!br->AreBorderSideFinalStylesSame(SIDE_BITS_ALL) ||
+      !br->AllBordersSameWidth()) {
+    return LAYER_NONE;
+  }
+
+  NS_FOR_CSS_SIDES(i) {
+    if (br->mBorderStyles[i] == NS_STYLE_BORDER_STYLE_SOLID) {
+      mColors[i] = ToDeviceColor(br->mBorderColors[i]);
+      mWidths[i] = br->mBorderWidths[i];
+    } else {
+      mWidths[i] = 0;
+    }
+  }
+
+  mRect = ViewAs<LayerPixel>(br->mOuterRect);
+  return LAYER_INACTIVE;
+}
+
+already_AddRefed<Layer>
+nsDisplayBorder::BuildLayer(nsDisplayListBuilder* aBuilder,
+                            LayerManager* aManager,
+                            const ContainerLayerParameters& aContainerParameters)
+{
+  RefPtr<BorderLayer> layer = static_cast<BorderLayer*>
+    (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this));
+  if (!layer) {
+    layer = aManager->CreateBorderLayer();
+    if (!layer)
+      return nullptr;
+  }
+  layer->SetRect(mRect);
+  layer->SetCornerRadii({ LayerSize(), LayerSize(), LayerSize(), LayerSize() });
+  layer->SetColors(mColors);
+  layer->SetWidths(mWidths);
+  layer->SetBaseTransform(gfx::Matrix4x4::Translation(aContainerParameters.mOffset.x,
+                                                      aContainerParameters.mOffset.y, 0));
+  return layer.forget();
+}
+
 void
 nsDisplayBorder::Paint(nsDisplayListBuilder* aBuilder,
                        nsRenderingContext* aCtx) {
   nsPoint offset = ToReferenceFrame();
 
   PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
                          ? PaintBorderFlags::SYNC_DECODE_IMAGES
                          : PaintBorderFlags();
@@ -4037,56 +4154,56 @@ nsDisplayBorder::Paint(nsDisplayListBuil
 
 nsRect
 nsDisplayBorder::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
 {
   *aSnap = true;
   return mBounds;
 }
 
-nsRect
+nsRegion
 nsDisplayBorder::CalculateBounds(const nsStyleBorder& aStyleBorder)
 {
   nsRect borderBounds(ToReferenceFrame(), mFrame->GetSize());
   if (aStyleBorder.IsBorderImageLoaded()) {
     borderBounds.Inflate(aStyleBorder.GetImageOutset());
     return borderBounds;
   } else {
     nsMargin border = aStyleBorder.GetComputedBorder();
-    nsRect result;
+    nsRegion result;
     if (border.top > 0) {
       result = nsRect(borderBounds.X(), borderBounds.Y(), borderBounds.Width(), border.top);
     }
     if (border.right > 0) {
-      result.UnionRect(result, nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height()));
+      result.OrWith(nsRect(borderBounds.XMost() - border.right, borderBounds.Y(), border.right, borderBounds.Height()));
     }
     if (border.bottom > 0) {
-      result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom));
+      result.OrWith(nsRect(borderBounds.X(), borderBounds.YMost() - border.bottom, borderBounds.Width(), border.bottom));
     }
     if (border.left > 0) {
-      result.UnionRect(result, nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height()));
+      result.OrWith(nsRect(borderBounds.X(), borderBounds.Y(), border.left, borderBounds.Height()));
     }
 
     nscoord radii[8];
     if (mFrame->GetBorderRadii(radii)) {
       if (border.left > 0 || border.top > 0) {
         nsSize cornerSize(radii[NS_CORNER_TOP_LEFT_X], radii[NS_CORNER_TOP_LEFT_Y]);
-        result.UnionRect(result, nsRect(borderBounds.TopLeft(), cornerSize));
+        result.OrWith(nsRect(borderBounds.TopLeft(), cornerSize));
       }
       if (border.top > 0 || border.right > 0) {
         nsSize cornerSize(radii[NS_CORNER_TOP_RIGHT_X], radii[NS_CORNER_TOP_RIGHT_Y]);
-        result.UnionRect(result, nsRect(borderBounds.TopRight() - nsPoint(cornerSize.width, 0), cornerSize));
+        result.OrWith(nsRect(borderBounds.TopRight() - nsPoint(cornerSize.width, 0), cornerSize));
       }
       if (border.right > 0 || border.bottom > 0) {
         nsSize cornerSize(radii[NS_CORNER_BOTTOM_RIGHT_X], radii[NS_CORNER_BOTTOM_RIGHT_Y]);
-        result.UnionRect(result, nsRect(borderBounds.BottomRight() - nsPoint(cornerSize.width, cornerSize.height), cornerSize));
+        result.OrWith(nsRect(borderBounds.BottomRight() - nsPoint(cornerSize.width, cornerSize.height), cornerSize));
       }
       if (border.bottom > 0 || border.left > 0) {
         nsSize cornerSize(radii[NS_CORNER_BOTTOM_LEFT_X], radii[NS_CORNER_BOTTOM_LEFT_Y]);
-        result.UnionRect(result, nsRect(borderBounds.BottomLeft() - nsPoint(0, cornerSize.height), cornerSize));
+        result.OrWith(nsRect(borderBounds.BottomLeft() - nsPoint(0, cornerSize.height), cornerSize));
       }
     }
 
     return result;
   }
 }
 
 // Given a region, compute a conservative approximation to it as a list
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -9,16 +9,17 @@
  * structures that represent things to be painted (ordered in z-order),
  * used during painting and hit testing
  */
 
 #ifndef NSDISPLAYLIST_H_
 #define NSDISPLAYLIST_H_
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/Maybe.h"
 #include "nsCOMPtr.h"
 #include "nsContainerFrame.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "plarena.h"
@@ -1421,16 +1422,23 @@ public:
    * @return a rectangle relative to aBuilder->ReferenceFrame() that
    * contains the area drawn by this display item
    */
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
   {
     *aSnap = false;
     return nsRect(ToReferenceFrame(), Frame()->GetSize());
   }
+
+  virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
+  {
+    *aSnap = false;
+    return nsRegion();
+  }
+
   /**
    * Returns true if nothing will be rendered inside aRect, false if uncertain.
    * aRect is assumed to be contained in this item's bounds.
    */
   virtual bool IsInvisibleInRect(const nsRect& aRect)
   {
     return false;
   }
@@ -2552,27 +2560,44 @@ public:
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayBorder() {
     MOZ_COUNT_DTOR(nsDisplayBorder);
   }
 #endif
 
   virtual bool IsInvisibleInRect(const nsRect& aRect) override;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override;
+
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
   NS_DISPLAY_DECL_NAME("Border", TYPE_BORDER)
   
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
 
+  virtual nsRegion GetTightBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
+  {
+    *aSnap = true;
+    return CalculateBounds(*mFrame->StyleBorder());
+  }
+
 protected:
-  nsRect CalculateBounds(const nsStyleBorder& aStyleBorder);
+  nsRegion CalculateBounds(const nsStyleBorder& aStyleBorder);
+
+  mozilla::Array<mozilla::gfx::Color, 4> mColors;
+  mozilla::Array<mozilla::LayerCoord, 4> mWidths;
+  mozilla::LayerRect mRect;
 
   nsRect mBounds;
 };
 
 /**
  * A simple display item that just renders a solid color across the
  * specified bounds. For canvas frames (in the CSS sense) we split off the
  * drawing of the background color into this class (from nsDisplayBackground
@@ -2902,17 +2927,23 @@ public:
                            const nsStyleBackground* aBackgroundStyle,
                            nscolor aColor)
     : nsDisplayItem(aBuilder, aFrame)
     , mBackgroundRect(aBackgroundRect)
     , mBackgroundStyle(aBackgroundStyle)
     , mColor(Color::FromABGR(aColor))
   { }
 
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override;
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
 
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) override;
   virtual mozilla::Maybe<nscolor> IsUniform(nsDisplayListBuilder* aBuilder) override;
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) override;
 
   virtual void ApplyOpacity(nsDisplayListBuilder* aBuilder,
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -4646,16 +4646,20 @@ StyleAnimationValue::ExtractComputedValu
     }
     case eStyleAnimType_Discrete: {
       if (aProperty == eCSSProperty_visibility) {
         aComputedValue.SetIntValue(
           static_cast<const nsStyleVisibility*>(styleStruct)->mVisible,
           eUnit_Visibility);
         return true;
       }
+      if (aStyleContext->StyleSource().IsServoComputedValues()) {
+        NS_ERROR("stylo: extracting discretely animated values not supported");
+        return false;
+      }
       auto cssValue = MakeUnique<nsCSSValue>(eCSSUnit_Unset);
       aStyleContext->RuleNode()->GetDiscretelyAnimatedCSSValue(aProperty,
                                                                cssValue.get());
       aComputedValue.SetAndAdoptCSSValueValue(cssValue.release(),
                                               eUnit_DiscreteCSSValue);
       return true;
     }
     case eStyleAnimType_None:
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1272,30 +1272,56 @@ nsTableFrame::DisplayGenericTablePart(ns
     // before everything else (cells and their blocks)
     separatedCollection.BorderBackground()->Sort(CompareByTablePartRank, nullptr);
     separatedCollection.MoveTo(aLists);
   }
 
   aFrame->DisplayOutline(aBuilder, aLists);
 }
 
+static inline bool FrameHasBorderOrBackground(nsTableFrame* tableFrame, nsIFrame* f)
+{
+  if (!f->StyleVisibility()->IsVisible()) {
+    return false;
+  }
+  if (f->StyleBorder()->HasBorder()) {
+    return true;
+  }
+  if (!f->StyleBackground()->IsTransparent() ||
+      f->StyleDisplay()->mAppearance) {
+    
+    nsTableCellFrame *cellFrame = do_QueryFrame(f);
+    // We could also return false here if the current frame is the root
+    // of a pseudo stacking context
+    if (cellFrame && !tableFrame->IsBorderCollapse()) {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
 static bool
-AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd)
+AnyTablePartHasBorderOrBackground(nsTableFrame* aTableFrame,
+                                  nsIFrame* aStart,
+                                  nsIFrame* aEnd)
 {
   for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
     NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
 
-    if (FrameHasBorderOrBackground(f))
+    if (FrameHasBorderOrBackground(aTableFrame, f))
       return true;
 
     nsTableCellFrame *cellFrame = do_QueryFrame(f);
     if (cellFrame)
       continue;
 
-    if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr))
+    if (AnyTablePartHasBorderOrBackground(aTableFrame,
+                                          f->PrincipalChildList().FirstChild(),
+                                          nullptr))
       return true;
   }
 
   return false;
 }
 
 static void
 UpdateItemForColGroupBackgrounds(nsDisplayTableItem* item,
@@ -1332,18 +1358,18 @@ nsTableFrame::BuildDisplayList(nsDisplay
     }
 
     // This background is created if any of the table parts are visible,
     // or if we're doing event handling (since DisplayGenericTablePart
     // needs the item for the |sortEventBackgrounds|-dependent code).
     // Specific visibility decisions are delegated to the table background
     // painter, which handles borders and backgrounds for the table.
     if (aBuilder->IsForEventDelivery() ||
-        AnyTablePartHasBorderOrBackground(this, GetNextSibling()) ||
-        AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) {
+        AnyTablePartHasBorderOrBackground(this, this, GetNextSibling()) ||
+        AnyTablePartHasBorderOrBackground(this, mColGroups.FirstChild(), nullptr)) {
       item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this,
           deflate != nsMargin(0, 0, 0, 0));
       aLists.BorderBackground()->AppendNewToTop(item);
     }
   }
   DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
   if (item) {
     UpdateItemForColGroupBackgrounds(item, mColGroups);
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -33,23 +33,16 @@ struct TableReflowInput;
 
 struct BCPropertyData;
 
 static inline bool IS_TABLE_CELL(nsIAtom* frameType) {
   return nsGkAtoms::tableCellFrame == frameType ||
     nsGkAtoms::bcTableCellFrame == frameType;
 }
 
-static inline bool FrameHasBorderOrBackground(nsIFrame* f) {
-  return (f->StyleVisibility()->IsVisible() &&
-          (!f->StyleBackground()->IsTransparent() ||
-           f->StyleDisplay()->mAppearance ||
-           f->StyleBorder()->HasBorder()));
-}
-
 class nsDisplayTableItem : public nsDisplayItem
 {
 public:
   nsDisplayTableItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                      bool aDrawsBackground = true) :
       nsDisplayItem(aBuilder, aFrame),
       mPartHasFixedBackground(false),
       mDrawsBackground(aDrawsBackground) {}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/AudioDecoder.cpp
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstdint>
-#include <limits>
-
-#include "AudioDecoder.h"
-#include "ClearKeyDecryptionManager.h"
-#include "ClearKeyUtils.h"
-#include "gmp-task-utils.h"
-
-using namespace wmf;
-
-AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI)
-  : mHostAPI(aHostAPI)
-  , mCallback(nullptr)
-  , mWorkerThread(nullptr)
-  , mMutex(nullptr)
-  , mNumInputTasks(0)
-  , mHasShutdown(false)
-{
-  // We drop the ref in DecodingComplete().
-  AddRef();
-}
-
-AudioDecoder::~AudioDecoder()
-{
-  if (mMutex) {
-    mMutex->Destroy();
-  }
-}
-
-void
-AudioDecoder::InitDecode(const GMPAudioCodec& aConfig,
-                         GMPAudioDecoderCallback* aCallback)
-{
-  mCallback = aCallback;
-  assert(mCallback);
-  mDecoder = new WMFAACDecoder();
-  HRESULT hr = mDecoder->Init(aConfig.mChannelCount,
-                              aConfig.mSamplesPerSecond,
-                              (BYTE*)aConfig.mExtraData,
-                              aConfig.mExtraDataLen);
-  LOG("[%p] AudioDecoder::InitializeAudioDecoder() hr=0x%x\n", this, hr);
-  if (FAILED(hr)) {
-    mCallback->Error(GMPGenericErr);
-    return;
-  }
-  auto err = GetPlatform()->createmutex(&mMutex);
-  if (GMP_FAILED(err)) {
-    mCallback->Error(GMPGenericErr);
-    return;
-  }
-}
-
-void
-AudioDecoder::EnsureWorker()
-{
-  if (!mWorkerThread) {
-    GetPlatform()->createthread(&mWorkerThread);
-    if (!mWorkerThread) {
-      mCallback->Error(GMPAllocErr);
-      return;
-    }
-  }
-}
-
-void
-AudioDecoder::Decode(GMPAudioSamples* aInput)
-{
-  EnsureWorker();
-  {
-    AutoLock lock(mMutex);
-    mNumInputTasks++;
-  }
-  mWorkerThread->Post(WrapTaskRefCounted(this,
-                                         &AudioDecoder::DecodeTask,
-                                         aInput));
-}
-
-void
-AudioDecoder::DecodeTask(GMPAudioSamples* aInput)
-{
-  HRESULT hr;
-
-  {
-    AutoLock lock(mMutex);
-    mNumInputTasks--;
-    assert(mNumInputTasks >= 0);
-  }
-
-  if (!aInput || !mHostAPI || !mDecoder) {
-    LOG("Decode job not set up correctly!");
-    return;
-  }
-
-  const uint8_t* inBuffer = aInput->Buffer();
-  if (!inBuffer) {
-    LOG("No buffer for encoded samples!\n");
-    return;
-  }
-
-  const GMPEncryptedBufferMetadata* crypto = aInput->GetDecryptionData();
-  std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
-  if (crypto) {
-    // Plugin host should have set up its decryptor/key sessions
-    // before trying to decode!
-    GMPErr rv =
-      ClearKeyDecryptionManager::Get()->Decrypt(buffer, CryptoMetaData(crypto));
-
-    if (GMP_FAILED(rv)) {
-      CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId());
-      MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
-      return;
-    }
-  }
-
-  hr = mDecoder->Input(&buffer[0],
-                       buffer.size(),
-                       aInput->TimeStamp());
-
-  // We must delete the input sample!
-  GetPlatform()->runonmainthread(WrapTask(aInput, &GMPAudioSamples::Destroy));
-
-  SAMPLE_LOG("AudioDecoder::DecodeTask() Input ret hr=0x%x\n", hr);
-  if (FAILED(hr)) {
-    LOG("AudioDecoder::DecodeTask() decode failed ret=0x%x%s\n",
-        hr,
-        ((hr == MF_E_NOTACCEPTING) ? " (MF_E_NOTACCEPTING)" : ""));
-    return;
-  }
-
-  while (hr == S_OK) {
-    CComPtr<IMFSample> output;
-    hr = mDecoder->Output(&output);
-    SAMPLE_LOG("AudioDecoder::DecodeTask() output ret=0x%x\n", hr);
-    if (hr == S_OK) {
-      ReturnOutput(output);
-    }
-    if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
-      AutoLock lock(mMutex);
-      if (mNumInputTasks == 0) {
-        // We have run all input tasks. We *must* notify Gecko so that it will
-        // send us more data.
-        MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
-      }
-    } else if (FAILED(hr)) {
-      LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
-    }
-  }
-}
-
-void
-AudioDecoder::ReturnOutput(IMFSample* aSample)
-{
-  SAMPLE_LOG("[%p] AudioDecoder::ReturnOutput()\n", this);
-  assert(aSample);
-
-  HRESULT hr;
-
-  GMPAudioSamples* samples = nullptr;
-  mHostAPI->CreateSamples(kGMPAudioIS16Samples, &samples);
-  if (!samples) {
-    LOG("Failed to create i420 frame!\n");
-    return;
-  }
-
-  hr = MFToGMPSample(aSample, samples);
-  if (FAILED(hr)) {
-    samples->Destroy();
-    LOG("Failed to prepare output sample!");
-    return;
-  }
-  ENSURE(SUCCEEDED(hr), /*void*/);
-
-  MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
-}
-
-HRESULT
-AudioDecoder::MFToGMPSample(IMFSample* aInput,
-                            GMPAudioSamples* aOutput)
-{
-  ENSURE(aInput != nullptr, E_POINTER);
-  ENSURE(aOutput != nullptr, E_POINTER);
-
-  HRESULT hr;
-  CComPtr<IMFMediaBuffer> mediaBuffer;
-
-  hr = aInput->ConvertToContiguousBuffer(&mediaBuffer);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we don't need to free it.
-  DWORD maxLength = 0, currentLength = 0;
-  hr = mediaBuffer->Lock(&data, &maxLength, &currentLength);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  auto err = aOutput->SetBufferSize(currentLength);
-  ENSURE(GMP_SUCCEEDED(err), E_FAIL);
-
-  memcpy(aOutput->Buffer(), data, currentLength);
-
-  mediaBuffer->Unlock();
-
-  LONGLONG hns = 0;
-  hr = aInput->GetSampleTime(&hns);
-  ENSURE(SUCCEEDED(hr), hr);
-  aOutput->SetTimeStamp(HNsToUsecs(hns));
-  aOutput->SetChannels(mDecoder->Channels());
-  aOutput->SetRate(mDecoder->Rate());
-
-  return S_OK;
-}
-
-void
-AudioDecoder::Reset()
-{
-  if (mDecoder) {
-    mDecoder->Reset();
-  }
-  if (mCallback) {
-    mCallback->ResetComplete();
-  }
-}
-
-void
-AudioDecoder::DrainTask()
-{
-  mDecoder->Drain();
-
-  // Return any pending output.
-  HRESULT hr = S_OK;
-  while (hr == S_OK) {
-    CComPtr<IMFSample> output;
-    hr = mDecoder->Output(&output);
-    SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
-    if (hr == S_OK) {
-      ReturnOutput(output);
-    }
-  }
-  MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
-}
-
-void
-AudioDecoder::Drain()
-{
-  if (!mDecoder) {
-    return;
-  }
-  EnsureWorker();
-  mWorkerThread->Post(WrapTaskRefCounted(this,
-                                         &AudioDecoder::DrainTask));
-}
-
-void
-AudioDecoder::DecodingComplete()
-{
-  if (mWorkerThread) {
-    mWorkerThread->Join();
-  }
-  mHasShutdown = true;
-
-  // Release the reference we added in the constructor. There may be
-  // WrapRefCounted tasks that also hold references to us, and keep
-  // us alive a little longer.
-  Release();
-}
-
-void
-AudioDecoder::MaybeRunOnMainThread(GMPTask* aTask)
-{
-  class MaybeRunTask : public GMPTask
-  {
-  public:
-    MaybeRunTask(AudioDecoder* aDecoder, GMPTask* aTask)
-      : mDecoder(aDecoder), mTask(aTask)
-    { }
-
-    virtual void Run(void) {
-      if (mDecoder->HasShutdown()) {
-        CK_LOGD("Trying to dispatch to main thread after AudioDecoder has shut down");
-        return;
-      }
-
-      mTask->Run();
-    }
-
-    virtual void Destroy()
-    {
-      mTask->Destroy();
-      delete this;
-    }
-
-  private:
-    RefPtr<AudioDecoder> mDecoder;
-    GMPTask* mTask;
-  };
-
-  GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
-}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/AudioDecoder.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2015, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __AudioDecoder_h__
-#define __AudioDecoder_h__
-
-#include "gmp-audio-decode.h"
-#include "gmp-audio-host.h"
-#include "gmp-task-utils.h"
-#include "WMFAACDecoder.h"
-#include "RefCounted.h"
-
-#include "mfobjects.h"
-
-class AudioDecoder : public GMPAudioDecoder
-                   , public RefCounted
-{
-public:
-  AudioDecoder(GMPAudioHost *aHostAPI);
-
-  virtual void InitDecode(const GMPAudioCodec& aCodecSettings,
-                          GMPAudioDecoderCallback* aCallback) override;
-
-  virtual void Decode(GMPAudioSamples* aEncodedSamples);
-
-  virtual void Reset() override;
-
-  virtual void Drain() override;
-
-  virtual void DecodingComplete() override;
-
-  bool HasShutdown() { return mHasShutdown; }
-
-private:
-  virtual ~AudioDecoder();
-
-  void EnsureWorker();
-
-  void DecodeTask(GMPAudioSamples* aEncodedSamples);
-  void DrainTask();
-
-  void ReturnOutput(IMFSample* aSample);
-
-  HRESULT MFToGMPSample(IMFSample* aSample,
-                        GMPAudioSamples* aAudioFrame);
-
-  void MaybeRunOnMainThread(GMPTask* aTask);
-
-  GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete
-  GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
-  GMPThread* mWorkerThread;
-  GMPMutex* mMutex;
-  wmf::AutoPtr<wmf::WMFAACDecoder> mDecoder;
-
-  int32_t mNumInputTasks;
-
-  bool mHasShutdown;
-};
-
-#endif // __AudioDecoder_h__
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/WMFAACDecoder.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright 2013, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "WMFAACDecoder.h"
-#include <algorithm>
-#include <stdint.h>
-#include <vector>
-
-using std::vector;
-
-namespace wmf {
-
-WMFAACDecoder::WMFAACDecoder()
-  : mDecoder(nullptr)
-  , mChannels(0)
-  , mRate(0)
-{
-  memset(&mInputStreamInfo, 0, sizeof(MFT_INPUT_STREAM_INFO));
-  memset(&mOutputStreamInfo, 0, sizeof(MFT_OUTPUT_STREAM_INFO));
-}
-
-WMFAACDecoder::~WMFAACDecoder()
-{
-  Reset();
-}
-
-HRESULT
-AACAudioSpecificConfigToUserData(BYTE* aAudioSpecConfig, UINT32 aConfigLength,
-                                 BYTE** aOutUserData, UINT32* aOutUserDataLength)
-{
-  // For MFAudioFormat_AAC, MF_MT_USER_DATA
-  // Contains the portion of the HEAACWAVEINFO structure that appears
-  // after the WAVEFORMATEX structure (that is, after the wfx member).
-  // This is followed by the AudioSpecificConfig() data, as defined
-  // by ISO/IEC 14496-3.
-  // ...
-  // The length of the AudioSpecificConfig() data is 2 bytes for AAC-LC
-  // or HE-AAC with implicit signaling of SBR/PS. It is more than 2 bytes
-  // for HE-AAC with explicit signaling of SBR/PS.
-  //
-  // The value of audioObjectType as defined in AudioSpecificConfig()
-  // must be 2, indicating AAC-LC. The value of extensionAudioObjectType
-  // must be 5 for SBR or 29 for PS.
-  //
-  // See:
-  // http://msdn.microsoft.com/en-us/library/windows/desktop/dd742784%28v=vs.85%29.aspx
-  //
-  // HEAACWAVEINFO structure:
-  //    typedef struct heaacwaveinfo_tag {
-  //      WAVEFORMATEX wfx;
-  //      WORD         wPayloadType;
-  //      WORD         wAudioProfileLevelIndication;
-  //      WORD         wStructType;
-  //      WORD         wReserved1;
-  //      DWORD        dwReserved2;
-  //    }
-  const UINT32 heeInfoLen = 4 * sizeof(WORD) + sizeof(DWORD);
-  BYTE heeInfo[heeInfoLen] = {0};
-  WORD* w = (WORD*)heeInfo;
-  w[0] = 0x0; // Payload type raw AAC
-  w[1] = 0; // Profile level unspecified
-
-  const UINT32 len =  heeInfoLen + aConfigLength;
-  BYTE* data = new BYTE[len];
-  memcpy(data, heeInfo, heeInfoLen);
-  memcpy(data+heeInfoLen, aAudioSpecConfig, aConfigLength);
-  *aOutUserData = data;
-  *aOutUserDataLength = len;
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::Init(int32_t aChannelCount,
-                    int32_t aSampleRate,
-                    BYTE* aAACAudioSpecificConfig,
-                    UINT32 aAudioConfigLength)
-{
-  HRESULT hr;
-
-  // AAC decoder is in msauddecmft on Win8, and msmpeg2adec in earlier versions.
-  hr = CreateMFT(CLSID_CMSAACDecMFT,
-                 WMFDecoderDllNameFor(AAC),
-                 mDecoder);
-  if (FAILED(hr)) {
-    hr = CreateMFT(CLSID_CMSAACDecMFT,
-                   WMFDecoderDllNameFor(AAC),
-                   mDecoder);
-    if (FAILED(hr)) {
-      LOG("Failed to create AAC decoder\n");
-      return E_FAIL;
-    }
-  }
-
-  BYTE* userData = nullptr;
-  UINT32 userDataLength;
-  hr = AACAudioSpecificConfigToUserData(aAACAudioSpecificConfig,
-                                        aAudioConfigLength,
-                                        &userData,
-                                        &userDataLength);
-  ENSURE(SUCCEEDED(hr), hr);
-  hr = SetDecoderInputType(aChannelCount, aSampleRate, userData, userDataLength);
-  delete userData;
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = SetDecoderOutputType();
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = SendMFTMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = mDecoder->GetInputStreamInfo(0, &mInputStreamInfo);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = mDecoder->GetOutputStreamInfo(0, &mOutputStreamInfo);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::SetDecoderInputType(int32_t aChannelCount,
-                                   int32_t aSampleRate,
-                                   BYTE* aUserData,
-                                   UINT32 aUserDataLength)
-{
-  HRESULT hr;
-
-  CComPtr<IMFMediaType> type;
-  hr = MFCreateMediaType(&type);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  mRate = aSampleRate;
-  hr = type->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, mRate);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  mChannels = aChannelCount;
-  hr = type->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, mChannels);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = type->SetBlob(MF_MT_USER_DATA, aUserData, aUserDataLength);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = mDecoder->SetInputType(0, type, 0);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::SetDecoderOutputType()
-{
-  HRESULT hr;
-
-  CComPtr<IMFMediaType> type;
-
-  UINT32 typeIndex = 0;
-  while (type = nullptr, SUCCEEDED(mDecoder->GetOutputAvailableType(0, typeIndex++, &type))) {
-    GUID subtype;
-    hr = type->GetGUID(MF_MT_SUBTYPE, &subtype);
-    if (FAILED(hr)) {
-      continue;
-    }
-    if (subtype == MFAudioFormat_PCM) {
-      hr = mDecoder->SetOutputType(0, type, 0);
-      ENSURE(SUCCEEDED(hr), hr);
-
-      hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mChannels);
-      ENSURE(SUCCEEDED(hr), hr);
-
-      hr = type->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &mRate);
-      ENSURE(SUCCEEDED(hr), hr);
-
-      return S_OK;
-    }
-  }
-
-  return E_FAIL;
-}
-
-HRESULT
-WMFAACDecoder::SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData)
-{
-  ENSURE(mDecoder != nullptr, E_POINTER);
-  HRESULT hr = mDecoder->ProcessMessage(aMsg, aData);
-  ENSURE(SUCCEEDED(hr), hr);
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::CreateInputSample(const uint8_t* aData,
-                                 uint32_t aDataSize,
-                                 Microseconds aTimestamp,
-                                 IMFSample** aOutSample)
-{
-  HRESULT hr;
-  CComPtr<IMFSample> sample = nullptr;
-  hr = MFCreateSample(&sample);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  CComPtr<IMFMediaBuffer> buffer = nullptr;
-  int32_t bufferSize = std::max<uint32_t>(uint32_t(mInputStreamInfo.cbSize), aDataSize);
-  UINT32 alignment = (mInputStreamInfo.cbAlignment > 1) ? mInputStreamInfo.cbAlignment - 1 : 0;
-  hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  DWORD maxLength = 0;
-  DWORD currentLength = 0;
-  BYTE* dst = nullptr;
-  hr = buffer->Lock(&dst, &maxLength, &currentLength);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  // Copy data into sample's buffer.
-  memcpy(dst, aData, aDataSize);
-
-  hr = buffer->Unlock();
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = buffer->SetCurrentLength(aDataSize);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = sample->AddBuffer(buffer);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = sample->SetSampleTime(UsecsToHNs(aTimestamp));
-  ENSURE(SUCCEEDED(hr), hr);
-
-  *aOutSample = sample.Detach();
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::CreateOutputSample(IMFSample** aOutSample)
-{
-  HRESULT hr;
-  CComPtr<IMFSample> sample = nullptr;
-  hr = MFCreateSample(&sample);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  CComPtr<IMFMediaBuffer> buffer = nullptr;
-  int32_t bufferSize = mOutputStreamInfo.cbSize;
-  UINT32 alignment = (mOutputStreamInfo.cbAlignment > 1) ? mOutputStreamInfo.cbAlignment - 1 : 0;
-  hr = MFCreateAlignedMemoryBuffer(bufferSize, alignment, &buffer);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  hr = sample->AddBuffer(buffer);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  *aOutSample = sample.Detach();
-
-  return S_OK;
-}
-
-
-HRESULT
-WMFAACDecoder::GetOutputSample(IMFSample** aOutSample)
-{
-  HRESULT hr;
-  // We allocate samples for MFT output.
-  MFT_OUTPUT_DATA_BUFFER output = {0};
-
-  CComPtr<IMFSample> sample = nullptr;
-  hr = CreateOutputSample(&sample);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  output.pSample = sample;
-
-  DWORD status = 0;
-  hr = mDecoder->ProcessOutput(0, 1, &output, &status);
-  CComPtr<IMFCollection> events = output.pEvents; // Ensure this is released.
-
-  if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
-    // Type change. Probably geometric apperature change.
-    hr = SetDecoderOutputType();
-    ENSURE(SUCCEEDED(hr), hr);
-
-    return GetOutputSample(aOutSample);
-  } else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT || !sample) {
-    return MF_E_TRANSFORM_NEED_MORE_INPUT;
-  }
-  // Treat other errors as fatal.
-  ENSURE(SUCCEEDED(hr), hr);
-
-  assert(sample);
-
-  *aOutSample = sample.Detach();
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::Input(const uint8_t* aData,
-                     uint32_t aDataSize,
-                     Microseconds aTimestamp)
-{
-  CComPtr<IMFSample> input = nullptr;
-  HRESULT hr = CreateInputSample(aData, aDataSize, aTimestamp, &input);
-  ENSURE(SUCCEEDED(hr) && input!=nullptr, hr);
-
-  hr = mDecoder->ProcessInput(0, input, 0);
-  if (hr == MF_E_NOTACCEPTING) {
-    // MFT *already* has enough data to produce a sample. Retrieve it.
-    LOG("ProcessInput returned MF_E_NOTACCEPTING\n");
-    return MF_E_NOTACCEPTING;
-  }
-  ENSURE(SUCCEEDED(hr), hr);
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::Output(IMFSample** aOutput)
-{
-  CComPtr<IMFSample> outputSample = nullptr;
-  HRESULT hr = GetOutputSample(&outputSample);
-  if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
-    return MF_E_TRANSFORM_NEED_MORE_INPUT;
-  }
-  // Treat other errors as fatal.
-  ENSURE(SUCCEEDED(hr) && outputSample, hr);
-
-  *aOutput = outputSample.Detach();
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::Reset()
-{
-  HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  return S_OK;
-}
-
-HRESULT
-WMFAACDecoder::Drain()
-{
-  HRESULT hr = SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0);
-  ENSURE(SUCCEEDED(hr), hr);
-
-  return S_OK;
-}
-
-}
deleted file mode 100644
--- a/media/gmp-clearkey/0.1/WMFAACDecoder.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2013, Mozilla Foundation and contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#if !defined(WMFAACDecoder_h_)
-#define WMFAACDecoder_h_
-
-#include "WMFUtils.h"
-
-namespace wmf {
-
-class WMFAACDecoder {
-public:
-  WMFAACDecoder();
-  ~WMFAACDecoder();
-
-  HRESULT Init(int32_t aChannelCount,
-               int32_t aSampleRate,
-               BYTE* aUserData,
-               UINT32 aUserDataLength);
-
-  HRESULT Input(const uint8_t* aData,
-                uint32_t aDataSize,
-                Microseconds aTimestamp);
-
-  HRESULT Output(IMFSample** aOutput);
-
-  HRESULT Reset();
-
-  HRESULT Drain();
-
-  UINT32 Channels() const { return mChannels; }
-  UINT32 Rate() const { return mRate; }
-
-private:
-
-  HRESULT GetOutputSample(IMFSample** aOutSample);
-  HRESULT CreateOutputSample(IMFSample** aOutSample);
-  HRESULT CreateInputSample(const uint8_t* aData,
-                            uint32_t aDataSize,
-                            Microseconds aTimestamp,
-                            IMFSample** aOutSample);
-
-  HRESULT SetDecoderInputType(int32_t aChannelCount,
-                              int32_t aSampleRate,
-                              BYTE* aUserData,
-                              UINT32 aUserDataLength);
-  HRESULT SetDecoderOutputType();
-  HRESULT SendMFTMessage(MFT_MESSAGE_TYPE aMsg, UINT32 aData);
-
-  MFT_INPUT_STREAM_INFO mInputStreamInfo;
-  MFT_OUTPUT_STREAM_INFO mOutputStreamInfo;
-
-  CComPtr<IMFTransform> mDecoder;
-
-  UINT32 mChannels;
-  UINT32 mRate;
-};
-
-}
-
-#endif
--- a/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
+++ b/media/gmp-clearkey/0.1/WMFH264Decoder.cpp
@@ -32,23 +32,23 @@ WMFH264Decoder::~WMFH264Decoder()
 }
 
 HRESULT
 WMFH264Decoder::Init(int32_t aCoreCount)
 {
   HRESULT hr;
 
   hr = CreateMFT(__uuidof(CMSH264DecoderMFT),
-                 WMFDecoderDllNameFor(H264),
+                 WMFDecoderDllName(),
                  mDecoder);
   if (FAILED(hr)) {
     // Windows 7 Enterprise Server N (which is what Mozilla's mochitests run
     // on) need a different CLSID to instantiate the H.264 decoder.
     hr = CreateMFT(CLSID_CMSH264DecMFT,
-                   WMFDecoderDllNameFor(H264),
+                   WMFDecoderDllName(),
                    mDecoder);
   }
   ENSURE(SUCCEEDED(hr), hr);
 
   CComPtr<IMFAttributes> attr;
   hr = mDecoder->GetAttributes(&attr);
   ENSURE(SUCCEEDED(hr), hr);
   hr = attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
--- a/media/gmp-clearkey/0.1/WMFUtils.cpp
+++ b/media/gmp-clearkey/0.1/WMFUtils.cpp
@@ -31,22 +31,16 @@ void LOG(const char* format, ...)
 {
 #ifdef WMF_DECODER_LOG
   va_list args;
   va_start(args, format);
   vprintf(format, args);
 #endif
 }
 
-#ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
-// Some SDK versions don't define the AAC decoder CLSID.
-// {32D186A7-218F-4C75-8876-DD77273A8999}
-DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
-#endif
-
 DEFINE_GUID(CLSID_CMSH264DecMFT, 0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D);
 
 namespace wmf {
 
 
 #define MFPLAT_FUNC(_func, _dllname) \
   decltype(::_func)* _func;
 #include "WMFSymbols.h"
@@ -70,51 +64,37 @@ LinkMfplat()
 #include "WMFSymbols.h"
 #undef MFPLAT_FUNC
     sInitOk = true;
   }
   return sInitOk;
 }
 
 const char*
-WMFDecoderDllNameFor(CodecType aCodec)
+WMFDecoderDllName()
 {
-  if (aCodec == H264) {
-    // For H.264 decoding, we need msmpeg2vdec.dll on Win 7 & 8,
-    // and mfh264dec.dll on Vista.
-    if (IsWindows7OrGreater()) {
-      return "msmpeg2vdec.dll";
-    } else {
-      return "mfh264dec.dll";
-    }
-  } else if (aCodec == AAC) {
-    // For AAC decoding, we need to use msauddecmft.dll on Win8,
-    // msmpeg2adec.dll on Win7, and msheaacdec.dll on Vista.
-    if (IsWindows8OrGreater()) {
-      return "msauddecmft.dll";
-    } else if (IsWindows7OrGreater()) {
-      return "msmpeg2adec.dll";
-    } else {
-      return "mfheaacdec.dll";
-    }
-  } else {
-    return "";
+  // For H.264 decoding, we need msmpeg2vdec.dll on Win 7 & 8,
+  // and mfh264dec.dll on Vista.
+  if (IsWindows7OrGreater()) {
+    return "msmpeg2vdec.dll";
+  }
+  else {
+    return "mfh264dec.dll";
   }
 }
 
 
 bool
 EnsureLibs()
 {
   static bool sInitDone = false;
   static bool sInitOk = false;
   if (!sInitDone) {
     sInitOk = LinkMfplat() &&
-              !!GetModuleHandleA(WMFDecoderDllNameFor(AAC)) &&
-              !!GetModuleHandleA(WMFDecoderDllNameFor(H264));
+              !!GetModuleHandleA(WMFDecoderDllName());
     sInitDone = true;
   }
   return sInitOk;
 }
 
 int32_t
 MFOffsetToInt32(const MFOffset& aOffset)
 {
--- a/media/gmp-clearkey/0.1/WMFUtils.h
+++ b/media/gmp-clearkey/0.1/WMFUtils.h
@@ -245,24 +245,19 @@ inline uint32_t MicrosecondsToRTPTime(Mi
 
 void dump(const uint8_t* data, uint32_t len, const char* filename);
 
 HRESULT
 CreateMFT(const CLSID& clsid,
           const char* aDllName,
           CComPtr<IMFTransform>& aOutMFT);
 
-enum CodecType {
-  H264,
-  AAC,
-};
-
-// Returns the name of the DLL that is needed to decode H.264 or AAC on
+// Returns the name of the DLL that is needed to decode H.264 on
 // the given windows version we're running on.
-const char* WMFDecoderDllNameFor(CodecType aCodec);
+const char* WMFDecoderDllName();
 
 // Returns the maximum number of threads we want WMF to use for decoding
 // given the number of logical processors available.
 int32_t GetNumThreads(int32_t aCoreCount);
 
 } // namespace wmf
 
 #endif // __WMFUtils_h__
--- a/media/gmp-clearkey/0.1/clearkey.info.in
+++ b/media/gmp-clearkey/0.1/clearkey.info.in
@@ -1,10 +1,10 @@
 Name: clearkey
 Description: ClearKey Gecko Media Plugin
 Version: 1
 #ifdef ENABLE_WMF
-APIs: eme-decrypt-v9[org.w3.clearkey], decode-audio[aac:org.w3.clearkey], decode-video[h264:org.w3.clearkey]
+APIs: eme-decrypt-v9[org.w3.clearkey], decode-video[h264:org.w3.clearkey]
 Libraries: dxva2.dll, d3d9.dll, msmpeg2vdec.dll, msmpeg2adec.dll, MSAudDecMFT.dll, evr.dll, mfheaacdec.dll, mfh264dec.dll, mfplat.dll
 #else
 APIs: eme-decrypt-v9[org.w3.clearkey]
 Libraries:
 #endif
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -21,17 +21,16 @@
 #include "ClearKeyAsyncShutdown.h"
 #include "ClearKeySessionManager.h"
 #include "gmp-api/gmp-async-shutdown.h"
 #include "gmp-api/gmp-decryption.h"
 #include "gmp-api/gmp-platform.h"
 
 #if defined(ENABLE_WMF)
 #include "WMFUtils.h"
-#include "AudioDecoder.h"
 #include "VideoDecoder.h"
 #endif
 
 #if defined(WIN32)
 #define GMP_EXPORT __declspec(dllexport)
 #else
 #define GMP_EXPORT __attribute__((visibility("default")))
 #endif
@@ -57,20 +56,17 @@ GMPGetAPI(const char* aApiName, void* aH
 {
   CK_LOGD("ClearKey GMPGetAPI |%s|", aApiName);
   assert(!*aPluginAPI);
 
   if (!strcmp(aApiName, GMP_API_DECRYPTOR)) {
     *aPluginAPI = new ClearKeySessionManager();
   }
 #if defined(ENABLE_WMF)
-  else if (!strcmp(aApiName, GMP_API_AUDIO_DECODER) &&
-           wmf::EnsureLibs()) {
-    *aPluginAPI = new AudioDecoder(static_cast<GMPAudioHost*>(aHostAPI));
-  } else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER) &&
+ else if (!strcmp(aApiName, GMP_API_VIDEO_DECODER) &&
              wmf::EnsureLibs()) {
     *aPluginAPI = new VideoDecoder(static_cast<GMPVideoHost*>(aHostAPI));
   }
 #endif
   else if (!strcmp(aApiName, GMP_API_ASYNC_SHUTDOWN)) {
     *aPluginAPI = new ClearKeyAsyncShutdown(static_cast<GMPAsyncShutdownHost*> (aHostAPI));
   } else {
     CK_LOGE("GMPGetAPI couldn't resolve API name |%s|\n", aApiName);
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -24,19 +24,17 @@ UNIFIED_SOURCES += [
 
 SOURCES += [
     'openaes/oaes_lib.c',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     UNIFIED_SOURCES += [
         'AnnexB.cpp',
-        'AudioDecoder.cpp',
         'VideoDecoder.cpp',
-        'WMFAACDecoder.cpp',
         'WMFH264Decoder.cpp',
     ]
 
     SOURCES += [
         'WMFUtils.cpp',
     ]
 
     OS_LIBS += [
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 13f167c23954527d693508d11dd7706b2b774729.
+The git commit ID used was 051cd8478dd48a18e2e91e5f07837640f40618c1.
--- a/media/libcubeb/gtest/test_audio.cpp
+++ b/media/libcubeb/gtest/test_audio.cpp
@@ -225,17 +225,17 @@ int run_panning_volume_test(int is_float
     delay(400);
     cubeb_stream_stop(stream);
     delay(100);
   }
 
   fprintf(stderr, "Testing: panning\n");
   for(int i=-4;i <= 4; ++i)
   {
-    fprintf(stderr, "Panning: %.2f%%\n", i/4.0f);
+    fprintf(stderr, "Panning: %.2f\n", i/4.0f);
 
     cubeb_stream_set_panning(stream, i/4.0f);
     cubeb_stream_start(stream);
     delay(400);
     cubeb_stream_stop(stream);
     delay(100);
   }
 
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -22,16 +22,17 @@
   X(pa_channel_map_can_balance)                 \
   X(pa_channel_map_init_auto)                   \
   X(pa_context_connect)                         \
   X(pa_context_disconnect)                      \
   X(pa_context_drain)                           \
   X(pa_context_get_server_info)                 \
   X(pa_context_get_sink_info_by_name)           \
   X(pa_context_get_sink_info_list)              \
+  X(pa_context_get_sink_input_info)             \
   X(pa_context_get_source_info_list)            \
   X(pa_context_get_state)                       \
   X(pa_context_new)                             \
   X(pa_context_rttime_new)                      \
   X(pa_context_set_sink_input_volume)           \
   X(pa_context_set_state_callback)              \
   X(pa_context_unref)                           \
   X(pa_cvolume_set)                             \
@@ -994,33 +995,74 @@ pulse_stream_set_volume(cubeb_stream * s
     }
   }
 
   WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   return CUBEB_OK;
 }
 
+struct sink_input_info_result {
+  pa_cvolume * cvol;
+  pa_threaded_mainloop * mainloop;
+};
+
+static void
+sink_input_info_cb(pa_context * c, pa_sink_input_info const * i, int eol, void * u)
+{
+  struct sink_input_info_result * r = u;
+  if (!eol) {
+    *r->cvol = i->volume;
+  }
+  WRAP(pa_threaded_mainloop_signal)(r->mainloop, 0);
+}
+
 static int
-pulse_stream_set_panning(cubeb_stream * stream, float panning)
+pulse_stream_set_panning(cubeb_stream * stm, float panning)
 {
   const pa_channel_map * map;
-  pa_cvolume vol;
+  pa_cvolume cvol;
+  uint32_t index;
+  pa_operation * op;
 
-  if (!stream->output_stream) {
+  if (!stm->output_stream) {
     return CUBEB_ERROR;
   }
 
-  map = WRAP(pa_stream_get_channel_map)(stream->output_stream);
+  WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
 
+  map = WRAP(pa_stream_get_channel_map)(stm->output_stream);
   if (!WRAP(pa_channel_map_can_balance)(map)) {
+    WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
     return CUBEB_ERROR;
   }
 
-  WRAP(pa_cvolume_set_balance)(&vol, map, panning);
+  index = WRAP(pa_stream_get_index)(stm->output_stream);
+
+  struct sink_input_info_result r = { &cvol, stm->context->mainloop };
+  op = WRAP(pa_context_get_sink_input_info)(stm->context->context,
+                                            index,
+                                            sink_input_info_cb,
+                                            &r);
+  if (op) {
+    operation_wait(stm->context, stm->output_stream, op);
+    WRAP(pa_operation_unref)(op);
+  }
+
+  WRAP(pa_cvolume_set_balance)(&cvol, map, panning);
+
+  op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
+                                              index, &cvol, volume_success,
+                                              stm);
+  if (op) {
+    operation_wait(stm->context, stm->output_stream, op);
+    WRAP(pa_operation_unref)(op);
+  }
+
+  WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
 
   return CUBEB_OK;
 }
 
 typedef struct {
   char * default_sink_name;
   char * default_source_name;
 
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1157,17 +1157,17 @@ int wasapi_init(cubeb ** context, char c
   if (ctx->mmcss_module) {
     ctx->set_mm_thread_characteristics =
       (set_mm_thread_characteristics_function) GetProcAddress(
           ctx->mmcss_module, "AvSetMmThreadCharacteristicsA");
     ctx->revert_mm_thread_characteristics =
       (revert_mm_thread_characteristics_function) GetProcAddress(
           ctx->mmcss_module, "AvRevertMmThreadCharacteristics");
     if (!(ctx->set_mm_thread_characteristics && ctx->revert_mm_thread_characteristics)) {
-      LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x", GetLastError());
+      LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %lx", GetLastError());
       FreeLibrary(ctx->mmcss_module);
     }
   } else {
     // This is not a fatal error, but we might end up glitching when
     // the system is under high load.
     LOG("Could not load Avrt.dll");
     ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
     ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
@@ -1784,17 +1784,17 @@ void close_wasapi_stream(cubeb_stream * 
 
   stm->mix_buffer.clear();
 }
 
 void wasapi_stream_destroy(cubeb_stream * stm)
 {
   XASSERT(stm);
 
-  // Only free stm->emergency_bailout if we could not join the thread.
+  // Only free stm->emergency_bailout if we could join the thread.
   // If we could not join the thread, stm->emergency_bailout is true
   // and is still alive until the thread wakes up and exits cleanly.
   if (stop_and_join_render_thread(stm)) {
     delete stm->emergency_bailout.load();
     stm->emergency_bailout = nullptr;
   } else {
     // If we're leaking, it must be that this is true.
     XASSERT(*(stm->emergency_bailout));
--- a/mfbt/Array.h
+++ b/mfbt/Array.h
@@ -41,16 +41,26 @@ public:
   }
 
   const T& operator[](size_t aIndex) const
   {
     MOZ_ASSERT(aIndex < Length);
     return mArr[aIndex];
   }
 
+  bool operator==(const Array<T, Length>& aOther) const
+  {
+    for (size_t i = 0; i < Length; i++) {
+      if (mArr[i] != aOther[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   typedef T*                        iterator;
   typedef const T*                  const_iterator;
   typedef ReverseIterator<T*>       reverse_iterator;
   typedef ReverseIterator<const T*> const_reverse_iterator;
 
   // Methods for range-based for loops.
   iterator begin() { return mArr; }
   const_iterator begin() const { return mArr; }
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoService.java
@@ -145,18 +145,17 @@ public class GeckoService extends Servic
 
         final String profileName = intent.getStringExtra(INTENT_PROFILE_NAME);
         final String profileDir = intent.getStringExtra(INTENT_PROFILE_DIR);
 
         if (profileName == null) {
             throw new IllegalArgumentException("Intent must specify profile.");
         }
 
-        if (!GeckoThread.initWithProfile(profileName != null ? profileName : "",
-                                         profileDir != null ? new File(profileDir) : null)) {
+        if (!GeckoThread.initWithProfile(profileName, profileDir != null ? new File(profileDir) : null)) {
             Log.w(LOGTAG, "Ignoring due to profile mismatch: " +
                           profileName + " [" + profileDir + ']');
 
             final GeckoProfile profile = GeckoThread.getActiveProfile();
             if (profile != null) {
                 Log.w(LOGTAG, "Current profile is " + profile.getName() +
                               " [" + profile.getDir().getAbsolutePath() + ']');
             }
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/GeckoCustomTabsService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/GeckoCustomTabsService.java
@@ -8,25 +8,27 @@ package org.mozilla.gecko.customtabs;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.customtabs.CustomTabsService;
 import android.support.customtabs.CustomTabsSessionToken;
 import android.util.Log;
 
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoService;
+import org.mozilla.gecko.GeckoThread;
 
 import java.util.List;
 
 /**
  * Custom tabs service external, third-party apps connect to.
  */
 public class GeckoCustomTabsService extends CustomTabsService {
     private static final String LOGTAG = "GeckoCustomTabsService";
     private static final boolean DEBUG = false;
+    private static final int MAX_SPECULATIVE_URLS = 50;
 
     @Override
     protected boolean updateVisuals(CustomTabsSessionToken sessionToken, Bundle bundle) {
         Log.v(LOGTAG, "updateVisuals()");
 
         return false;
     }
 
@@ -46,19 +48,45 @@ public class GeckoCustomTabsService exte
         Log.v(LOGTAG, "newSession()");
 
         // Pretend session has been started
         return true;
     }
 
     @Override
     protected boolean mayLaunchUrl(CustomTabsSessionToken sessionToken, Uri uri, Bundle bundle, List<Bundle> list) {
-        Log.v(LOGTAG, "mayLaunchUrl()");
+        if (DEBUG) {
+            Log.v(LOGTAG, "opening speculative connections...");
+        }
+
+        if (uri == null) {
+            return false;
+        }
+
+        GeckoThread.speculativeConnect(uri.toString());
+
+        if (list == null) {
+            return true;
+        }
 
-        return false;
+        for (int i = 0; i < list.size() && i < MAX_SPECULATIVE_URLS; i++) {
+            Bundle listItem = list.get(i);
+            if (listItem == null) {
+                continue;
+            }
+
+            Uri listUri = listItem.getParcelable(KEY_URL);
+            if (listUri == null) {
+                continue;
+            }
+
+            GeckoThread.speculativeConnect(listUri.toString());
+        }
+
+        return true;
     }
 
     @Override
     protected Bundle extraCommand(String commandName, Bundle bundle) {
         Log.v(LOGTAG, "extraCommand()");
 
         return null;
     }
--- a/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/push/PushService.java
@@ -391,20 +391,16 @@ public class PushService implements Bund
                 callback.sendError("Could not unsubscribe from channel: " + channelID);
                 return;
             }
             if ("FxAccountsPush:ReceivedPushMessageToDecode:Response".equals(event)) {
                 FxAccountPushHandler.handleFxAPushMessage(context, message);
                 return;
             }
             if ("History:GetPrePathLastVisitedTimeMilliseconds".equals(event)) {
-                if (callback == null) {
-                    Log.e(LOG_TAG, "callback must not be null in " + event);
-                    return;
-                }
                 final String prePath = message.getString("prePath");
                 if (prePath == null) {
                     callback.sendError("prePath must not be null in " + event);
                     return;
                 }
                 // We're on a background thread, so we can be synchronous.
                 final long millis = BrowserDB.from(geckoProfile).getPrePathLastVisitedTimeMilliseconds(
                         context.getContentResolver(), prePath);
--- a/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
+++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
@@ -91,24 +91,28 @@ nsHttpChunkedDecoder::ParseChunkRemainin
     NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero");
     NS_PRECONDITION(count, "unexpected");
 
     *bytesConsumed = 0;
 
     char *p = static_cast<char *>(memchr(buf, '\n', count));
     if (p) {
         *p = 0;
-        if ((p > buf) && (*(p-1) == '\r')) // eliminate a preceding CR
+        count = p - buf; // new length
+        *bytesConsumed = count + 1; // length + newline
+        if ((p > buf) && (*(p-1) == '\r')) { // eliminate a preceding CR
             *(p-1) = 0;
-        *bytesConsumed = p - buf + 1;
+            count--;
+        }
 
         // make buf point to the full line buffer to parse
         if (!mLineBuf.IsEmpty()) {
-            mLineBuf.Append(buf);
+            mLineBuf.Append(buf, count);
             buf = (char *) mLineBuf.get();
+            count = mLineBuf.Length();
         }
 
         if (mWaitEOF) {
             if (*buf) {
                 LOG(("got trailer: %s\n", buf));
                 // allocate a header array for the trailers on demand
                 if (!mTrailers) {
                     mTrailers = new nsHttpHeaderArray();
index 10ffcf34be1cf4c9efcfe6ee92173955959b74c1..2a6f60a6e3e0e70ca045206ef0c26a02dc14a1a9
GIT binary patch
literal 123904
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
TutD>e$3E`z?=H=Jdj2{$5+QZ0
--- a/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/resource.rc
+++ b/other-licenses/7zstub/src/7zip/Bundles/SFXSetup-moz/resource.rc
@@ -7,12 +7,12 @@ IDI_ICON3  ICON "setup.ico"
 
 STRINGTABLE
 BEGIN
   IDS_EXTRACTION_ERROR_TITLE "Extraction Failed"
   IDS_EXTRACTION_ERROR_MESSAGE "File is corrupt"
   IDS_CANNOT_CREATE_FOLDER "Cannot create folder '{0}'"
   IDS_PROGRESS_EXTRACTING "Extracting"
   IDS_MIN_OS_TITLE "Setup Error"
-  IDS_MIN_OS_TEXT "Microsoft Windows XP SP2 or newer is required."
+  IDS_MIN_OS_TEXT "Microsoft Windows 7 or newer is required."
 END
 
 #include "../../FileManager/Resource/ProgressDialog/resource.rc"
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -313,16 +313,11 @@ class DesktopInstance(GeckoInstance):
 
 
 class NullOutput(object):
     def __call__(self, line):
         pass
 
 
 apps = {
+    'fxdesktop': DesktopInstance,
     'fennec': FennecInstance,
-    'fxdesktop': DesktopInstance,
 }
-
-app_ids = {
-    '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'fennec',
-    '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'fxdesktop',
-}
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1,12 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+import ConfigParser
 import base64
 import datetime
 import json
 import os
 import socket
 import sys
 import time
 import traceback
@@ -597,17 +598,28 @@ class Marionette(object):
             # select instance class for the given app
             try:
                 instance_class = geckoinstance.apps[app]
             except KeyError:
                 msg = 'Application "{0}" unknown (should be one of {1})'
                 raise NotImplementedError(
                     msg.format(app, geckoinstance.apps.keys()))
         else:
-            instance_class = geckoinstance.GeckoInstance
+            try:
+                if not isinstance(self.bin, basestring):
+                    raise TypeError("bin must be a string if app is not specified")
+                config = ConfigParser.RawConfigParser()
+                config.read(os.path.join(os.path.dirname(self.bin),
+                                         'application.ini'))
+                app = config.get('App', 'Name')
+                instance_class = geckoinstance.apps[app.lower()]
+            except (ConfigParser.NoOptionError,
+                    ConfigParser.NoSectionError,
+                    KeyError):
+                instance_class = geckoinstance.GeckoInstance
         return instance_class(host=self.host, port=self.port, bin=self.bin,
                               **instance_args)
 
     @property
     def profile_path(self):
         if self.instance and self.instance.profile:
             return self.instance.profile.profile
 
--- a/testing/marionette/harness/marionette/runner/base.py
+++ b/testing/marionette/harness/marionette/runner/base.py
@@ -17,17 +17,16 @@ import time
 import traceback
 import unittest
 import warnings
 import mozprofile
 
 
 from manifestparser import TestManifest
 from manifestparser.filters import tags
-from marionette_driver.geckoinstance import app_ids
 from marionette_driver.marionette import Marionette
 from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult
 from moztest.results import TestResultCollection, TestResult, relevant_line
 import mozversion
 
 import httpd
 
 
@@ -422,19 +421,16 @@ class BaseMarionetteArguments(ArgumentPa
 
         missing_tests = [path for path in args.tests if not os.path.exists(path)]
         if missing_tests:
             self.error("Test file(s) not found: " + " ".join([path for path in missing_tests]))
 
         if not args.address and not args.binary and not args.emulator:
             self.error('You must specify --binary, or --address, or --emulator')
 
-        if not os.path.isfile(args.binary):
-            self.error('You must specify an existing binary.')
-
         if args.total_chunks is not None and args.this_chunk is None:
             self.error('You must specify which chunk to run.')
 
         if args.this_chunk is not None and args.total_chunks is None:
             self.error('You must specify how many chunks to split the tests into.')
 
         if args.total_chunks is not None:
             if not 1 < args.total_chunks:
@@ -505,39 +501,35 @@ class BaseMarionetteTestRunner(object):
                  symbols_path=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint), this_chunk=1,
                  total_chunks=1,
                  server_root=None, gecko_log=None, result_callbacks=None,
                  prefs=None, test_tags=None,
                  socket_timeout=BaseMarionetteArguments.socket_timeout_default,
                  startup_timeout=None, addons=None, workspace=None,
                  verbose=0, e10s=True, emulator=False, **kwargs):
-
-        self._appinfo = None
-        self._appName = None
-        self._capabilities = None
-        self._filename_pattern = None
-        self._version_info = {}
-
         self.extra_kwargs = kwargs
         self.test_kwargs = deepcopy(kwargs)
         self.address = address
         self.app = app
         self.app_args = app_args or []
         self.bin = binary
         self.emulator = emulator
         self.profile = profile
         self.addons = addons
         self.logger = logger
         self.httpd = None
         self.marionette = None
         self.logdir = logdir
         self.repeat = repeat
         self.symbols_path = symbols_path
         self.socket_timeout = socket_timeout
+        self._capabilities = None
+        self._appinfo = None
+        self._appName = None
         self.shuffle = shuffle
         self.shuffle_seed = shuffle_seed
         self.server_root = server_root
         self.this_chunk = this_chunk
         self.total_chunks = total_chunks
         self.mixin_run_tests = []
         self.manifest_skipped_tests = []
         self.tests = []
@@ -546,24 +538,17 @@ class BaseMarionetteTestRunner(object):
         self.test_tags = test_tags
         self.startup_timeout = startup_timeout
         self.workspace = workspace
         # If no workspace is set, default location for gecko.log is .
         # and default location for profile is TMP
         self.workspace_path = workspace or os.getcwd()
         self.verbose = verbose
         self.e10s = e10s
-
-        # If no application type has been specified try to auto-detect it
-        if not self.app:
-            try:
-                app_id = self.version_info['application_id']
-                self.app = app_ids[app_id]
-            except KeyError:
-                self.logger.warning('Failed to detect the type of application.')
+        self._filename_pattern = None
 
         def gather_debug(test, status):
             rv = {}
             marionette = test._marionette_weakref()
 
             # In the event we're gathering debug without starting a session,
             # skip marionette commands
             if marionette.session is not None:
@@ -673,27 +658,16 @@ class BaseMarionetteTestRunner(object):
     def appName(self):
         if self._appName:
             return self._appName
 
         self._appName = self.capabilities.get('browserName')
         return self._appName
 
     @property
-    def version_info(self):
-        if not self._version_info:
-            try:
-                # TODO: Get version_info in Fennec case
-                self._version_info = mozversion.get_version(binary=self.bin)
-            except Exception:
-                self.logger.warning("Failed to retrieve version information for {}".format(
-                    self.bin))
-        return self._version_info
-
-    @property
     def bin(self):
         return self._bin
 
     @bin.setter
     def bin(self, path):
         """Set binary and reset parts of runner accordingly.
 
         Intended use: to change binary between calls to run_tests
@@ -846,20 +820,25 @@ class BaseMarionetteTestRunner(object):
 
         device_info = None
         if self.marionette.instance and self.emulator:
             try:
                 device_info = self.marionette.instance.runner.device.dm.getInfo()
             except Exception:
                 self.logger.warning('Could not get device info.')
 
+        # TODO: Get version_info in Fennec case
+        version_info = None
+        if self.bin:
+            version_info = mozversion.get_version(binary=self.bin)
+
         self.logger.info("running with e10s: {}".format(self.e10s))
 
         self.logger.suite_start(self.tests,
-                                version_info=self.version_info,
+                                version_info=version_info,
                                 device_info=device_info)
 
         self._log_skipped_tests()
 
         interrupted = None
         try:
             counter = self.repeat
             while counter >= 0:
--- a/testing/web-platform/meta/FileAPI/blob/Blob-constructor.html.ini
+++ b/testing/web-platform/meta/FileAPI/blob/Blob-constructor.html.ini
@@ -13,22 +13,16 @@
     expected: FAIL
 
   [Passing an platform object that supports indexed properties as the blobParts array should work (window with custom toString).]
     expected: FAIL
 
   [The 'endings' property should be ignored.]
     expected: FAIL
 
-  [Passing object "/regex/" (index 4) for options should use the defaults.]
-    expected: FAIL
-
-  [Passing object "/regex/" (index 4) for options should use the defaults (with newlines).]
-    expected: FAIL
-
   [Newlines should not change when endings is 'native'.]
     expected: FAIL
 
   [Blob with type "A"]
     expected: FAIL
 
   [Blob with type "TEXT/HTML"]
     expected: FAIL
--- a/testing/web-platform/tests/proximity/DeviceProximityEvent_tests.js
+++ b/testing/web-platform/tests/proximity/DeviceProximityEvent_tests.js
@@ -123,26 +123,28 @@
         var event = new DeviceProximityEvent('test', null);
         assert_equals(event.value, Infinity);
         assert_equals(event.min, -Infinity);
         assert_equals(event.max, Infinity);
     }, 'eventInitDict argument is null');
 
     test(function() {
         var date = new Date();
-        assert_throws(new TypeError(), function() {
-            new DeviceProximityEvent('test', date);
-        });
+        var event = new DeviceProximityEvent('test', date);
+        assert_equals(event.value, Infinity);
+        assert_equals(event.min, -Infinity);
+        assert_equals(event.max, Infinity);
     }, 'eventInitDict argument is Date object');
 
     test(function() {
         var regexp = /abc/;
-        assert_throws(new TypeError(), function() {
-            new DeviceProximityEvent('test', regexp);
-        });
+        var event = new DeviceProximityEvent('test', regexp);
+        assert_equals(event.value, Infinity);
+        assert_equals(event.min, -Infinity);
+        assert_equals(event.max, Infinity);
     }, 'eventInitDict argument is RegExp object');
 
     test(function() {
         assert_throws(new TypeError(), function() {
             new DeviceProximityEvent('test', false);
         });
     }, 'eventInitDict argument is boolean');
 
--- a/testing/web-platform/tests/proximity/UserProximityEvent_tests.js
+++ b/testing/web-platform/tests/proximity/UserProximityEvent_tests.js
@@ -119,26 +119,24 @@
 
     test(function() {
         var event = new UserProximityEvent('test', null);
         assert_false(event.near);
     }, 'eventInitDict argument is null');
 
     test(function() {
         var date = new Date();
-        assert_throws(new TypeError(), function() {
-            new UserProximityEvent('test', date);
-        });
+        var event = new UserProximityEvent('test', date);
+        assert_false(event.near);
     }, 'eventInitDict argument is Date object');
 
     test(function() {
         var regexp = /abc/;
-        assert_throws(new TypeError(), function() {
-            new UserProximityEvent('test', regexp);
-        });
+        var event = new UserProximityEvent('test', regexp);
+        assert_false(event.near);
     }, 'eventInitDict argument is RegExp object');
 
     test(function() {
         assert_throws(new TypeError(), function() {
             new UserProximityEvent('test', false);
         });
     }, 'eventInitDict argument is boolean');
 
--- a/toolkit/components/contextualidentity/ContextualIdentityService.jsm
+++ b/toolkit/components/contextualidentity/ContextualIdentityService.jsm
@@ -278,41 +278,45 @@ function _ContextualIdentityService(path
       return;
     }
 
     let userContextId = tab.getAttribute("usercontextid");
     let identity = this.getIdentityFromId(userContextId);
     tab.setAttribute("data-identity-color", identity ? identity.color : "");
   },
 
-  countContainerTabs() {
+  countContainerTabs(userContextId = 0) {
     let count = 0;
-    this._forEachContainerTab(function() { ++count; });
+    this._forEachContainerTab(function() {
+      ++count;
+    }, userContextId);
     return count;
   },
 
-  closeAllContainerTabs() {
+  closeContainerTabs(userContextId = 0) {
     this._forEachContainerTab(function(tab, tabbrowser) {
       tabbrowser.removeTab(tab);
-    });
+    }, userContextId);
   },
 
-  _forEachContainerTab(callback) {
+  _forEachContainerTab(callback, userContextId = 0) {
     let windowList = Services.wm.getEnumerator("navigator:browser");
     while (windowList.hasMoreElements()) {
       let win = windowList.getNext();
 
       if (win.closed || !win.gBrowser) {
 	continue;
       }
 
       let tabbrowser = win.gBrowser;
       for (let i = tabbrowser.tabContainer.childNodes.length - 1; i >= 0; --i) {
         let tab = tabbrowser.tabContainer.childNodes[i];
-	if (tab.hasAttribute("usercontextid")) {
+	if (tab.hasAttribute("usercontextid") &&
+            (!userContextId ||
+             parseInt(tab.getAttribute("usercontextid"), 10) == userContextId)) {
 	  callback(tab, tabbrowser);
 	}
       }
     }
   },
 
   telemetry(userContextId) {
     let identity = this.getIdentityFromId(userContextId);
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm
@@ -1362,17 +1362,18 @@ UserAutoCompleteResult.prototype = {
   getCommentAt(index) {
     return "";
   },
 
   getStyleAt(index) {
     if (index == 0 && this._showInsecureFieldWarning) {
       return "insecureWarning";
     }
-    return "";
+
+    return "login";
   },
 
   getImageAt(index) {
     return "";
   },
 
   getFinalCompleteValueAt(index) {
     return this.getValueAt(index);
--- a/toolkit/components/passwordmgr/test/unit/test_user_autocomplete_result.js
+++ b/toolkit/components/passwordmgr/test/unit/test_user_autocomplete_result.js
@@ -34,385 +34,385 @@ let expectedResults = [
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: true,
     isSecure: true,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: true,
     isSecure: false,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised.",
       style: "insecureWarning"
     }, {
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: true,
     isSecure: true,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: true,
     isSecure: false,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised.",
       style: "insecureWarning"
     }, {
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: true,
     isSecure: true,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: true,
     isSecure: false,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: true,
     isSecure: true,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: true,
     isSecure: false,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: false,
     isSecure: true,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: false,
     isSecure: false,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised.",
       style: "insecureWarning"
     }, {
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: false,
     isSecure: true,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: true,
     insecureAutoFillFormsEnabled: false,
     isSecure: false,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: "This connection is not secure. Logins entered here could be compromised.",
       style: "insecureWarning"
     }, {
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: false,
     isSecure: true,
     isPasswordField: false,
     matchingLogins: matchingLogins,
     items: [{
       value: "",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "tempuser1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testuser2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testuser3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzuser4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: false,
     isSecure: false,
     isPasswordField: false,
     matchingLogins: matchingLogins,
@@ -422,33 +422,33 @@ let expectedResults = [
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: false,
     isSecure: true,
     isPasswordField: true,
     matchingLogins: matchingLogins,
     items: [{
       value: "emptypass1",
       label: LABEL_NO_USERNAME,
-      style: ""
+      style: "login",
     }, {
       value: "temppass1",
       label: "tempuser1",
-      style: ""
+      style: "login",
     }, {
       value: "testpass2",
       label: "testuser2",
-      style: ""
+      style: "login",
     }, {
       value: "testpass3",
       label: "testuser3",
-      style: ""
+      style: "login",
     }, {
       value: "zzzpass4",
       label: "zzzuser4",
-      style: ""
+      style: "login",
     }]
   },
   {
     insecureFieldWarningEnabled: false,
     insecureAutoFillFormsEnabled: false,
     isSecure: false,
     isPasswordField: true,
     matchingLogins: matchingLogins,
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -70,16 +70,18 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/PoisonIOInterposer.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/HangMonitor.h"
 #if defined(MOZ_ENABLE_PROFILER_SPS)
 #include "shared-libraries.h"
+#include "mozilla/StackWalk.h"
+#include "nsPrintfCString.h"
 #endif
 
 namespace {
 
 using namespace mozilla;
 using namespace mozilla::HangMonitor;
 using Telemetry::Common::AutoHashtable;
 
@@ -95,16 +97,19 @@ public:
   CombinedStacks() : mNextIndex(0) {}
   typedef std::vector<Telemetry::ProcessedStack::Frame> Stack;
   const Telemetry::ProcessedStack::Module& GetModule(unsigned aIndex) const;
   size_t GetModuleCount() const;
   const Stack& GetStack(unsigned aIndex) const;
   size_t AddStack(const Telemetry::ProcessedStack& aStack);
   size_t GetStackCount() const;
   size_t SizeOfExcludingThis() const;
+
+  /** Clears the contents of vectors and resets the index. */
+  void Clear();
 private:
   std::vector<Telemetry::ProcessedStack::Module> mModules;
   // A circular buffer to hold the stacks.
   std::vector<Stack> mStacks;
   // The index of the next buffer element to write to in mStacks.
   size_t mNextIndex;
 };
 
@@ -201,16 +206,23 @@ ComputeAnnotationsKey(const HangAnnotati
   while (annotationsEnum->Next(key, value)) {
     aKeyOut.Append(key);
     aKeyOut.Append(value);
   }
 
   return NS_OK;
 }
 
+void
+CombinedStacks::Clear() {
+  mNextIndex = 0;
+  mStacks.clear();
+  mModules.clear();
+}
+
 class HangReports {
 public:
   /**
    * This struct encapsulates information for an individual ChromeHang annotation.
    * mHangIndex is the index of the corresponding ChromeHang.
    */
   struct AnnotationInfo {
     AnnotationInfo(uint32_t aHangIndex,
@@ -378,16 +390,201 @@ HangReports::GetFirefoxUptime(unsigned a
   return mHangInfo[aIndex].mFirefoxUptime;
 }
 
 const nsClassHashtable<nsStringHashKey, HangReports::AnnotationInfo>&
 HangReports::GetAnnotationInfo() const {
   return mAnnotationInfo;
 }
 
+#if defined(MOZ_ENABLE_PROFILER_SPS)
+
+const uint8_t kMaxKeyLength = 50;
+
+/**
+ * Checks if a single character of the key string is valid.
+ *
+ * @param aChar a character to validate.
+ * @return True, if the char is valid, False - otherwise.
+ */
+bool
+IsKeyCharValid(const char aChar)
+{
+  return (aChar >= 'A' && aChar <= 'Z')
+      || (aChar >= 'a' && aChar <= 'z')
+      || (aChar >= '0' && aChar <= '9')
+      || aChar == '-';
+}
+
+/**
+ * Checks if a given string is a valid telemetry key.
+ *
+ * @param aKey is the key string.
+ * @return True, if the key is valid, False - otherwise.
+ */
+bool
+IsKeyValid(const nsACString& aKey)
+{
+  // Check key length.
+  if (aKey.Length() > kMaxKeyLength) {
+    return false;
+  }
+
+  // Check key characters.
+  const char* cur = aKey.BeginReading();
+  const char* end = aKey.EndReading();
+
+  for (; cur < end; ++cur) {
+      if (!IsKeyCharValid(*cur)) {
+        return false;
+      }
+  }
+  return true;
+}
+
+/**
+ * Allows taking a snapshot of a call stack on demand. Captured stacks are
+ * indexed by a string key in a hash table. The stack is only captured Once
+ * for each key. Consequent captures with the same key result in incrementing
+ * capture counter without re-capturing the stack.
+ */
+class KeyedStackCapturer {
+public:
+  KeyedStackCapturer();
+
+  void Capture(const nsACString& aKey);
+  NS_IMETHODIMP ReflectCapturedStacks(JSContext *cx, JS::MutableHandle<JS::Value> ret);
+
+  /**
+   * Resets captured stacks and the information related to them.
+   */
+  void Clear();
+private:
+  /**
+   * Describes how often a stack was captured.
+   */
+  struct StackFrequencyInfo {
+    // A number of times the stack was captured.
+    uint32_t mCount;
+    // Index of the stack inside stacks array.
+    uint32_t mIndex;
+
+    StackFrequencyInfo(uint32_t aCount, uint32_t aIndex)
+      : mCount(aCount)
+      , mIndex(aIndex)
+    {}
+  };
+
+  typedef nsClassHashtable<nsCStringHashKey, StackFrequencyInfo> FrequencyInfoMapType;
+
+  FrequencyInfoMapType mStackInfos;
+  CombinedStacks mStacks;
+  Mutex mStackCapturerMutex;
+};
+
+KeyedStackCapturer::KeyedStackCapturer()
+  : mStackCapturerMutex("Telemetry::StackCapturerMutex")
+{}
+
+void KeyedStackCapturer::Capture(const nsACString& aKey) {
+  // Check if the key is ok.
+  if (!IsKeyValid(aKey)) {
+    NS_WARNING(nsPrintfCString(
+      "Invalid key is used to capture stack in telemetry: '%s'",
+      PromiseFlatCString(aKey).get()
+    ).get());
+    return;
+  }
+
+  // Trying to find and update the stack information.
+  StackFrequencyInfo* info = mStackInfos.Get(aKey);
+  if (info) {
+    // We already recorded this stack before, only increase the count.
+    info->mCount++;
+    return;
+  }
+
+  // Check if we have room for new captures.
+  if (mStackInfos.Count() >= kMaxChromeStacksKept) {
+    // Addressed by Bug 1316793.
+    return;
+  }
+
+  // We haven't captured a stack for this key before, do it now.
+  // Note that this does a stackwalk and is an expensive operation.
+  std::vector<uintptr_t> rawStack;
+  auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
+    std::vector<uintptr_t>* stack =
+      static_cast<std::vector<uintptr_t>*>(aClosure);
+    stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+  };
+  MozStackWalk(callback, /* skipFrames */ 0,
+              /* maxFrames */ 0, reinterpret_cast<void*>(&rawStack), 0, nullptr);
+  Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
+
+  // Store the new stack info.
+  MutexAutoLock captureStackMutex(mStackCapturerMutex);
+  size_t stackIndex = mStacks.AddStack(stack);
+  mStackInfos.Put(aKey, new StackFrequencyInfo(1, stackIndex));
+}
+
+NS_IMETHODIMP
+KeyedStackCapturer::ReflectCapturedStacks(JSContext *cx, JS::MutableHandle<JS::Value> ret)
+{
+  MutexAutoLock capturedStackMutex(mStackCapturerMutex);
+
+  // this adds the memoryMap and stacks properties.
+  JS::RootedObject fullReportObj(cx, CreateJSStackObject(cx, mStacks));
+  if (!fullReportObj) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JS::RootedObject keysArray(cx, JS_NewArrayObject(cx, 0));
+  if (!keysArray) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool ok = JS_DefineProperty(cx, fullReportObj, "captures",
+                              keysArray, JSPROP_ENUMERATE);
+  if (!ok) {
+    return NS_ERROR_FAILURE;
+  }
+
+  size_t keyIndex = 0;
+  for (auto iter = mStackInfos.ConstIter(); !iter.Done(); iter.Next(), ++keyIndex) {
+    const StackFrequencyInfo* info = iter.Data();
+
+    JS::RootedObject infoArray(cx, JS_NewArrayObject(cx, 0));
+    if (!keysArray) {
+      return NS_ERROR_FAILURE;
+    }
+    JS::RootedString str(cx, JS_NewStringCopyZ(cx,
+                         PromiseFlatCString(iter.Key()).get()));
+    if (!str ||
+        !JS_DefineElement(cx, infoArray, 0, str, JSPROP_ENUMERATE) ||
+        !JS_DefineElement(cx, infoArray, 1, info->mIndex, JSPROP_ENUMERATE) ||
+        !JS_DefineElement(cx, infoArray, 2, info->mCount, JSPROP_ENUMERATE) ||
+        !JS_DefineElement(cx, keysArray, keyIndex, infoArray, JSPROP_ENUMERATE)) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  ret.setObject(*fullReportObj);
+  return NS_OK;
+}
+
+void
+KeyedStackCapturer::Clear()
+{
+  MutexAutoLock captureStackMutex(mStackCapturerMutex);
+  mStackInfos.Clear();
+  mStacks.Clear();
+}
+#endif
+
 /**
  * IOInterposeObserver recording statistics of main-thread I/O during execution,
  * aimed at consumption by TelemetryImpl
  */
 class TelemetryIOInterposeObserver : public IOInterposeObserver
 {
   /** File-level statistics structure */
   struct FileStats {
@@ -681,16 +878,17 @@ public:
   static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
                                   uint32_t delay);
 #if defined(MOZ_ENABLE_PROFILER_SPS)
   static void RecordChromeHang(uint32_t aDuration,
                                Telemetry::ProcessedStack &aStack,
                                int32_t aSystemUptime,
                                int32_t aFirefoxUptime,
                                HangAnnotationsPtr aAnnotations);
+  static void DoStackCapture(const nsACString& aKey);
 #endif
   static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
   struct Stat {
     uint32_t hitCount;
     uint32_t totalTime;
   };
   struct StmtStats {
@@ -727,16 +925,22 @@ private:
   void ReadLateWritesStacks(nsIFile* aProfileDir);
 
   static TelemetryImpl *sTelemetry;
   AutoHashtable<SlowSQLEntryType> mPrivateSQL;
   AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
   Mutex mHashMutex;
   HangReports mHangReports;
   Mutex mHangReportsMutex;
+
+#if defined(MOZ_ENABLE_PROFILER_SPS)
+  // Stores data about stacks captured on demand.
+  KeyedStackCapturer mStackCapturer;
+#endif
+
   // mThreadHangStats stores recorded, inactive thread hang stats
   Vector<Telemetry::ThreadHangStats> mThreadHangStats;
   Mutex mThreadHangStatsMutex;
 
   CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
   bool mCachedTelemetryData;
   uint32_t mLastShutdownTime;
   uint32_t mFailedLockCount;
@@ -1316,16 +1520,30 @@ TelemetryImpl::GetChromeHangs(JSContext 
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+TelemetryImpl::SnapshotCapturedStacks(bool clear, JSContext *cx, JS::MutableHandle<JS::Value> ret)
+{
+#if defined(MOZ_ENABLE_PROFILER_SPS)
+  nsresult rv = mStackCapturer.ReflectCapturedStacks(cx, ret);
+  if (clear) {
+    mStackCapturer.Clear();
+  }
+  return rv;
+#else
+  return NS_OK;
+#endif
+}
+
 static JSObject *
 CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) {
   JS::Rooted<JSObject*> ret(cx, JS_NewPlainObject(cx));
   if (!ret) {
     return nullptr;
   }
 
   JS::Rooted<JSObject*> moduleArray(cx, JS_NewArrayObject(cx, 0));
@@ -2237,18 +2455,33 @@ TelemetryImpl::RecordChromeHang(uint32_t
   }
 
   MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
 
   sTelemetry->mHangReports.AddHang(aStack, aDuration,
                                    aSystemUptime, aFirefoxUptime,
                                    Move(annotations));
 }
+
+void
+TelemetryImpl::DoStackCapture(const nsACString& aKey) {
+  if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
+    sTelemetry->mStackCapturer.Capture(aKey);
+  }
+}
 #endif
 
+nsresult
+TelemetryImpl::CaptureStack(const nsACString& aKey) {
+#if defined(MOZ_ENABLE_PROFILER_SPS)
+  TelemetryImpl::DoStackCapture(aKey);
+#endif
+  return NS_OK;
+}
+
 void
 TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
 {
   if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
     return;
 
   MutexAutoLock autoLock(sTelemetry->mThreadHangStatsMutex);
 
@@ -2907,16 +3140,21 @@ void RecordChromeHang(uint32_t duration,
                       int32_t aSystemUptime,
                       int32_t aFirefoxUptime,
                       HangAnnotationsPtr aAnnotations)
 {
   TelemetryImpl::RecordChromeHang(duration, aStack,
                                   aSystemUptime, aFirefoxUptime,
                                   Move(aAnnotations));
 }
+
+void CaptureStack(const nsACString& aKey)
+{
+  TelemetryImpl::DoStackCapture(aKey);
+}
 #endif
 
 void RecordThreadHangStats(ThreadHangStats& aStats)
 {
   TelemetryImpl::RecordThreadHangStats(aStats);
 }
 
 
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -323,16 +323,27 @@ class ProcessedStack;
  */
 #if defined(MOZ_ENABLE_PROFILER_SPS)
 void RecordChromeHang(uint32_t aDuration,
                       ProcessedStack &aStack,
                       int32_t aSystemUptime,
                       int32_t aFirefoxUptime,
                       mozilla::UniquePtr<mozilla::HangMonitor::HangAnnotations>
                               aAnnotations);
+
+/**
+ * Record the current thread's call stack on demand. Note that, the stack is
+ * only captured once. Subsequent calls result in incrementing the capture
+ * counter.
+ *
+ * @param aKey - A user defined key associated with the captured stack.
+ *
+ * NOTE: Unwinding call stacks is an expensive operation performance-wise.
+ */
+void CaptureStack(const nsCString& aKey);
 #endif
 
 class ThreadHangStats;
 
 /**
  * Move a ThreadHangStats to Telemetry storage. Normally Telemetry queries
  * for active ThreadHangStats through BackgroundHangMonitor, but once a
  * thread exits, the thread's copy of ThreadHangStats needs to be moved to
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -1341,16 +1341,23 @@ var Impl = {
            Object.keys(this._slowSQLStartup.otherThreads).length)) {
         payloadObj.slowSQLStartup = this._slowSQLStartup;
       }
 
       if (!this._isClassicReason(reason)) {
         payloadObj.processes.parent.gc = protect(() => GCTelemetry.entries("main", clearSubsession));
         payloadObj.processes.content.gc = protect(() => GCTelemetry.entries("content", clearSubsession));
       }
+
+      // Adding captured stacks to the payload only if any exist and clearing
+      // captures for this sub-session.
+      let stacks = protect(() => Telemetry.snapshotCapturedStacks(true));
+      if (stacks && ("captures" in stacks) && (stacks.captures.length > 0)) {
+        payloadObj.processes.parent.capturedStacks = stacks;
+      }
     }
 
     if (this._childTelemetry.length) {
       payloadObj.childPayloads = protect(() => this.getChildPayloads());
     }
 
     return payloadObj;
   },
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -57,16 +57,17 @@ Structure:
       childPayloads: [...], // only present with e10s; reduced payloads from content processes, null on failure
       simpleMeasurements: {...},
 
       // The following properties may all be null if we fail to collect them.
       histograms: {...},
       keyedHistograms: {...},
       chromeHangs: {...},
       threadHangStats: [...],
+      capturedStacks: {...},
       log: [...],
       webrtc: {...},
       gc: {...},
       fileIOReports: {...},
       lateWrites: {...},
       addonDetails: {...},
       addonHistograms: {...},
       UIMeasurements: [...],
@@ -219,17 +220,17 @@ This section contains the histograms tha
 keyedHistograms
 ---------------
 This section contains the keyed histograms available for the current platform.
 
 As of Firefox 48, this section does not contain empty keyed histograms anymore.
 
 threadHangStats
 ---------------
-Contains the statistics about the hangs in main and background threads. Note that hangs in this section capture the [C++ pseudostack](https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Native_stack_vs._Pseudo_stack) and an incomplete JS stack, which is not 100% precise.
+Contains the statistics about the hangs in main and background threads. Note that hangs in this section capture the `C++ pseudostack <https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Profiling_with_the_Built-in_Profiler#Native_stack_vs._Pseudo_stack>`_ and an incomplete JS stack, which is not 100% precise.
 
 To avoid submitting overly large payloads, some limits are applied:
 
 * Identical, adjacent "(chrome script)" or "(content script)" stack entries are collapsed together. If a stack is reduced, the "(reduced stack)" frame marker is added as the oldest frame.
 * The depth of the reported stacks is limited to 11 entries. This value represents the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
 
 Structure:
 
@@ -258,16 +259,49 @@ Structure:
               ... other annotations ...
             ]
           },
         ],
       },
       ... other threads ...
      ]
 
+capturedStacks
+--------------
+Contains information about stacks captured on demand via Telemetry API. This is similar to `chromeHangs`, but only stacks captured on the main thread of the parent process are reported. It reports precise C++ stacks are reported and is only available on Windows, either in Firefox Nightly or in builds using "--enable-profiling" switch.
+
+Limits for captured stacks are the same as for chromeHangs (see below). Furthermore:
+
+* the key length is limited to 50 characters,
+* keys are restricted to alpha-numeric characters and `-`.
+
+Structure:
+
+.. code-block:: js
+
+    "capturedStacks" : {
+      "memoryMap": [
+        ["wgdi32.pdb", "08A541B5942242BDB4AEABD8C87E4CFF2"],
+        ["igd10iumd32.pdb", "D36DEBF2E78149B5BE1856B772F1C3991"],
+        // ... other entries in the format ["module name", "breakpad identifier"] ...
+      ],
+      "stacks": [
+        [
+           [
+             0, // the module index or -1 for invalid module indices
+             190649 // the offset of this program counter in its module or an absolute pc
+           ],
+           [1, 2540075],
+           // ... other frames ...
+        ],
+        // ... other stacks ...
+      ],
+      "captures": [["string-key", stack-index, count], ... ]
+    }
+
 chromeHangs
 -----------
 Contains the statistics about the hangs happening exclusively on the main thread of the parent process. Precise C++ stacks are reported. This is only available on Nightly Release on Windows, when building using "--enable-profiling" switch.
 
 Some limits are applied:
 
 * Reported chrome hang stacks are limited in depth to 50 entries.
 * The maximum number of reported stacks is 50.
--- a/toolkit/components/telemetry/nsITelemetry.idl
+++ b/toolkit/components/telemetry/nsITelemetry.idl
@@ -135,16 +135,57 @@ interface nsITelemetry : nsISupports
    * An array of chrome hang reports. Each element is a hang report represented
    * as an object containing the hang duration, call stack PCs and information
    * about modules in memory.
    */
   [implicit_jscontext]
   readonly attribute jsval chromeHangs;
 
   /*
+   * Record the current thread's call stack on demand. Note that, the stack is
+   * only captured at the first call. All subsequent calls result in incrementing
+   * the capture counter without doing actual stack unwinding.
+   *
+   * @param aKey - A user defined key associated with the captured stack.
+   *
+   * NOTE: Unwinding call stacks is an expensive operation performance-wise.
+   */
+  void captureStack(in ACString name);
+
+  /*
+   * Returns a snapshot of captured stacks. The data has the following structure:
+   *
+   * {
+   *  "memoryMap": [
+   *      ["wgdi32.pdb", "08A541B5942242BDB4AEABD8C87E4CFF2"],
+   *      ["igd10iumd32.pdb", "D36DEBF2E78149B5BE1856B772F1C3991"],
+   *      ... other entries in the format ["module name", "breakpad identifier"] ...
+   *   ],
+   *   "stacks": [
+   *      [
+   *         [
+   *           0, // the module index or -1 for invalid module indices
+   *           190649 // the offset of this program counter in its module or an absolute pc
+   *         ],
+   *         [1, 2540075],
+   *         ... other frames ...
+   *      ],
+   *      ... other stacks ...
+   *   ],
+   *   "captures": [["string-key", stack-index, count], ... ]
+   * }
+   *
+   * @param clear Whether to clear out the subsession histograms after taking a  snapshot.
+   *
+   * @return A snapshot of captured stacks.
+   */
+  [implicit_jscontext]
+  jsval snapshotCapturedStacks([optional] in boolean clear);
+
+  /*
    * An array of thread hang stats,
    *   [<thread>, <thread>, ...]
    * <thread> represents a single thread,
    *   {"name": "<name>",
    *    "activity": <time>,
    *    "hangs": [<hang>, <hang>, ...]}
    * <time> represents a histogram of time intervals in milliseconds,
    *   with the same format as histogramSnapshots
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryCaptureStack.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/TelemetryController.jsm", this);
+Cu.import("resource://gre/modules/AppConstants.jsm", this);
+
+/**
+ * Ensures that the sctucture of the javascript object used for capturing stacks
+ * is as intended. The structure is expected to be as in this example:
+ *
+ * {
+ *  "memoryMap": [
+ *      [String, String],
+ *      ...
+ *   ],
+ *   "stacks": [
+ *      [
+ *         [Integer, Integer], // Frame
+ *         ...
+ *      ],
+ *      ...
+ *   ],
+ *   "captures": [
+ *      [String, Integer, Integer],
+ *      ...
+ *   ]
+ * }
+ *
+ * @param {Object} obj  abject to be inpected vor validity.
+ *
+ * @return {Boolean} True if the structure is valid. False - otherwise.
+ */
+function checkObjectStructure(obj) {
+  // Ensuring an object is given.
+  if (!obj || typeof obj !== "object") {
+    return false;
+  }
+
+  // Ensuring all properties exist inside the object and are arrays.
+  for (let property of ["memoryMap", "stacks", "captures"]) {
+    if (!(property in obj) || !Array.isArray(obj[property]))
+      return false;
+  }
+
+  return true;
+}
+
+/**
+ * A helper for triggering a stack capture and returning the new state of stacks.
+ *
+ * @param {String}  key   The key for capturing stack.
+ * @param {Boolean} clear True to reset captured stacks, False - otherwise.
+ *
+ * @return {Object} captured stacks.
+ */
+function captureStacks(key, clear = true) {
+  Telemetry.captureStack(key);
+  let stacks = Telemetry.snapshotCapturedStacks(clear);
+  Assert.ok(checkObjectStructure(stacks));
+  return stacks;
+}
+
+const TEST_STACK_KEYS = ["TEST-KEY1", "TEST-KEY2"];
+
+/**
+ * Ensures that captured stacks appear in pings, if any were captured.
+ */
+add_task({
+  skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
+}, function* test_capturedStacksAppearInPings() {
+  yield TelemetryController.testSetup();
+  captureStacks("DOES-NOT-MATTER", false);
+
+  let ping = TelemetryController.getCurrentPingData();
+  Assert.ok("capturedStacks" in ping.payload.processes.parent);
+
+  let capturedStacks = ping.payload.processes.parent.capturedStacks;
+  Assert.ok(checkObjectStructure(capturedStacks));
+});
+
+/**
+ * Ensures that capturing a stack for a new key increases the number
+ * of captured stacks and adds a new entry to captures.
+ */
+add_task({
+  skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
+}, function* test_CaptureStacksIncreasesNumberOfCapturedStacks() {
+  // Construct a unique key for this test.
+  let key = TEST_STACK_KEYS[0] + "-UNIQUE-KEY-1";
+
+  // Ensure that no captures for the key exist.
+  let original = Telemetry.snapshotCapturedStacks();
+  Assert.equal(undefined, original.captures.find(capture => capture[0] === key));
+
+  // Capture stack and find updated capture stats for TEST_STACK_KEYS[0].
+  let updated = captureStacks(key);
+
+  // Ensure that a new element has been appended to both stacks and captures.
+  Assert.equal(original.stacks.length + 1, updated.stacks.length);
+  Assert.equal(original.captures.length + 1, updated.captures.length);
+
+  // Ensure that the capture info for the key exists and structured well.
+  Assert.deepEqual(
+    [key, original.stacks.length, 1],
+    updated.captures.find(capture => capture[0] === key)
+  );
+});
+
+/**
+ * Ensures that stacks are grouped by the key. If a stack is captured
+ * more than once for the key, the length of stacks does not increase.
+ */
+ add_task({
+   skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
+ }, function* test_CaptureStacksGroupsDuplicateStacks() {
+  // Make sure that there are initial captures for TEST_STACK_KEYS[0].
+  let stacks = captureStacks(TEST_STACK_KEYS[0], false);
+  let original = {
+    captures: stacks.captures.find(capture => capture[0] === TEST_STACK_KEYS[0]),
+    stacks: stacks.stacks
+  };
+
+  // Capture stack and find updated capture stats for TEST_STACK_KEYS[0].
+  stacks = captureStacks(TEST_STACK_KEYS[0]);
+  let updated = {
+    captures: stacks.captures.find(capture => capture[0] === TEST_STACK_KEYS[0]),
+    stacks: stacks.stacks
+  };
+
+  // The length of captured stacks should remain same.
+  Assert.equal(original.stacks.length, updated.stacks.length);
+
+  // We expect the info on captures to look as original. Only
+  // stack counter should be increased by one.
+  let expectedCaptures = original.captures;
+  expectedCaptures[2]++;
+  Assert.deepEqual(expectedCaptures, updated.captures);
+});
+
+/**
+ * Ensure that capturing the stack for a key does not affect info
+ * for other keys.
+ */
+add_task({
+  skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
+}, function* test_CaptureStacksSeparatesInformationByKeys() {
+  // Make sure that there are initial captures for TEST_STACK_KEYS[0].
+  let stacks = captureStacks(TEST_STACK_KEYS[0], false);
+  let original = {
+    captures: stacks.captures.find(capture => capture[0] === TEST_STACK_KEYS[0]),
+    stacks: stacks.stacks
+  };
+
+  // Capture stack for a new key.
+  let uniqueKey = TEST_STACK_KEYS[1] + "-UNIQUE-KEY-2";
+  updated = captureStacks(uniqueKey);
+
+  // The length of captured stacks should increase to reflect the new capture.
+  Assert.equal(original.stacks.length + 1, updated.stacks.length);
+
+  // The information for TEST_STACK_KEYS[0] should remain same.
+  Assert.deepEqual(
+    original.captures,
+    updated.captures.find(capture => capture[0] === TEST_STACK_KEYS[0])
+  );
+});
+
+/**
+ * Ensure that Telemetry does not allow weird keys.
+ */
+add_task({
+  skip_if: () => !AppConstants.MOZ_ENABLE_PROFILER_SPS
+}, function* test_CaptureStacksDoesNotAllowBadKey() {
+  for (let badKey of [null, "KEY-!@\"#$%^&*()_"]) {
+    let stacks = captureStacks(badKey);
+    let captureData = stacks.captures.find(capture => capture[0] === badKey)
+    Assert.ok(!captureData, `"${badKey}" should not be allowed as a key`);
+  }
+});
+
+function run_test() {
+  do_get_profile(true);
+  Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
+  run_next_test();
+}
--- a/toolkit/components/telemetry/tests/unit/xpcshell.ini
+++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini
@@ -55,9 +55,10 @@ run-sequentially = Bug 1046307, test can
 [test_ChildHistograms.js]
 skip-if = os == "android"
 tags = addons
 [test_TelemetryReportingPolicy.js]
 tags = addons
 [test_TelemetryScalars.js]
 [test_TelemetryTimestamps.js]
 skip-if = toolkit == 'android'
+[test_TelemetryCaptureStack.js]
 [test_TelemetryEvents.js]
--- a/toolkit/components/url-classifier/Classifier.cpp
+++ b/toolkit/components/url-classifier/Classifier.cpp
@@ -17,16 +17,17 @@
 #include "nsThreadUtils.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Logging.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Unused.h"
 #include "mozilla/TypedEnumBits.h"
 #include "nsIUrlClassifierUtils.h"
+#include "nsUrlClassifierDBService.h"
 
 // MOZ_LOG=UrlClassifierDbService:5
 extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
 #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
 
 #define STORE_DIRECTORY      NS_LITERAL_CSTRING("safebrowsing")
 #define TO_DELETE_DIR_SUFFIX NS_LITERAL_CSTRING("-to_delete")
@@ -139,16 +140,17 @@ Classifier::GetPrivateStoreDirectory(nsI
   }
 
   providerDirectory.forget(aPrivateStoreDirectory);
 
   return NS_OK;
 }
 
 Classifier::Classifier()
+  : mIsTableRequestResultOutdated(true)
 {
 }
 
 Classifier::~Classifier()
 {
   Close();
 }
 
@@ -343,16 +345,26 @@ Classifier::AbortUpdateAndReset(const ns
   // from an update.
   Unused << RemoveBackupTables();
   Unused << CleanToDelete();
 }
 
 void
 Classifier::TableRequest(nsACString& aResult)
 {
+  MOZ_ASSERT(!NS_IsMainThread(),
+             "TableRequest must be called on the classifier worker thread.");
+
+  // This function and all disk I/O are guaranteed to occur
+  // on the same thread so we don't need to add a lock around.
+  if (!mIsTableRequestResultOutdated) {
+    aResult = mTableRequestResult;
+    return;
+  }
+
   // Generating v2 table info.
   nsTArray<nsCString> tables;
   ActiveTables(tables);
   for (uint32_t i = 0; i < tables.Length(); i++) {
     HashStore store(tables[i], GetProvider(tables[i]), mRootStoreDirectory);
 
     nsresult rv = store.Open();
     if (NS_FAILED(rv))
@@ -382,18 +394,23 @@ Classifier::TableRequest(nsACString& aRe
 
     aResult.Append('\n');
   }
 
   // Load meta data from *.metadata files in the root directory.
   // Specifically for v4 tables.
   nsCString metadata;
   nsresult rv = LoadMetadata(mRootStoreDirectory, metadata);
-  NS_ENSURE_SUCCESS_VOID(rv);
-  aResult.Append(metadata);
+  if (NS_SUCCEEDED(rv)) {
+    aResult.Append(metadata);
+  }
+
+  // Update the TableRequest result in-memory cache.
+  mTableRequestResult = aResult;
+  mIsTableRequestResultOutdated = false;
 }
 
 // This is used to record the matching statistics for v2 and v4.
 enum class PrefixMatch : uint8_t {
   eNoMatch = 0x00,
   eMatchV2Prefix = 0x01,
   eMatchV4Prefix = 0x02,
   eMatchBoth = eMatchV2Prefix | eMatchV4Prefix
@@ -529,16 +546,20 @@ Classifier::ApplyUpdates(nsTArray<TableU
         nsCString updateTable(aUpdates->ElementAt(i)->TableName());
 
         if (TableUpdate::Cast<TableUpdateV2>((*aUpdates)[i])) {
           rv = UpdateHashStore(aUpdates, updateTable);
         } else {
           rv = UpdateTableV4(aUpdates, updateTable);
         }
 
+        // We mark the table associated info outdated no matter the
+        // update is successful to avoid any possibile non-atomic update.
+        mIsTableRequestResultOutdated = true;
+
         if (NS_FAILED(rv)) {
           if (rv != NS_ERROR_OUT_OF_MEMORY) {
 #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
             DumpFailedUpdate();
 #endif
             AbortUpdateAndReset(updateTable);
           }
           return rv;
@@ -1004,16 +1025,19 @@ Classifier::UpdateHashStore(nsTArray<Tab
 
   return NS_OK;
 }
 
 nsresult
 Classifier::UpdateTableV4(nsTArray<TableUpdate*>* aUpdates,
                           const nsACString& aTable)
 {
+  MOZ_ASSERT(!NS_IsMainThread(),
+             "UpdateTableV4 must be called on the classifier worker thread.");
+
   LOG(("Classifier::UpdateTableV4(%s)", PromiseFlatCString(aTable).get()));
 
   if (!CheckValidUpdate(aUpdates, aTable)) {
     return NS_OK;
   }
 
   LookupCacheV4* lookupCache =
     LookupCache::Cast<LookupCacheV4>(GetLookupCache(aTable));
--- a/toolkit/components/url-classifier/Classifier.h
+++ b/toolkit/components/url-classifier/Classifier.h
@@ -154,14 +154,22 @@ private:
   nsCOMPtr<nsIFile> mBackupDirectory;
   nsCOMPtr<nsIFile> mToDeleteDirectory;
   nsCOMPtr<nsICryptoHash> mCryptoHash;
   nsTArray<LookupCache*> mLookupCaches;
   nsTArray<nsCString> mActiveTablesCache;
   uint32_t mHashKey;
   // Stores the last time a given table was updated (seconds).
   nsDataHashtable<nsCStringHashKey, int64_t> mTableFreshness;
+
+  // In-memory cache for the result of TableRequest. See
+  // nsIUrlClassifierDBService.getTables for the format.
+  nsCString mTableRequestResult;
+
+  // Whether mTableRequestResult is outdated and needs to
+  // be reloaded from disk.
+  bool mIsTableRequestResultOutdated;
 };
 
 } // namespace safebrowsing
 } // namespace mozilla
 
 #endif
--- a/toolkit/components/url-classifier/tests/unit/test_listmanager.js
+++ b/toolkit/components/url-classifier/tests/unit/test_listmanager.js
@@ -355,17 +355,27 @@ function waitUntilMetaDataSaved(expected
 
       if (tableName !== 'test-phish-proto') {
         return false; // continue.
       }
 
       if (stateBase64 === btoa(expectedState) &&
           checksumBase64 === btoa(expectedChecksum)) {
         do_print('State has been saved to disk!');
-        callback();
+
+        // We slightly defer the callback to see if the in-memory
+        // |getTables| caching works correctly.
+        dbService.getTables(cachedMetadata => {
+          equal(cachedMetadata, metaData);
+          callback();
+        });
+
+        // Even though we haven't done callback at this moment
+        // but we still claim "we have" in order to stop repeating
+        // a new timer.
         didCallback = true;
       }
 
       return true; // break no matter whether the state is matching.
     });
 
     if (!didCallback) {
       do_timeout(1000, waitUntilMetaDataSaved.bind(null, expectedState,
--- a/toolkit/content/aboutTelemetry.js
+++ b/toolkit/content/aboutTelemetry.js
@@ -1103,16 +1103,38 @@ var ChromeHangs = {
                                (index) => this.renderHangHeader(index, durations));
   },
 
   renderHangHeader: function ChromeHangs_renderHangHeader(aIndex, aDurations) {
     StackRenderer.renderHeader("chrome-hangs", [aIndex + 1, aDurations[aIndex]]);
   }
 };
 
+var CapturedStacks = {
+  symbolRequest: null,
+
+  render: function CapturedStacks_render(payload) {
+    // Retrieve captured stacks from telemetry payload.
+    let capturedStacks = "processes" in payload && "parent" in payload.processes
+      ? payload.processes.parent.capturedStacks
+      : false;
+    let hasData = capturedStacks && capturedStacks.stacks &&
+                  capturedStacks.stacks.length > 0;
+    setHasData("captured-stacks-section", hasData);
+    if (!hasData) {
+      return;
+    }
+
+    let stacks = capturedStacks.stacks;
+    let memoryMap = capturedStacks.memoryMap;
+
+    StackRenderer.renderStacks("captured-stacks", stacks, memoryMap, () => {});
+  },
+};
+
 var ThreadHangStats = {
 
   /**
    * Renders raw thread hang stats data
    */
   render: function(aPayload) {
     let div = document.getElementById("thread-hang-stats");
     removeAllChildNodes(div);
@@ -1792,16 +1814,39 @@ function setupListeners() {
     function() {
       if (!gPingData) {
         return;
       }
 
       ChromeHangs.render(gPingData);
   }, false);
 
+  document.getElementById("captured-stacks-fetch-symbols").addEventListener("click",
+    function() {
+      if (!gPingData) {
+        return;
+      }
+      let capturedStacks = gPingData.payload.processes.parent.capturedStacks;
+      let req = new SymbolicationRequest("captured-stacks",
+                                         CapturedStacks.render,
+                                         capturedStacks.memoryMap,
+                                         capturedStacks.stacks,
+                                         null);
+      req.fetchSymbols();
+  }, false);
+
+  document.getElementById("captured-stacks-hide-symbols").addEventListener("click",
+    function() {
+      if (!gPingData) {
+        return;
+      }
+
+      CapturedStacks.render(gPingData);
+  }, false);
+
   document.getElementById("late-writes-fetch-symbols").addEventListener("click",
     function() {
       if (!gPingData) {
         return;
       }
 
       let lateWrites = gPingData.payload.lateWrites;
       let req = new SymbolicationRequest("late-writes",
@@ -2058,16 +2103,19 @@ function displayPingData(ping, updatePay
   let payload = ping.payload;
   if (payloadIndex > 0) {
     payload = ping.payload.childPayloads[payloadIndex - 1];
   }
 
   // Show thread hang stats
   ThreadHangStats.render(payload);
 
+  // Show captured stacks.
+  CapturedStacks.render(payload);
+
   // Show simple measurements
   let simpleMeasurements = sortStartupMilestones(payload.simpleMeasurements);
   let hasData = Object.keys(simpleMeasurements).length > 0;
   setHasData("simple-measurements-section", hasData);
   let simpleSection = document.getElementById("simple-measurements");
   removeAllChildNodes(simpleSection);
 
   if (hasData) {
--- a/toolkit/content/aboutTelemetry.xhtml
+++ b/toolkit/content/aboutTelemetry.xhtml
@@ -269,16 +269,31 @@
         <input type="checkbox" class="statebox"/>
         <h1 class="section-name">&aboutTelemetry.addonHistogramsSection;</h1>
         <span class="toggle-caption">&aboutTelemetry.toggle;</span>
         <span class="empty-caption">&aboutTelemetry.emptySection;</span>
         <div id="addon-histograms" class="data">
         </div>
       </section>
 
+      <section id="captured-stacks-section" class="data-section">
+        <input type="checkbox" class="statebox"/>
+        <h1 class="section-name">&aboutTelemetry.capturedStacksSection;</h1>
+        <span class="toggle-caption">&aboutTelemetry.toggle;</span>
+        <span class="empty-caption">&aboutTelemetry.emptySection;</span>
+        <div id="captured-stacks" class="data">
+          <a id="captured-stacks-fetch-symbols" href="#">&aboutTelemetry.fetchSymbols;</a>
+          <a id="captured-stacks-hide-symbols" class="hidden" href="#">&aboutTelemetry.hideSymbols;</a>
+          <br/>
+          <br/>
+          <div id="captured-stacks-data">
+          </div>
+        </div>
+      </section>
+
       <section id="raw-payload-section" class="data-section">
         <input type="checkbox" class="statebox"/>
         <h1 class="section-name">&aboutTelemetry.rawPayload;</h1>
         <span class="toggle-caption">&aboutTelemetry.toggle;</span>
         <span class="empty-caption">&aboutTelemetry.emptySection;</span>
         <div id="raw-payload-data" class="data">
           <pre id="raw-payload-data-pre"></pre>
         </div>
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -1469,17 +1469,16 @@ extends="chrome://global/content/binding
       <xul:vbox class="ac-title"
                 align="left"
                 xbl:inherits="">
         <xul:description class="ac-text-overflow-container">
           <xul:description anonid="title-text"
                            class="ac-title-text"
                            xbl:inherits="selected"/>
         </xul:description>
-        <xul:label id="learnMoreLink" align="left" class="text-link"/>
       </xul:vbox>
       <xul:hbox anonid="tags"
                 class="ac-tags"
                 align="center"
                 xbl:inherits="selected">
         <xul:description class="ac-text-overflow-container">
           <xul:description anonid="tags-text"
                            class="ac-tags-text"
@@ -1506,32 +1505,16 @@ extends="chrome://global/content/binding
                 xbl:inherits="selected,actiontype">
         <xul:description class="ac-text-overflow-container">
           <xul:description anonid="action-text"
                            class="ac-action-text"
                            xbl:inherits="selected"/>
         </xul:description>
       </xul:hbox>
     </content>
-    <implementation>
-      <constructor><![CDATA[
-        let learnMoreLink = document.getElementById("learnMoreLink");
-        learnMoreLink.setAttribute("value", this._stringBundle.GetStringFromName("insecureFieldWarningLearnMore"));
-        learnMoreLink.setAttribute("href", Services.urlFormatter.formatURLPref("app.support.baseURL") + "insecure-form-field-warning");
-      ]]></constructor>
-
-      <property name="_stringBundle">
-        <getter><![CDATA[
-          if (!this.__stringBundle) {
-            this.__stringBundle = Services.strings.createBundle("chrome://passwordmgr/locale/passwordmgr.properties");
-          }
-          return this.__stringBundle;
-        ]]></getter>
-      </property>
-    </implementation>
   </binding>
 
   <binding id="autocomplete-richlistitem" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
 
     <content align="center"
              onoverflow="this._onOverflow();"
              onunderflow="this._onUnderflow();">
       <xul:image anonid="type-icon"
--- a/toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutTelemetry.dtd
@@ -95,16 +95,20 @@ Ping
 <!ENTITY aboutTelemetry.chromeHangsSection "
   Browser Hangs
 ">
 
 <!ENTITY aboutTelemetry.threadHangStatsSection "
   Thread Hangs
 ">
 
+<!ENTITY aboutTelemetry.capturedStacksSection "
+  Captured Stacks
+">
+
 <!ENTITY aboutTelemetry.scalarsSection "
   Scalars
 ">
 
 <!ENTITY aboutTelemetry.keyedScalarsSection "
   Keyed Scalars
 ">
 
@@ -165,9 +169,9 @@ Ping
 ">
 
 <!ENTITY aboutTelemetry.payloadChoiceHeader "
   Payload
 ">
 
 <!ENTITY aboutTelemetry.rawPayload "
   Raw Payload
-">
\ No newline at end of file
+">
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordmgr.properties
@@ -67,9 +67,8 @@ loginsDescriptionFiltered=The following 
 loginHostAge=%1$S (%2$S)
 # LOCALIZATION NOTE (noUsername):
 # String is used on the context menu when a login doesn't have a username.
 noUsername=No username
 duplicateLoginTitle=Login already exists
 duplicateLogin=A duplicate login already exists.
 
 insecureFieldWarningDescription = This connection is not secure. Logins entered here could be compromised.
-insecureFieldWarningLearnMore = Learn More
--- a/widget/cocoa/nsLookAndFeel.mm
+++ b/widget/cocoa/nsLookAndFeel.mm
@@ -97,16 +97,21 @@ nsLookAndFeel::NativeGetColor(ColorID aI
       aColor = NS_RGB(0xff,0xff,0xff);
       break;
     case eColorID_TextForeground:
       aColor = NS_RGB(0x00,0x00,0x00);
       break;
     case eColorID_TextSelectBackground:
       aColor = GetColorFromNSColor([NSColor selectedTextBackgroundColor]);
       break;
+    // This is used to gray out the selection when it's not focused. Used with
+    // nsISelectionController::SELECTION_DISABLED.
+    case eColorID_TextSelectBackgroundDisabled:
+      aColor = GetColorFromNSColor([NSColor secondarySelectedControlColor]);
+      break;
     case eColorID_highlight: // CSS2 color
       aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]);
       break;
     case eColorID__moz_menuhover:
       aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]);
       break;      
     case eColorID_TextSelectForeground:
       GetColor(eColorID_TextSelectBackground, aColor);
--- a/widget/gtk/WidgetStyleCache.cpp
+++ b/widget/gtk/WidgetStyleCache.cpp
@@ -755,16 +755,37 @@ GetWidgetRootStyle(WidgetNodeType aNodeT
       break;
     case MOZ_GTK_CHECKMENUITEM:
       style = CreateStyleForWidget(gtk_check_menu_item_new(), MOZ_GTK_MENUPOPUP);
       break;
     case MOZ_GTK_RADIOMENUITEM:
       style = CreateStyleForWidget(gtk_radio_menu_item_new(nullptr),
                                    MOZ_GTK_MENUPOPUP);
       break;
+    case MOZ_GTK_TOOLTIP:
+      if (gtk_check_version(3, 20, 0) != nullptr) {
+          // The tooltip style class is added first in CreateTooltipWidget()
+          // and transfered to style in CreateStyleForWidget().
+          GtkWidget* tooltipWindow = CreateTooltipWidget();
+          style = CreateStyleForWidget(tooltipWindow, nullptr);
+          gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
+      } else {
+          // We create this from the path because GtkTooltipWindow is not public.
+          style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
+          gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
+      }
+      break;
+    case MOZ_GTK_TOOLTIP_BOX:
+      style = CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
+                                   MOZ_GTK_TOOLTIP);
+      break;
+    case MOZ_GTK_TOOLTIP_BOX_LABEL:
+      style = CreateStyleForWidget(gtk_label_new(nullptr),
+                                   MOZ_GTK_TOOLTIP_BOX);
+      break;
     default:
       GtkWidget* widget = GetWidget(aNodeType);
       MOZ_ASSERT(widget);
       return gtk_widget_get_style_context(widget);
   }
 
   MOZ_ASSERT(style);
   sStyleStorage[aNodeType] = style;
@@ -845,21 +866,16 @@ GetCssNodeStyleInternal(WidgetNodeType a
       /* Progress bar background (trough) */
       style = CreateChildCSSNode(GTK_STYLE_CLASS_TROUGH,
                                  MOZ_GTK_PROGRESSBAR);
       break;
     case MOZ_GTK_PROGRESS_CHUNK:
       style = CreateChildCSSNode("progress",
                                  MOZ_GTK_PROGRESS_TROUGH);
       break;
-    case MOZ_GTK_TOOLTIP:
-      // We create this from the path because GtkTooltipWindow is not public.
-      style = CreateCSSNode("tooltip", nullptr, GTK_TYPE_TOOLTIP);
-      gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND);
-      break; 
     case MOZ_GTK_GRIPPER:
       // TODO - create from CSS node
       return GetWidgetStyleWithClass(MOZ_GTK_GRIPPER,
                                      GTK_STYLE_CLASS_GRIP);
     case MOZ_GTK_INFO_BAR:
       // TODO - create from CSS node
       return GetWidgetStyleWithClass(MOZ_GTK_INFO_BAR,
                                      GTK_STYLE_CLASS_INFO);
@@ -981,30 +997,16 @@ GetWidgetStyleInternal(WidgetNodeType aN
       return GetWidgetStyleWithClass(MOZ_GTK_RADIOMENUITEM,
                                      GTK_STYLE_CLASS_RADIO);
     case MOZ_GTK_CHECKMENUITEM_INDICATOR:
       return GetWidgetStyleWithClass(MOZ_GTK_CHECKMENUITEM,
                                      GTK_STYLE_CLASS_CHECK);
     case MOZ_GTK_PROGRESS_TROUGH:
       return GetWidgetStyleWithClass(MOZ_GTK_PROGRESSBAR,
                                      GTK_STYLE_CLASS_TROUGH);
-    case MOZ_GTK_TOOLTIP: {
-      GtkStyleContext* style = sStyleStorage[aNodeType];
-      if (style)
-        return style;
-
-      // The tooltip style class is added first in CreateTooltipWidget() so
-      // that gtk_widget_path_append_for_widget() in CreateStyleForWidget()
-      // will find it.
-      GtkWidget* tooltipWindow = CreateTooltipWidget();
-      style = CreateStyleForWidget(tooltipWindow, nullptr);
-      gtk_widget_destroy(tooltipWindow); // Release GtkWindow self-reference.
-      sStyleStorage[aNodeType] = style;
-      return style;
-    }
     case MOZ_GTK_GRIPPER:
       return GetWidgetStyleWithClass(MOZ_GTK_GRIPPER,
                                      GTK_STYLE_CLASS_GRIP);
     case MOZ_GTK_INFO_BAR:
       return GetWidgetStyleWithClass(MOZ_GTK_INFO_BAR,
                                      GTK_STYLE_CLASS_INFO);
     case MOZ_GTK_SPINBUTTON_ENTRY:
       return GetWidgetStyleWithClass(MOZ_GTK_SPINBUTTON,
--- a/widget/gtk/gtk3drawing.cpp
+++ b/widget/gtk/gtk3drawing.cpp
@@ -1282,48 +1282,46 @@ moz_gtk_tooltip_paint(cairo_t *cr, const
     // Each element can be fully styled by CSS of GTK theme.
     // We have to draw all elements with appropriate offset and right dimensions.
 
     // Tooltip drawing
     GtkStyleContext* style = ClaimStyleContext(MOZ_GTK_TOOLTIP, direction);
     GdkRectangle rect = *aRect;
     gtk_render_background(style, cr, rect.x, rect.y, rect.width, rect.height);
     gtk_render_frame(style, cr, rect.x, rect.y, rect.width, rect.height);
+    ReleaseStyleContext(style);
 
     // Horizontal Box drawing
     //
     // The box element has hard-coded 6px margin-* GtkWidget properties, which
     // are added between the window dimensions and the CSS margin box of the
     // horizontal box.  The frame of the tooltip window is drawn in the
     // 6px margin.
     // For drawing Horizontal Box we have to inset drawing area by that 6px
     // plus its CSS margin.
-    GtkStyleContext* boxStyle =
-        CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0), style);
+    GtkStyleContext* boxStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX, direction);
 
     rect.x += 6;
     rect.y += 6;
     rect.width -= 12;
     rect.height -= 12;
 
     InsetByMargin(&rect, boxStyle);
     gtk_render_background(boxStyle, cr, rect.x, rect.y, rect.width, rect.height);
     gtk_render_frame(boxStyle, cr, rect.x, rect.y, rect.width, rect.height);
 
     // Label drawing
     InsetByBorderPadding(&rect, boxStyle);
+    ReleaseStyleContext(boxStyle);
 
     GtkStyleContext* labelStyle =
-        CreateStyleForWidget(gtk_label_new(nullptr), boxStyle);
+        ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL, direction);
     moz_gtk_draw_styled_frame(labelStyle, cr, &rect, false);
-    g_object_unref(labelStyle);
+    ReleaseStyleContext(labelStyle);
 
-    g_object_unref(boxStyle);
-
-    ReleaseStyleContext(style);
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
 moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect,
                       GtkWidgetState* state,
                       GtkTextDirection direction)
 {
@@ -2191,40 +2189,34 @@ moz_gtk_get_widget_border(WidgetNodeType
             ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_INFO_BAR:
         w = GetWidget(MOZ_GTK_INFO_BAR);
         break;
     case MOZ_GTK_TOOLTIP:
         {
-            style = ClaimStyleContext(MOZ_GTK_TOOLTIP);
             // In GTK 3 there are 6 pixels of additional margin around the box.
             // See details there:
             // https://github.com/GNOME/gtk/blob/5ea69a136bd7e4970b3a800390e20314665aaed2/gtk/ui/gtktooltipwindow.ui#L11
             *left = *right = *top = *bottom = 6;
 
             // We also need to add margin/padding/borders from Tooltip content.
             // Tooltip contains horizontal box, where icon and label is put.
             // We ignore icon as long as we don't have support for it.
-            GtkStyleContext* boxStyle =
-                CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
-                                     style);
+            GtkStyleContext* boxStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX);
             moz_gtk_add_margin_border_padding(boxStyle,
                                               left, top, right, bottom);
+            ReleaseStyleContext(boxStyle);
 
-            GtkStyleContext* labelStyle =
-                CreateStyleForWidget(gtk_label_new(nullptr), boxStyle);
+            GtkStyleContext* labelStyle = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL);
             moz_gtk_add_margin_border_padding(labelStyle,
                                               left, top, right, bottom);
+            ReleaseStyleContext(labelStyle);
 
-            g_object_unref(labelStyle);
-            g_object_unref(boxStyle);
-
-            ReleaseStyleContext(style);
             return MOZ_GTK_SUCCESS;
         }
     case MOZ_GTK_SCROLLBAR_VERTICAL:
     case MOZ_GTK_SCROLLBAR_TROUGH_HORIZONTAL:
         {
           if (gtk_check_version(3,20,0) == nullptr) {
             style = ClaimStyleContext(widget);
             moz_gtk_add_margin_border_padding(style, left, top, right, bottom);
--- a/widget/gtk/gtkdrawing.h
+++ b/widget/gtk/gtkdrawing.h
@@ -155,16 +155,20 @@ typedef enum {
   MOZ_GTK_DROPDOWN_ENTRY,
 
   /* Paints the background of a GtkHandleBox. */
   MOZ_GTK_TOOLBAR,
   /* Paints a toolbar separator */
   MOZ_GTK_TOOLBAR_SEPARATOR,
   /* Paints a GtkToolTip */
   MOZ_GTK_TOOLTIP,
+  /* Paints a GtkBox from GtkToolTip  */
+  MOZ_GTK_TOOLTIP_BOX,
+  /* Paints a GtkLabel of GtkToolTip */
+  MOZ_GTK_TOOLTIP_BOX_LABEL,
   /* Paints a GtkFrame (e.g. a status bar panel). */
   MOZ_GTK_FRAME,
   /* Paints the border of a GtkFrame */
   MOZ_GTK_FRAME_BORDER,
   /* Paints a resize grip for a GtkWindow */
   MOZ_GTK_RESIZER,
   /* Paints a GtkProgressBar. */
   MOZ_GTK_PROGRESSBAR,
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -1156,26 +1156,20 @@ nsLookAndFeel::Init()
     sMozWindowText = GDK_RGBA_TO_NS_RGBA(color);
     gtk_style_context_restore(style);
     g_object_unref(style);
 
     // tooltip foreground and background
     style = ClaimStyleContext(MOZ_GTK_TOOLTIP);
     gtk_style_context_get_background_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sInfoBackground = GDK_RGBA_TO_NS_RGBA(color);
-    {
-        GtkStyleContext* boxStyle =
-            CreateStyleForWidget(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0),
-                                 style);
-        GtkStyleContext* labelStyle =
-            CreateStyleForWidget(gtk_label_new(nullptr), boxStyle);
-        gtk_style_context_get_color(labelStyle, GTK_STATE_FLAG_NORMAL, &color);
-        g_object_unref(labelStyle);
-        g_object_unref(boxStyle);
-    }
+    ReleaseStyleContext(style);
+
+    style = ClaimStyleContext(MOZ_GTK_TOOLTIP_BOX_LABEL);
+    gtk_style_context_get_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sInfoText = GDK_RGBA_TO_NS_RGBA(color);
     ReleaseStyleContext(style);
 
     // menu foreground & menu background
     GtkWidget *accel_label = gtk_accel_label_new("M");
     GtkWidget *menuitem = gtk_menu_item_new();
     GtkWidget *menu = gtk_menu_new();
 
--- a/widget/nsXPLookAndFeel.cpp
+++ b/widget/nsXPLookAndFeel.cpp
@@ -764,22 +764,24 @@ nsXPLookAndFeel::GetColorImpl(ColorID aI
   }
 
   if (!aUseStandinsForNativeColors && IS_COLOR_CACHED(aID)) {
     aResult = sCachedColors[aID];
     return NS_OK;
   }
 
   // There are no system color settings for these, so set them manually
+#ifndef XP_MACOSX
   if (aID == eColorID_TextSelectBackgroundDisabled) {
     // This is used to gray out the selection when it's not focused
     // Used with nsISelectionController::SELECTION_DISABLED
     aResult = NS_RGB(0xb0, 0xb0, 0xb0);
     return NS_OK;
   }
+#endif
 
   if (aID == eColorID_TextSelectBackgroundAttention) {
     if (sFindbarModalHighlight) {
       aResult = NS_RGBA(0, 0, 0, 0);
       return NS_OK;
     }
 
     // This makes the selection stand out when typeaheadfind is on
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -1516,16 +1516,22 @@ public:
   // @param aArray    The elements to append to this array.
   // @param aArrayLen The number of elements to append to this array.
   // @return          A pointer to the new elements in the array, or null if
   //                  the operation failed due to insufficient memory.
 protected:
   template<class Item, typename ActualAlloc = Alloc>
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen);
 
+  template<class Item, size_t Length, typename ActualAlloc = Alloc>
+  elem_type* AppendElements(const mozilla::Array<Item, Length>& aArray)
+  {
+    return AppendElements<Item, ActualAlloc>(&aArray[0], Length);
+  }
+
 public:
 
   template<class Item>
   /* MOZ_MUST_USE */
   elem_type* AppendElements(const Item* aArray, size_type aArrayLen,
                             const mozilla::fallible_t&)
   {
     return AppendElements<Item, FallibleAlloc>(aArray, aArrayLen);