merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 24 Nov 2016 16:41:59 +0100
changeset 324182 2cd5b97ccf5a44569b87bb8f30ca0d7bebe060e8
parent 324077 9aef92f7911d35abc9520ffa0e802be3f4b92f5a (current diff)
parent 324181 7e68f80c7826318c9e15ae693c607e9fa689272a (diff)
child 324183 e43617e146e463a96c32a367e75c2a3b63a86e9e
child 324198 076d3bc7ff2320f5155518fa90f026d2efd77427
child 324259 1d7ac245464255493c907d5048ea9036542da981
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
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))