Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 13 Sep 2016 13:09:21 +0200
changeset 313810 2320bcd12315f543b46ea2fb9a41d1fe202fae0d
parent 313809 6fe90a668d040b5463e2fe730783a412365d3757 (current diff)
parent 313650 f5d043ce6d36a3c461cbd829d4a4a38394b7c436 (diff)
child 313811 1bca17123507305c26103c9719a4795d140897bf
push id30698
push usercbook@mozilla.com
push dateWed, 14 Sep 2016 10:07:43 +0000
treeherdermozilla-central@501e27643a52 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
build/moz.configure/toolchain.configure
devtools/client/locales/en-US/promisedebugger.properties
dom/media/fmp4/MP4Decoder.cpp
modules/libpref/init/all.js
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1287426 - Clobber required because of Linux Chromium sandbox file moves.
+Bug 1288460 requires another clobber due to bug 1298779.
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -253,16 +253,34 @@ EventTree::FindOrInsert(Accessible* aCon
     while (parent) {
       // Reached a top, no match for a current event.
       if (parent == top) {
         break;
       }
 
       // We got a match.
       if (parent->Parent() == node->mContainer) {
+        // Reject the node if it's contained by a show/hide event target
+        uint32_t evCount = node->mDependentEvents.Length();
+        for (uint32_t idx = 0; idx < evCount; idx++) {
+          AccMutationEvent* ev = node->mDependentEvents[idx];
+          if (ev->GetAccessible() == parent) {
+#ifdef A11Y_LOG
+            if (logging::IsEnabledAll(logging::eEventTree |
+                                      logging::eVerbose)) {
+              logging::MsgBegin("EVENTS_TREE",
+                "Rejecting node since contained by show/hide target");
+              logging::AccessibleInfo("Container", aContainer);
+              logging::MsgEnd();
+            }
+#endif
+            return nullptr;
+          }
+        }
+
         return node->FindOrInsert(aContainer);
       }
 
       parent = parent->Parent();
       MOZ_ASSERT(parent, "Wrong tree");
     }
 
     // If the given container contains a current node
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -441,16 +441,57 @@
         testIsDefunct(this.b);
       }
 
       this.getID = function test5_getID() {
         return "remove a child, remove a parent sibling, remove the parent";
       }
     }
 
+    /**
+     * Insert accessibles with a child node moved by aria-owns
+     * Markup:
+     * <div id="t6_fc">
+     *   <div id="t6_owns"></div>
+     * </div>
+     * <div id="t6_sc" aria-owns="t6_owns"></div>
+     */
+    function test6()
+    {
+      this.parent = getNode("t6");
+      this.fc = document.createElement("div");
+      this.fc.setAttribute("id", "t6_fc");
+      this.owns = document.createElement("div");
+      this.owns.setAttribute("id", "t6_owns");
+      this.sc = document.createElement("div");
+      this.sc.setAttribute("id", "t6_sc");
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_SHOW, this.fc),
+        new invokerChecker(EVENT_SHOW, this.sc),
+        new invokerChecker(EVENT_REORDER, this.parent),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.fc),
+        new unexpectedInvokerChecker(EVENT_REORDER, this.sc),
+        new unexpectedInvokerChecker(EVENT_HIDE, this.owns),
+        new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+      ];
+
+      this.invoke = function test6_invoke()
+      {
+        getNode("t6").appendChild(this.fc);
+        getNode("t6_fc").appendChild(this.owns);
+        getNode("t6").appendChild(this.sc);
+        getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
+      };
+
+      this.getID = function test6_getID() {
+        return "Insert accessibles with a child node moved by aria-owns";
+      };
+    }
+
     ////////////////////////////////////////////////////////////////////////////
     // Do tests.
 
     //gA11yEventDumpToConsole = true; // debug stuff
     //enableLogging("tree,eventTree,verbose");
 
     var gQueue = null;
     function doTests()
@@ -472,16 +513,17 @@
       gQueue.push(new showParentNChild("select10", "option10", true));
       gQueue.push(new showParentNAddChild("select11", false));
       gQueue.push(new showParentNAddChild("select12", true));
 
       gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
       gQueue.push(new test3());
       gQueue.push(new test4());
       gQueue.push(new test5());
+      gQueue.push(new test6());
 
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 </head>
@@ -562,10 +604,13 @@
   </div>
 
   <div id="t5">
     <div role="button" id="t5_b">btn</div>
     <div role="listbox" id="t5_lb">
       <div role="option" id="t5_o">opt</div>
     </div>
   </div>
+
+  <div id="t6">
+  </div>
 </body>
 </html>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -5,18 +5,16 @@
 
 #include "nsXULAppAPI.h"
 #include "mozilla/AppData.h"
 #include "application.ini.h"
 #include "nsXPCOMGlue.h"
 #if defined(XP_WIN)
 #include <windows.h>
 #include <stdlib.h>
-#include <io.h>
-#include <fcntl.h>
 #elif defined(XP_UNIX)
 #include <sys/resource.h>
 #include <unistd.h>
 #endif
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <time.h>
@@ -279,18 +277,18 @@ InitXPCOMGlue(const char *argv0, nsIFile
 
   nsresult rv = mozilla::BinaryPath::Get(argv0, exePath);
   if (NS_FAILED(rv)) {
     Output("Couldn't find the application directory.\n");
     return rv;
   }
 
   char *lastSlash = strrchr(exePath, XPCOM_FILE_PATH_SEPARATOR[0]);
-  if (!lastSlash || (size_t(lastSlash - exePath) > MAXPATHLEN -
-sizeof(XPCOM_DLL) - 1))
+  if (!lastSlash ||
+      (size_t(lastSlash - exePath) > MAXPATHLEN - sizeof(XPCOM_DLL) - 1))
     return NS_ERROR_FAILURE;
 
   strcpy(lastSlash + 1, XPCOM_DLL);
 
   if (!FileExists(exePath)) {
     Output("Could not find the Mozilla runtime.\n");
     return NS_ERROR_FAILURE;
   }
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -420,22 +420,16 @@ Section "-Application" APP_IDX
     StrCpy $0 "Software\Clients\StartMenuInternet\$R9\InstallInfo"
     ${If} $AddDesktopSC == 1
     ${OrIf} $AddStartMenuSC == 1
     ${OrIf} $AddTaskbarSC == 1
       WriteRegDWORD HKCU "$0" "IconsVisible" 1
     ${Else}
       WriteRegDWORD HKCU "$0" "IconsVisible" 0
     ${EndIf}
-  ${If} ${AtLeastWin8}
-    ${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
-                             $AppUserModelID \
-                             "FirefoxURL" \
-                             "FirefoxHTML"
-  ${EndIf}
   ${EndIf}
 
 !ifdef MOZ_MAINTENANCE_SERVICE
   ; If the maintenance service page was displayed then a value was already 
   ; explicitly selected for installing the maintenance service and 
   ; and so InstallMaintenanceService will already be 0 or 1.
   ; If the maintenance service page was not displayed then 
   ; InstallMaintenanceService will be equal to "".
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -1,35 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-; The registration ID of the COM server which is used for choosing wether
-; to launch the Win8 metro browser or desktop browser.
-!define DELEGATE_EXECUTE_HANDLER_ID {5100FEC1-212B-4BF5-9BF8-3E650FD794A3}
-;
-; Defines for adjust token privs and for enumerating keys
-!ifndef TOKEN_QUERY
-  !define TOKEN_QUERY             0x0008
-!endif
-!ifndef TOKEN_ADJUST_PRIVILEGES
-  !define TOKEN_ADJUST_PRIVILEGES 0x0020
-!endif
-!ifndef SE_RESTORE_NAME
-  !define SE_RESTORE_NAME         SeRestorePrivilege
-!endif
-!ifndef SE_PRIVILEGE_ENABLED
-  !define SE_PRIVILEGE_ENABLED    0x00000002
-!endif
-!ifndef HKEY_USERS
-  !define HKEY_USERS              0x80000003
-!endif
-
 !macro PostUpdate
-
   ; PostUpdate is called from both session 0 and from the user session
   ; for service updates, make sure that we only register with the user session
   ; Otherwise ApplicationID::Set can fail intermittently with a file in use error.
   System::Call "kernel32::GetCurrentProcessId() i.r0"
   System::Call "kernel32::ProcessIdToSessionId(i $0, *i ${NSIS_MAX_STRLEN} r9)"
 
   ; Determine if we're the protected UserChoice default or not. If so fix the
   ; start menu tile.  In case there are 2 Firefox installations, we only do
@@ -186,24 +164,16 @@
       ; based on the above checks, so attempt to just run the EXE.
       ; In the worst case, in case there is some edge case with the
       ; IsAdmin check and the permissions check, the maintenance service
       ; will just fail to be attempted to be installed.
       nsExec::Exec "$\"$INSTDIR\maintenanceservice_installer.exe$\""
     ${EndIf}
   ${EndIf}
 !endif
-
-; Register the DEH
-${If} ${AtLeastWin8}
-  ${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
-                           $AppUserModelID \
-                           "FirefoxURL" \
-                           "FirefoxHTML"
-${EndIf}
 !macroend
 !define PostUpdate "!insertmacro PostUpdate"
 
 !macro SetAsDefaultAppGlobal
   ${RemoveDeprecatedKeys} ; Does not use SHCTX
 
   SetShellVarContext all      ; Set SHCTX to all users (e.g. HKLM)
   ${SetHandlers} ; Uses SHCTX
@@ -826,178 +796,16 @@
   StrCpy $0 "Software\Microsoft\Windows\Shell\Associations\UrlAssociations\gopher"
   ReadRegStr $2 HKCU "$0\UserChoice" "Progid"
   ${If} "$2" == "FirefoxURL"
     DeleteRegKey HKCU "$0"
   ${EndIf}
 !macroend
 !define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys"
 
-; Resets Win8+ specific toast keys Windows sets. We call this on a
-; fresh install and on uninstall.
-!macro ResetWin8PromptKeys KEY PREFIX
-  ${If} ${AtLeastWin8}
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.htm"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.html"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xht"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.xhtml"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxHTML_.shtml"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_ftp"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_http"
-    DeleteRegValue ${KEY} "${PREFIX}Software\Microsoft\Windows\CurrentVersion\ApplicationAssociationToasts" "FirefoxURL_https"
-  ${EndIf}
-!macroend
-!define ResetWin8PromptKeys "!insertmacro ResetWin8PromptKeys"
-
-; Adds SE_RESTORE_NAME privs
-!macro AcquireSERestoreName
-  StrCpy $R1 0
-
-  System::Call "kernel32::GetCurrentProcess() i .R0"
-  System::Call "advapi32::OpenProcessToken(i R0, i ${TOKEN_QUERY}|${TOKEN_ADJUST_PRIVILEGES}, \
-                                          *i R1R1) i .R0"
-  ${If} $R0 != 0
-    System::Call "advapi32::LookupPrivilegeValue(t n, t '${SE_RESTORE_NAME}', *l .R2) i .R0"
-    ${If} $R0 != 0
-      System::Call "*(i 1, l R2, i ${SE_PRIVILEGE_ENABLED}) i .R0"
-      System::Call "advapi32::AdjustTokenPrivileges(i R1, i 0, i R0, i 0, i 0, i 0)"
-      System::Free $R0
-    ${EndIf}
-    System::Call "kernel32::CloseHandle(i R1)"
-  ${EndIf}
-!macroend
-!define AcquireSERestoreName "!insertmacro AcquireSERestoreName"
-!define un.AcquireSERestoreName "!insertmacro AcquireSERestoreName"
-
-; Mounts all user ntuser.dat files into the registry as a subkey of HKU
-!macro MountRegistryIntoHKU
-  ; $0 is used as an index for HKEY_USERS enumeration
-  StrCpy $0 0
-  ${Do}
-    EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
-    ${If} $1 == ""
-      ${Break}
-    ${EndIf}
-    ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
-    System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0', t '$2\ntuser.dat')"
-    System::Call "advapi32::RegLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes', t '$2\AppData\Local\Microsoft\Windows\UsrClass.dat')"
-    IntOp $0 $0 + 1
-  ${Loop}
-!macroend
-!define MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
-!define un.MountRegistryIntoHKU "!insertmacro MountRegistryIntoHKU"
-
-; Unmounts all user ntuser.dat files into the registry as a subkey of HKU
-!macro UnmountRegistryIntoHKU
-  ; $0 is used as an index for HKEY_USERS enumeration
-  StrCpy $0 0
-  ${Do}
-    EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $0
-    ${If} $1 == ""
-      ${Break}
-    ${EndIf}
-    ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$1" "ProfileImagePath"
-    System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0')"
-    System::Call "advapi32::RegUnLoadKey(i ${HKEY_USERS}, t 'User-$0_Classes')"
-    IntOp $0 $0 + 1
-  ${Loop}
-!macroend
-!define UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
-!define un.UnmountRegistryIntoHKU "!insertmacro UnmountRegistryIntoHKU"
-
-; Unconditionally removes the delegate execute handler registration used to
-; launch the metro browser and misc. metro related registry values.
-!macro RemoveDEHRegistration DELEGATE_EXECUTE_HANDLER_ID \
-                             APP_USER_MODEL_ID \
-                             PROTOCOL_ACTIVATION_ID \
-                             FILE_ACTIVATION_ID
-  ${AcquireSERestoreName}
-  ${MountRegistryIntoHKU}
-
-  ; $0 is used as an index for HKEY_USERS enumeration
-  StrCpy $0 0
-
-  ${Do}
-    EnumRegKey $1 HKU "" $0
-    ${If} $1 == ""
-      ${Break}
-    ${EndIf}
-
-    ClearErrors
-    ${WordFind} "$1" "_Classes" "E#" $3
-    ${Unless} ${Errors}
-      ; remove the app user model id root registration. We don't need this
-      ; here anymore, we just use it for tray registrationdown in widget,
-      ; which we read out of the mozilla keys.
-      ${If} "${APP_USER_MODEL_ID}" != ""
-        ; The removal of this key intermittently fails, so do the best we can in cleanup
-        DeleteRegValue HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
-        DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell\open"
-        DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe\shell"
-        DeleteRegKey HKU "$1\${APP_USER_MODEL_ID}\.exe"
-        DeleteRegKey HKU "$1\\${APP_USER_MODEL_ID}"
-      ${EndIf}
-      ;
-      ; Remove delegate execute handler clsid registration
-      DeleteRegKey HKU "$1\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
-
-      ; Remove protocol and file delegate execute handler id assoc
-      DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
-      DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}" "AppUserModelID"
-
-      ; Remove delegate execute application registry keys
-      DeleteRegKey HKU "$1\${PROTOCOL_ACTIVATION_ID}\Application"
-      DeleteRegKey HKU "$1\${FILE_ACTIVATION_ID}\Application"
-
-      ; Remove misc. shell open info
-      DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
-      DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
-      DeleteRegValue HKU "$1\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
-      DeleteRegValue HKU "$1\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
-
-      ; remove metro browser splash image data
-      DeleteRegKey HKU "$1\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData\DefaultBrowser_NOPUBLISHERID\SplashScreen\DefaultBrowser_NOPUBLISHERID!${APP_USER_MODEL_ID}"
-    ${Else}
-      ; misc. Metro keys
-      DeleteRegKey HKU "$1\Software\Mozilla\Firefox\Metro"
-      DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "CEHDump"
-      DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroD3DAvailable"
-      DeleteRegValue HKU "$1\Software\Mozilla\Firefox" "MetroLastAHE"
-      ${ResetWin8PromptKeys} "HKU" "$1\"
-    ${EndIf}
-    IntOp $0 $0 + 1
-  ${Loop}
-  ${UnmountRegistryIntoHKU}
-
-  ; The removal of this key intermittently fails, so do the best we can in cleanup
-  ${If} "${APP_USER_MODEL_ID}" != ""
-    DeleteRegValue HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open\command" "DelegateExecute"
-    DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell\open"
-    DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe\shell"
-    DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}\.exe"
-    DeleteRegKey HKLM "Software\Classes\${APP_USER_MODEL_ID}"
-  ${EndIf}
-
-  ; Remove HKLM entries
-  DeleteRegKey HKLM "Software\Classes\CLSID\${DELEGATE_EXECUTE_HANDLER_ID}"
-  DeleteRegKey HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\Application"
-  DeleteRegKey HKLM "Software\Classes\${FILE_ACTIVATION_ID}\Application"
-  DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
-  DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open\command" "DelegateExecute"
-  DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}" "AppUserModelID"
-  DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}" "AppUserModelID"
-  DeleteRegValue HKLM "Software\Classes\${PROTOCOL_ACTIVATION_ID}\shell\open" "CommandId"
-  DeleteRegValue HKLM "Software\Classes\${FILE_ACTIVATION_ID}\shell\open" "CommandId"
-
-  ClearErrors
-!macroend
-
-!define RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
-!define un.RemoveDEHRegistration "!insertmacro RemoveDEHRegistration"
-
 ; Removes various directories and files for reasons noted below.
 !macro RemoveDeprecatedFiles
   ; Some users are ending up with unpacked chrome instead of omni.ja. This
   ; causes Firefox to break badly after upgrading from Firefox 31, see bug
   ; 1063052. Removing the chrome.manifest from the install directory causes
   ; Firefox to use the updated omni.ja so it won't crash.
   ${If} ${FileExists} "$INSTDIR\chrome.manifest"
     Delete "$INSTDIR\chrome.manifest"
--- a/browser/installer/windows/nsis/uninstaller.nsi
+++ b/browser/installer/windows/nsis/uninstaller.nsi
@@ -273,23 +273,16 @@ Section "Uninstall"
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
     StrCpy $TmpVal "HKLM" ; used primarily for logging
     ${un.RegCleanMain} "Software\Mozilla"
     ${un.RegCleanUninstall}
     ${un.DeleteShortcuts}
     ${un.SetAppLSPCategories}
   ${EndIf}
 
-  ${If} ${AtLeastWin8}
-    ${RemoveDEHRegistration} ${DELEGATE_EXECUTE_HANDLER_ID} \
-                             $AppUserModelID \
-                             "FirefoxURL" \
-                             "FirefoxHTML"
-  ${EndIf}
-
   ${un.RegCleanAppHandler} "FirefoxURL"
   ${un.RegCleanAppHandler} "FirefoxHTML"
   ${un.RegCleanProtocolHandler} "ftp"
   ${un.RegCleanProtocolHandler} "http"
   ${un.RegCleanProtocolHandler} "https"
 
   ClearErrors
   ReadRegStr $R9 HKCR "FirefoxHTML" ""
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -28,16 +28,19 @@
 
   --toolbarbutton-active-background: rgba(154,154,154,.5) linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.4));
   --toolbarbutton-active-bordercolor: rgba(0,0,0,.3);
   --toolbarbutton-active-boxshadow: 0 1px 1px rgba(0,0,0,.1) inset, 0 0 1px rgba(0,0,0,.3) inset;
 
   --toolbarbutton-checkedhover-backgroundcolor: rgba(200,200,200,.5);
 
   --panel-separator-color: ThreeDShadow;
+  --arrowpanel-dimmed: hsla(210,4%,10%,.07);
+  --arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
+  --arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
 
   --urlbar-separator-color: ThreeDShadow;
 }
 
 #menubar-items {
   -moz-box-orient: vertical; /* for flex hack */
 }
 
@@ -990,35 +993,35 @@ toolbaritem[cui-areatype="menu-panel"] >
 }
 
 #urlbar-zoom-button > .toolbarbutton-icon {
   display: none;
 }
 
 #urlbar-search-footer {
   border-top: 1px solid var(--panel-separator-color);
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings {
   -moz-appearance: none;
   -moz-user-focus: ignore;
   color: GrayText;
   margin: 0;
   border: 0;
   padding: 8px 20px;
   background: transparent;
 }
 
 #urlbar-search-settings:hover {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings:hover:active {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 #urlbar-search-splitter {
   -moz-appearance: none;
   width: 8px;
   margin-inline-start: -4px;
 }
 
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -132,17 +132,17 @@ menuitem[cmd="cmd_clearhistory"][disable
 }
 
 .search-panel-tree {
   border-top: 1px solid var(--panel-separator-color) !important;
 }
 
 .search-panel-header {
   font-weight: normal;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   border: none;
   border-top: 1px solid var(--panel-separator-color);
   padding: 3px 5px;
   color: GrayText;
 }
 
 .search-panel-header > label {
   margin-top: 2px !important;
@@ -296,17 +296,17 @@ menuitem[cmd="cmd_clearhistory"][disable
 
 .search-setting-button {
   -moz-appearance: none;
   margin: 0;
   min-height: 32px;
 }
 
 .search-setting-button[selected] {
-  background-color: hsla(210,4%,10%,.15);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact {
   list-style-image: url("chrome://browser/skin/gear.svg#gear");
 }
 
 .search-setting-button-compact[selected] {
   list-style-image: url("chrome://browser/skin/gear.svg#gear-inverted");
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -39,16 +39,19 @@
   --urlbar-dropmarker-url: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   --urlbar-dropmarker-region: rect(0, 11px, 14px, 0);
   --urlbar-dropmarker-active-region: rect(0, 22px, 14px, 11px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
   --urlbar-dropmarker-active-2x-region: rect(0, 44px, 28px, 22px);
 
   --panel-separator-color: hsla(210,4%,10%,.14);
+  --arrowpanel-dimmed: hsla(210,4%,10%,.07);
+  --arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
+  --arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
 
   --urlbar-separator-color: hsla(0,0%,16%,.2);
 }
 
 #urlbar:-moz-lwtheme:not([focused="true"]),
 .searchbar-textbox:-moz-lwtheme:not([focused="true"]) {
   opacity: .9;
 }
@@ -1703,33 +1706,33 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 }
 
 #urlbar-zoom-button > .toolbarbutton-icon {
   display: none;
 }
 
 #urlbar-search-footer {
   border-top: 1px solid var(--panel-separator-color);
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings {
   -moz-appearance: none;
   -moz-user-focus: ignore;
   color: GrayText;
   margin: 0;
   padding: 8px 20px;
 }
 
 #urlbar-search-settings:hover {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings:hover:active {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 #urlbar-search-splitter {
   min-width: 8px;
   width: 8px;
   background-image: none;
   margin: 0 -4px;
   position: relative;
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -122,17 +122,17 @@
 
 .search-panel-tree {
   border-top: 1px solid var(--panel-separator-color) !important;
 }
 
 .search-panel-header {
   font-size: 10px;
   font-weight: normal;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
   margin: 0;
   padding: 3px 6px;
   color: GrayText;
 }
 
 .search-panel-header > label {
   margin-top: 2px !important;
@@ -278,17 +278,17 @@
 
 .search-setting-button {
   -moz-appearance: none;
   border-radius: 0 0 4px 4px;
   min-height: 32px;
 }
 
 .search-setting-button[selected] {
-  background-color: hsla(210,4%,10%,.15);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact {
   list-style-image: url("chrome://browser/skin/gear.svg#gear");
 }
 
 .search-setting-button-compact[selected] {
   list-style-image: url("chrome://browser/skin/gear.svg#gear-inverted");
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -151,21 +151,21 @@
 }
 
 .identity-popup-expander:-moz-focusring > .button-box,
 .identity-popup-expander[panel-multiview-anchor] > .button-box {
   border-style: none;
 }
 
 .identity-popup-expander:hover {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 .identity-popup-expander:hover:active {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 /* CONTENT */
 
 .identity-popup-permission-label,
 .identity-popup-permission-state-label,
 #identity-popup-security-content > description,
@@ -273,36 +273,36 @@
 }
 
 #identity-popup-securityView-body {
   padding-inline-end: 1em;
 }
 
 #identity-popup-securityView-footer {
   margin-top: 1em;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #identity-popup-securityView-footer > button {
   -moz-appearance: none;
   margin: 0;
   border: none;
   border-top: 1px solid var(--panel-separator-color);
   padding: 8px 20px;
   color: inherit;
   background-color: transparent;
 }
 
 #identity-popup-securityView-footer > button:hover,
 #identity-popup-securityView-footer > button:focus {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #identity-popup-securityView-footer > button:hover:active {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 #identity-popup-content-verifier ~ description {
   margin-top: 1em;
   color: Graytext;
 }
 
 description#identity-popup-content-verified-by,
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -402,17 +402,17 @@ toolbarpaletteitem:-moz-any([place="pale
   width: 16px;
   margin-inline-start: -16px;
   height: 51px;
   margin-bottom: 2.2em;
   padding: 0;
 }
 
 .panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker {
-  background-color: hsla(210,4%,10%,.1) !important;
+  background-color: var(--arrowpanel-dimmed) !important;
   border-radius: 0 0 0 2px;
 }
 
 .panelUI-grid .toolbarbutton-1:not([buttonover])@buttonStateHover@ > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) {
   border-radius: 0 0 2px 0;
 }
 
 #main-window:not([customizing]) .panel-combined-button[disabled] > .toolbarbutton-icon {
@@ -429,17 +429,17 @@ toolbaritem[cui-areatype="menu-panel"][s
   -moz-box-pack: center;
 }
 
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe {
   margin: 4px auto;
 }
 
 #PanelUI-multiView[viewtype="subview"] > .panel-viewcontainer > .panel-viewstack > .panel-mainview >  #PanelUI-mainView {
-  background-color: hsla(210,4%,10%,.1);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .panel-wide-item,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-contents-scroller > #PanelUI-contents > .toolbarbutton-1:not([panel-multiview-anchor="true"]),
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-update-status,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-avatar,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-status > #PanelUI-fxa-label,
 #PanelUI-multiView[viewtype="subview"] #PanelUI-mainView > #PanelUI-footer > #PanelUI-footer-fxa > #PanelUI-fxa-icon,
@@ -530,48 +530,48 @@ toolbarpaletteitem[place="palette"] > to
 #zoom-reset-button > .toolbarbutton-icon {
   display: none;
 }
 
 #PanelUI-footer {
   display: flex;
   flex-shrink: 0;
   flex-direction: column;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   padding: 0;
   margin: 0;
 }
 
 #main-window[customizing] #PanelUI-footer-fxa {
   display: none;
 }
 
 #PanelUI-footer-fxa:not([fxastatus="signedin"]) > toolbarseparator,
 #PanelUI-footer-fxa:not([fxastatus="signedin"]) > #PanelUI-fxa-icon,
 #PanelUI-footer-fxa:not([fxaprofileimage]) > #PanelUI-fxa-status > #PanelUI-fxa-avatar {
   display: none;
 }
 
 #PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status::after {
   content: url(chrome://browser/skin/warning.svg);
-  filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
+  filter: drop-shadow(0 1px 0 hsla(206,50%,10%,.15));
   width: 47px;
   padding-top: 1px;
   display: block;
   text-align: center;
   position: relative;
   top: 25%;
 }
 
 #PanelUI-update-status[update-status]::after {
   content: "";
   width: 14px;
   height: 14px;
   margin-right: 16.5px;
-  box-shadow: 0px 1px 0px rgba(255, 255, 255, 0.2) inset, 0px -1px 0px rgba(0, 0, 0, 0.1) inset, 0px 1px 0px rgba(12, 27, 38, 0.2);
+  box-shadow: 0px 1px 0px rgba(255,255,255,.2) inset, 0px -1px 0px rgba(0,0,0,.1) inset, 0px 1px 0px rgba(12,27,38,.2);
   border-radius: 2px;
   background-size: contain;
   display: -moz-box;
 }
 
 #PanelUI-update-status[update-status="succeeded"]::after {
   background-image: url(chrome://browser/skin/update-badge.svg);
   background-color: #74BF43;
@@ -904,77 +904,77 @@ toolbarpaletteitem[place="palette"] > to
   opacity: 0.4;
 }
 
 #PanelUI-fxa-status:not([disabled]):hover,
 #PanelUI-fxa-icon:not([disabled]):hover,
 #PanelUI-help:not([disabled]):hover,
 #PanelUI-customize:hover,
 #PanelUI-quit:not([disabled]):hover {
-  outline: 1px solid hsla(210,4%,10%,.07);
-  background-color: hsla(210,4%,10%,.07);
+  outline: 1px solid var(--arrowpanel-dimmed);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #PanelUI-fxa-status:not([disabled]):hover:active,
 #PanelUI-fxa-icon:not([disabled]):hover:active,
 #PanelUI-help:not([disabled]):hover:active,
 #PanelUI-customize:hover:active,
 #PanelUI-quit:not([disabled]):hover:active {
-  outline: 1px solid hsla(210,4%,10%,.12);
-  background-color: hsla(210,4%,10%,.12);
+  outline: 1px solid var(--arrowpanel-dimmed-further);
+  background-color: var(--arrowpanel-dimmed-further);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 #PanelUI-fxa-status:not([disabled]):hover,
 #PanelUI-fxa-status:not([disabled]):hover:active,
 #PanelUI-fxa-icon:not([disabled]):hover,
 #PanelUI-fxa-icon:not([disabled]):hover:active {
   outline: none;
 }
 
 #PanelUI-footer-fxa[fxastatus="error"] {
-  background-color: hsla(42, 94%, 88%, 1.0);
-  border-top: 1px solid hsla(42, 94%, 70%, 1.0);
+  background-color: hsl(42,94%,88%);
+  border-top: 1px solid hsl(42,94%,70%);
 }
 
 #PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover {
-  background-color: hsla(42, 94%, 85%, 1.0);
+  background-color: hsl(42,94%,85%);
 }
 
 #PanelUI-footer-fxa[fxastatus="error"] > #PanelUI-fxa-status:hover:active {
-  background-color: hsla(42, 94%, 82%, 1.0);
+  background-color: hsl(42,94%,82%);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 #PanelUI-update-status {
   color: black;
 }
 
 #PanelUI-update-status[update-status="succeeded"] {
-  background-color: hsla(96, 65%, 75%, 0.5);
+  background-color: hsla(96,65%,75%,.5);
 }
 
 #PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover {
-  background-color: hsla(96, 65%, 75%, 0.8);
+  background-color: hsla(96,65%,75%,.8);
 }
 
 #PanelUI-update-status[update-status="succeeded"]:not([disabled]):hover:active {
-  background-color: hsl(96, 65%, 75%);
+  background-color: hsl(96,65%,75%);
 }
 
 #PanelUI-update-status[update-status="failed"] {
-  background-color: hsla(359, 69%, 84%, 0.5);
+  background-color: hsla(359,69%,84%,.5);
 }
 
 #PanelUI-update-status[update-status="failed"]:not([disabled]):hover {
-  background-color: hsla(359, 69%, 84%, 0.8);
+  background-color: hsla(359,69%,84%,.8);
 }
 
 #PanelUI-update-status[update-status="failed"]:not([disabled]):hover:active {
-  background-color: hsl(359, 69%, 84%);
+  background-color: hsl(359,69%,84%);
 }
 
 #PanelUI-quit:not([disabled]):hover {
   background-color: #d94141;
   outline-color: #c23a3a;
 }
 
 #PanelUI-quit:not([disabled]):hover:active {
@@ -1006,20 +1006,20 @@ toolbarpaletteitem[place="palette"] > to
 panelview .toolbarbutton-1,
 .subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 .share-provider-button,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
   -moz-appearance: none;
   padding: 0 6px;
-  background-color: hsla(210,4%,10%,0);
+  background-color: transparent;
   border-radius: 2px;
   border-style: solid;
-  border-color: hsla(210,4%,10%,0);
+  border-color: transparent;
 }
 
 panelview .toolbarbutton-1,
 .subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
 .share-provider-button,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
   border-width: 1px;
@@ -1092,51 +1092,51 @@ panelview .toolbarbutton-1,
 
 panelview .toolbarbutton-1@buttonStateHover@,
 toolbarbutton.subviewbutton@buttonStateHover@,
 menu.subviewbutton@menuStateHover@,
 menuitem.subviewbutton@menuStateHover@,
 .share-provider-button@buttonStateHover@:not([checked="true"]),
 .widget-overflow-list .toolbarbutton-1@buttonStateHover@,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
-  background-color: hsla(210,4%,10%,.08);
-  border-color: hsla(210,4%,10%,.11);
+  background-color: var(--arrowpanel-dimmed);
+  border-color: var(--panel-separator-color);
 }
 
 .toolbaritem-combined-buttons@inAnyPanel@@buttonStateHover@ {
-  border-color: hsla(210,4%,10%,.11);
+  border-color: var(--panel-separator-color);
 }
 
 panelview .toolbarbutton-1:-moz-any(@buttonStateActive@,[checked=true]),
 toolbarbutton.subviewbutton@buttonStateActive@,
 menu.subviewbutton@menuStateActive@,
 menuitem.subviewbutton@menuStateActive@,
 .share-provider-button:-moz-any(@buttonStateActive@,[checked=true]),
 .widget-overflow-list .toolbarbutton-1@buttonStateActive@,
 .toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
   border-color: var(--panel-separator-color);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
 }
 
 .subviewbutton.panel-subview-footer {
   margin: 4px -4px -4px;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
   border-radius: 0;
 }
 
 menuitem.panel-subview-footer@menuStateHover@,
 .subviewbutton.panel-subview-footer@buttonStateHover@ {
-  background-color: hsla(210,4%,10%,.15);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 menuitem.panel-subview-footer@menuStateActive@,
 .subviewbutton.panel-subview-footer@buttonStateActive@ {
-  background-color: hsla(210,4%,10%,.19);
+  background-color: var(--arrowpanel-dimmed-even-further);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 #BMB_bookmarksPopup .subviewbutton {
   font: menu;
   font-weight: normal;
 }
 
@@ -1293,21 +1293,21 @@ toolbarpaletteitem[place="palette"] > .t
 }
 
 toolbarpaletteitem[place="palette"] > #search-container {
   min-width: 7em;
   width: 7em;
 }
 
 .toolbaritem-combined-buttons@inAnyPanel@ {
-  background-color: hsla(210,4%,10%,0);
+  background-color: transparent;
   border-radius: 2px;
   border: 1px solid;
-  border-color: hsla(210,4%,10%,0);
-  border-bottom-color: hsla(210,4%,10%,.1);
+  border-color: transparent;
+  border-bottom-color: var(--panel-separator-color);
   padding: 0;
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
 /* Make direct siblings overlap borders: */
 .toolbaritem-combined-buttons + .toolbaritem-combined-buttons@inAnyPanel@ {
   border-top-color: transparent !important;
@@ -1476,17 +1476,17 @@ menuitem[checked="true"].subviewbutton >
 }
 
 #PanelUI-panicView.cui-widget-panelview {
   min-width: 280px;
 }
 
 #PanelUI-panic-timeframe {
   padding: 15px;
-  border-bottom: 1px solid rgba(0,0,0,0.1);
+  border-bottom: 1px solid var(--panel-separator-color);
 }
 
 #panic-button-success-icon,
 #PanelUI-panic-timeframe-icon,
 #PanelUI-panic-timeframe-icon-small {
   background-color: transparent;
   margin-inline-end: 10px;
 }
@@ -1535,24 +1535,24 @@ menuitem[checked="true"].subviewbutton >
   padding: 1px;
   margin: 0 0 2px;
   background-color: transparent;
   border-radius: 2px;
   border: 1px solid transparent;
 }
 
 .subviewradio@buttonStateHover@ {
-  background-color: hsla(210,4%,10%,.08);
+  background-color: var(--arrowpanel-dimmed);
   border-color: var(--panel-separator-color);
 }
 
 .subviewradio[selected],
 .subviewradio[selected]:hover,
 .subviewradio@buttonStateActive@ {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
   border-color: var(--panel-separator-color);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.03) inset;
 }
 
 .subviewradio > .radio-check {
   -moz-appearance: none;
   width: 16px;
   height: 16px;
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -36,17 +36,17 @@
   padding: 16px 25px;
   margin: 0;
   /* The panel can be wider than this description after the blocked subview is
      shown, so center the text. */
   text-align: center;
 }
 
 .downloadsPanelFooter {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   border-top: 1px solid var(--panel-separator-color);
 }
 
 .downloadsPanelFooter toolbarseparator {
   margin: 0;
   border: 0;
   min-width: 0;
   border-left: 1px solid var(--panel-separator-color);
@@ -60,24 +60,24 @@
   margin: 0;
   padding: 0;
   min-width: 0;
   min-height: 40px;
   border: none;
 }
 
 .downloadsPanelFooterButton:hover {
-  outline: 1px solid hsla(210,4%,10%,.07);
-  background-color: hsla(210,4%,10%,.07);
+  outline: 1px solid var(--arrowpanel-dimmed);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 .downloadsPanelFooterButton:hover:active,
 .downloadsPanelFooterButton[open="true"] {
-  outline: 1px solid hsla(210,4%,10%,.12);
-  background-color: hsla(210,4%,10%,.12);
+  outline: 1px solid var(--arrowpanel-dimmed-further);
+  background-color: var(--arrowpanel-dimmed-further);
   box-shadow: 0 1px 0 hsla(210,4%,10%,.05) inset;
 }
 
 .downloadsPanelFooterButton[default] {
   background-color: #0996f8;
   color: white;
 }
 
--- a/browser/themes/shared/plugin-doorhanger.inc.css
+++ b/browser/themes/shared/plugin-doorhanger.inc.css
@@ -21,18 +21,18 @@
   background-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
   background-repeat: no-repeat;
   width: 16px;
   height: 15px;
   margin-inline-start: 6px;
 }
 
 .click-to-play-plugins-notification-button-container {
-  background-color: hsla(210,4%,10%,.07);
-  border-top: 1px solid hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed);
+  border-top: 1px solid var(--panel-separator-color);
   padding: 10px;
   margin-top: 5px;
 }
 
 .click-to-play-popup-button {
   width: 50%;
 }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -41,16 +41,19 @@
   --urlbar-dropmarker-hover-region: rect(0px, 22px, 14px, 11px);
   --urlbar-dropmarker-active-region: rect(0px, 33px, 14px, 22px);
   --urlbar-dropmarker-2x-url: url("chrome://browser/skin/urlbar-history-dropmarker@2x.png");
   --urlbar-dropmarker-2x-region: rect(0, 22px, 28px, 0);
   --urlbar-dropmarker-hover-2x-region: rect(0, 44px, 28px, 22px);
   --urlbar-dropmarker-active-2x-region: rect(0, 66px, 28px, 44px);
 
   --panel-separator-color: ThreeDLightShadow;
+  --arrowpanel-dimmed: hsla(210,4%,10%,.07);
+  --arrowpanel-dimmed-further: hsla(210,4%,10%,.12);
+  --arrowpanel-dimmed-even-further: hsla(210,4%,10%,.17);
 
   --urlbar-separator-color: ThreeDLightShadow;
 }
 
 @media (-moz-windows-default-theme) {
   :root {
     --panel-separator-color: hsla(210,4%,10%,.14);
   }
@@ -1439,35 +1442,35 @@ html|*.urlbar-input:-moz-lwtheme::placeh
 }
 
 .search-go-container {
   padding: 2px 2px;
 }
 
 #urlbar-search-footer {
   border-top: 1px solid var(--panel-separator-color);
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings {
   -moz-appearance: none;
   -moz-user-focus: ignore;
   color: GrayText;
   margin: 0;
   border: 0;
   padding: 8px 20px;
   background: transparent;
 }
 
 #urlbar-search-settings:hover {
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings:hover:active {
-  background-color: hsla(210,4%,10%,.12);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 #urlbar-search-splitter {
   min-width: 6px;
   margin-inline-start: -3px;
   border: none;
   background: transparent;
 }
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -127,17 +127,17 @@
 }
 
 .search-panel-tree {
   border-top: 1px solid var(--panel-separator-color) !important;
 }
 
 .search-panel-header {
   font-weight: normal;
-  background-color: hsla(210,4%,10%,.07);
+  background-color: var(--arrowpanel-dimmed);
   border: none;
   border-top: 1px solid var(--panel-separator-color);
   margin: 0;
   padding: 3px 6px;
   color: GrayText;
 }
 
 .search-panel-header > label {
@@ -288,17 +288,17 @@
 }
 
 .search-setting-button {
   -moz-appearance: none;
   min-height: 32px;
 }
 
 .search-setting-button[selected] {
-  background-color: hsla(210,4%,10%,.15);
+  background-color: var(--arrowpanel-dimmed-further);
 }
 
 .search-setting-button-compact {
   list-style-image: url("chrome://browser/skin/gear.svg#gear");
 }
 
 .search-setting-button-compact[selected] {
   list-style-image: url("chrome://browser/skin/gear.svg#gear-inverted");
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -1,12 +1,20 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+dnl Several autoconf functions AC_REQUIRE AC_PROG_CPP/AC_PROG_CXXCPP,
+dnl meaning they are called even when we don't call them explicitly.
+dnl However, theses checks are not necessary and python configure sets
+dnl the corresponding variables already, so just skip those tests
+dnl entirely.
+define([AC_PROG_CPP],[])
+define([AC_PROG_CXXCPP],[])
+
 AC_DEFUN([MOZ_TOOL_VARIABLES],
 [
 GNU_AS=
 GNU_LD=
 
 GNU_CC=
 GNU_CXX=
 if test "$CC_TYPE" = "gcc"; then
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -751,16 +751,33 @@ def compiler(language, host_or_target, c
 
     def compiler_error():
         raise FatalCheckError('Failed compiling a simple %s source with %s'
                               % (language, what))
 
     valid_compiler.try_compile(check_msg='%s works' % what,
                                onerror=compiler_error)
 
+
+    # Set CPP/CXXCPP for both the build system and old-configure. We don't
+    # need to check this works for preprocessing, because we already relied
+    # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
+    # in the first place.
+    if host_or_target == target:
+        pp_var = {
+            'C': 'CPP',
+            'C++': 'CXXCPP',
+        }[language]
+
+        preprocessor = depends_if(valid_compiler)(
+                lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
+
+        set_config(pp_var, preprocessor)
+        add_old_configure_assignment(pp_var, preprocessor)
+
     return valid_compiler
 
 
 c_compiler = compiler('C', target)
 cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
 host_c_compiler = compiler('C', host, other_compiler=c_compiler)
 host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
                              other_compiler=cxx_compiler,
--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
@@ -1,15 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the a function call's stack is properly displayed in the UI.
  */
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function* ifTestingSupported() {
   let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
 
   yield reload(target);
 
   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
@@ -1,16 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the a function call's stack is properly displayed in the UI
  * and jumping to source in the debugger for the topmost call item works.
  */
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function* ifTestingSupported() {
   let { target, panel } = yield initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
 
   yield reload(target);
 
   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
--- a/devtools/client/debugger/new/panel.js
+++ b/devtools/client/debugger/new/panel.js
@@ -17,16 +17,17 @@ DebuggerPanel.prototype = {
       targetPromise = Promise.resolve(this.toolbox.target);
     }
 
     return targetPromise.then(() => {
       this.panelWin.Debugger.bootstrap({
         threadClient: this.toolbox.threadClient,
         tabTarget: this.toolbox.target
       });
+      this.isReady = true;
       return this;
     });
   },
 
   _store: function() {
     return this.panelWin.Debugger.store;
   },
 
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -26,17 +26,21 @@ const chromeRegistry = Cc["@mozilla.org/
 // Override promise with deprecated-sync-thenables
 promise = Cu.import("resource://devtools/shared/deprecated-sync-thenables.js", {}).Promise;
 
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
 const CHROME_URL = "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/";
 const CHROME_URI = Services.io.newURI(CHROME_URL, null, null);
 
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+
 registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+
   info("finish() was called, cleaning up...");
   Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
 
   while (gBrowser && gBrowser.tabs && gBrowser.tabs.length > 1) {
     info("Destroying toolbox.");
     let target = TargetFactory.forTab(gBrowser.selectedTab);
     yield gDevTools.closeToolbox(target);
 
--- a/devtools/client/framework/test/browser_source_map-01.js
+++ b/devtools/client/framework/test/browser_source_map-01.js
@@ -8,16 +8,22 @@ thisTestLeaksUncaughtRejectionsAndShould
   "TypeError: this.transport is null");
 
 /**
  * Tests the SourceMapService updates generated sources when source maps
  * are subsequently found. Also checks when no column is provided, and
  * when tagging an already source mapped location initially.
  */
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
 const JS_URL = `${URL_ROOT}code_binary_search.js`;
 const COFFEE_URL = `${URL_ROOT}code_binary_search.coffee`;
 const { SourceMapService } = require("devtools/client/framework/source-map-service");
 const { serialize } = require("devtools/client/framework/location-store");
 
--- a/devtools/client/framework/test/browser_source_map-02.js
+++ b/devtools/client/framework/test/browser_source_map-02.js
@@ -2,16 +2,22 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests the SourceMapService updates generated sources when pretty printing
  * and un pretty printing.
  */
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 const DEBUGGER_ROOT = "http://example.com/browser/devtools/client/debugger/test/mochitest/";
 // Empty page
 const PAGE_URL = `${DEBUGGER_ROOT}doc_empty-tab-01.html`;
 const JS_URL = `${URL_ROOT}code_ugly.js`;
 const { SourceMapService } = require("devtools/client/framework/source-map-service");
 
 add_task(function* () {
   let toolbox = yield openNewTabAndToolbox(PAGE_URL, "jsdebugger");
--- a/devtools/client/framework/test/browser_toolbox_split_console.js
+++ b/devtools/client/framework/test/browser_toolbox_split_console.js
@@ -8,16 +8,22 @@
 //  * toolbox.isSplitConsoleFocused
 
 let gToolbox = null;
 let panelWin = null;
 
 const URL = "data:text/html;charset=utf8,test split console key delegation";
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   let tab = yield addTab(URL);
   let target = TargetFactory.forTab(tab);
   gToolbox = yield gDevTools.showToolbox(target, "jsdebugger");
   panelWin = gToolbox.getPanel("jsdebugger").panelWin;
 
   yield gToolbox.openSplitConsole();
   yield testIsSplitConsoleFocused();
--- a/devtools/client/framework/test/browser_toolbox_view_source_01.js
+++ b/devtools/client/framework/test/browser_toolbox_view_source_01.js
@@ -6,16 +6,22 @@
 /**
  * Tests that Toolbox#viewSourceInDebugger works when debugger is not
  * yet opened.
  */
 
 var URL = `${URL_ROOT}doc_viewsource.html`;
 var JS_URL = `${URL_ROOT}code_math.js`;
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function* viewSource() {
   let toolbox = yield openNewTabAndToolbox(URL);
 
   yield toolbox.viewSourceInDebugger(JS_URL, 2);
 
   let debuggerPanel = toolbox.getPanel("jsdebugger");
   ok(debuggerPanel, "The debugger panel was opened.");
   is(toolbox.currentToolId, "jsdebugger", "The debugger panel was selected.");
--- a/devtools/client/framework/test/browser_toolbox_view_source_02.js
+++ b/devtools/client/framework/test/browser_toolbox_view_source_02.js
@@ -5,16 +5,22 @@
 
 /**
  * Tests that Toolbox#viewSourceInDebugger works when debugger is already loaded.
  */
 
 var URL = `${URL_ROOT}doc_viewsource.html`;
 var JS_URL = `${URL_ROOT}code_math.js`;
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function* viewSource() {
   let toolbox = yield openNewTabAndToolbox(URL);
   let { panelWin: debuggerWin } = yield toolbox.selectTool("jsdebugger");
   let debuggerEvents = debuggerWin.EVENTS;
   let { DebuggerView } = debuggerWin;
   let Sources = DebuggerView.Sources;
 
   yield debuggerWin.once(debuggerEvents.SOURCE_SHOWN);
deleted file mode 100644
--- a/devtools/client/locales/en-US/promisedebugger.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# LOCALIZATION NOTE These strings are used inside the Promise debugger
-# which is available as a panel in the Debugger.
-# The correct localization of this file might be to keep it in
-# English, or another language commonly spoken among web developers.
-# You want to make that choice consistent across the developer tools.
-# A good criteria is the language in which you'd find the best
-# documentation on web development on the web.
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -87,17 +87,22 @@ pref("devtools.debugger.remote-timeout",
 pref("devtools.debugger.pause-on-exceptions", false);
 pref("devtools.debugger.ignore-caught-exceptions", true);
 pref("devtools.debugger.source-maps-enabled", true);
 pref("devtools.debugger.pretty-print-enabled", true);
 pref("devtools.debugger.auto-pretty-print", false);
 pref("devtools.debugger.auto-black-box", true);
 pref("devtools.debugger.workers", false);
 pref("devtools.debugger.promise", false);
+
+#if defined(NIGHTLY_BUILD)
+pref("devtools.debugger.new-debugger-frontend", true);
+#else
 pref("devtools.debugger.new-debugger-frontend", false);
+#endif
 
 // The default Debugger UI settings
 pref("devtools.debugger.ui.panes-workers-and-sources-width", 200);
 pref("devtools.debugger.ui.panes-instruments-width", 300);
 pref("devtools.debugger.ui.panes-visible-on-startup", false);
 pref("devtools.debugger.ui.variables-sorting-enabled", true);
 pref("devtools.debugger.ui.variables-only-enum-visible", false);
 pref("devtools.debugger.ui.variables-searchbox-visible", false);
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -273,17 +273,17 @@ Editor.prototype = {
         win.document.documentElement.setAttribute("force-theme", "light");
       }
 
       Services.scriptloader.loadSubScript(
         "chrome://devtools/content/shared/theme-switching.js",
         win, "utf8"
       );
       this.container = env;
-      this._setup(win.document.body);
+      this._setup(win.document.body, el.ownerDocument);
       env.removeEventListener("load", onLoad, true);
 
       def.resolve();
     };
 
     env.addEventListener("load", onLoad, true);
     env.setAttribute("src", CM_IFRAME);
     el.appendChild(env);
@@ -296,17 +296,18 @@ Editor.prototype = {
     this._setup(el);
   },
 
   /**
    * Do the actual appending and configuring of the CodeMirror instance. This is
    * used by both append functions above, and does all the hard work to
    * configure CodeMirror with all the right options/modes/etc.
    */
-  _setup: function (el) {
+  _setup: function (el, doc) {
+    doc = doc || el.ownerDocument;
     let win = el.ownerDocument.defaultView;
 
     let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
     scriptsToInject.forEach(url => {
       if (url.startsWith("chrome://")) {
         Services.scriptloader.loadSubScript(url, win, "utf8");
       }
     });
@@ -368,17 +369,17 @@ Editor.prototype = {
       ev.preventDefault();
 
       if (!this.config.contextMenu) {
         return;
       }
 
       let popup = this.config.contextMenu;
       if (typeof popup == "string") {
-        popup = el.ownerDocument.getElementById(this.config.contextMenu);
+        popup = doc.getElementById(this.config.contextMenu);
       }
 
       this.emit("popupOpen", ev, popup);
       popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
     }, false);
 
     // Intercept the find and find again keystroke on CodeMirror, to avoid
     // the browser's search
--- a/devtools/client/themes/images/tool-profiler-active.svg
+++ b/devtools/client/themes/images/tool-profiler-active.svg
@@ -1,10 +1,9 @@
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#5FC749">
-  <path d="M6.9 7.8c-.3.1-.6.4-.6.7-.1.5.2 1 .7 1.1.3 0 .7-.1.9-.3l2.5-2.9-3.5 1.4z"/>
-  <path opacity="0.5" d="M4.7 10.6c.7 1.1 1.9 1.9 3.3 1.9s2.6-.8 3.3-1.9H4.7z"/>
-  <path d="M-12.7-2.5c-3.8 0-6.7 3-6.7 6.7s3 6.7 6.7 6.7c3.8 0 6.7-3 6.7-6.7s-2.9-6.7-6.7-6.7zM-12.8 9c-2.5 0-4.6-2.1-4.8-4.5v-.2h.6c.6 0 1-.4 1-1s-.4-1-1-1h-.4c.4-.9.8-1.4 1.5-1.9l.2.4c.3.5.8.7 1.3.4.5-.2.7-.7.4-1.2l-.2-.4c.4-.1.9-.2 1.4-.2.6 0 1.1.1 1.6.3l-.2.4c-.3.5-.1 1 .4 1.3.5.3 1 .1 1.3-.4l.2-.6c.6.6 1.2 1.5 1.4 1.8h-.4c-.6 0-1 .4-1 1s.4 1 1 1h.6v.2C-8.2 6.9-10.3 9-12.8 9zM-12.8 12.7c-3.4 0-6.2 2.7-6.2 6.2s2.7 6.3 6.3 6.3c3.5 0 6.3-2.7 6.3-6.3-.1-3.4-2.9-6.2-6.4-6.2zm0 11.4c-2.9 0-5.2-2.3-5.2-5.2 0-2.9 2.3-5.2 5.2-5.2s5.2 2.3 5.2 5.2c0 3-2.2 5.2-5.2 5.2z"/>
-  <path d="M-14.5 16.3c-.2 0-.4.2-.4.4v4.5c0 .2.3.4.5.4s.5-.2.5-.4v-4.5c0-.2-.3-.4-.5-.4"/>
-  <path d="M8 2.3C4.5 2.3 1.8 5 1.8 8.5s2.7 6.3 6.3 6.3c3.5 0 6.3-2.7 6.3-6.3-.2-3.4-2.9-6.2-6.4-6.2zm0 11.4c-2.9 0-5.2-2.3-5.2-5.2v-.3h1.7c.2 0 .4-.3.4-.5s-.2-.6-.4-.6H2.9C3.3 5.9 4 4.8 5 4.1l1.4 1.3c.1.1.5.1.6-.1.1-.1.2-.5.1-.6L6 3.6c.6-.2 1.3-.4 2-.4.8 0 1.5.2 2.2.5L9 4.8c-.1.1 0 .5.1.6s.5.2.6.1L11 4.3c1 .7 1.7 1.7 2 2.8h-1.7c-.2 0-.4.3-.4.5s.2.5.4.5h1.9v.3c0 3.1-2.2 5.3-5.2 5.3z"/>
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="#5FC749" fill-rule="evenodd">
+  <path d="M15 9.004C14.51 12.394 11.578 15 8.035 15 4.15 15 1 11.866 1 8s3.15-7 7.036-7c1.941 0 3.7.783 4.972 2.048l-.709.709A6.027 6.027 0 0 0 8.036 2c-3.33 0-6.03 2.686-6.03 6s2.7 6 6.03 6a6.023 6.023 0 0 0 5.946-4.993l1.017-.003z"/>
+  <path d="M4.137 9H3.1a5.002 5.002 0 0 0 9.8 0h-.965a4.023 4.023 0 0 1-3.9 3 4.023 4.023 0 0 1-3.898-3z" fill-opacity=".5"/>
+  <path d="M8.036 11a2.994 2.994 0 0 0 2.987-3c0-1.657-1.338-3-2.987-3a2.994 2.994 0 0 0-2.988 3c0 1.657 1.338 3 2.988 3zm0-1c1.11 0 2.011-.895 2.011-2s-.9-2-2.011-2c-1.111 0-2.012.895-2.012 2s.9 2 2.012 2z"/>
+  <path d="M10.354 6.354l4-4a.5.5 0 0 0-.708-.708l-4 4a.5.5 0 1 0 .708.708z"/>
 </svg>
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -107,16 +107,22 @@ a {
 .message-repeats[value="1"] {
   display: none;
 }
 
 .message-location {
   max-width: 40%;
 }
 
+.stack-trace {
+  /* The markup contains extra whitespace to improve formatting of clipboard text.
+     Make sure this whitespace doesn't affect the HTML rendering */
+  white-space: normal;
+}
+
 .stack-trace .frame-link-source,
 .message-location .frame-link-source {
   /* Makes the file name truncated (and ellipsis shown) on the left side */
   direction: rtl;
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
--- a/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
+++ b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
@@ -3,16 +3,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Check that inspecting an optimized out variable works when execution is
 // paused.
 
 "use strict";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function test() {
   Task.spawn(function* () {
     const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                      "test/test-closure-optimized-out.html";
     let {tab} = yield loadTab(TEST_URI);
     let hud = yield openConsole(tab);
     let { toolbox, panel, panelWin } = yield openDebugger();
 
--- a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js
+++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js
@@ -7,28 +7,36 @@
 // from the js debugger, when changing the value of a property in the variables
 // view.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-eval-in-stackframe.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   yield loadTab(TEST_URI);
   let hud = yield openConsole();
 
   let dbgPanel = yield openDebugger();
-  yield waitForFrameAdded(dbgPanel);
+  yield waitForFrameAdded();
   yield openConsole();
   yield testVariablesView(hud);
 });
 
-function* waitForFrameAdded(dbgPanel) {
-  let thread = dbgPanel.panelWin.DebuggerController.activeThread;
+function* waitForFrameAdded() {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  let toolbox = gDevTools.getToolbox(target);
+  let thread = toolbox.threadClient;
 
   info("Waiting for framesadded");
   yield new Promise(resolve => {
     thread.addOneTimeListener("framesadded", resolve);
     ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
       content.wrappedJSObject.firstCall();
     });
   });
--- a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
+++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
@@ -6,30 +6,38 @@
 // Test that makes sure web console eval works while the js debugger paused the
 // page, and while the inspector is active. See bug 886137.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-eval-in-stackframe.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   yield loadTab(TEST_URI);
   let hud = yield openConsole();
 
   let dbgPanel = yield openDebugger();
   yield openInspector();
-  yield waitForFrameAdded(dbgPanel);
+  yield waitForFrameAdded();
 
   yield openConsole();
   yield testVariablesView(hud);
 });
 
-function* waitForFrameAdded(dbgPanel) {
-  let thread = dbgPanel.panelWin.DebuggerController.activeThread;
+function* waitForFrameAdded() {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  let toolbox = gDevTools.getToolbox(target);
+  let thread = toolbox.threadClient;
 
   info("Waiting for framesadded");
   yield new Promise(resolve => {
     thread.addOneTimeListener("framesadded", resolve);
     ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
       content.wrappedJSObject.firstCall();
     });
   });
--- a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js
+++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js
@@ -9,16 +9,22 @@
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-eval-in-stackframe.html";
 
 var gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController;
 var gStackframes;
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function test() {
   loadTab(TEST_URI).then(() => {
     openConsole().then(consoleOpened);
   });
 }
 
 function consoleOpened(hud) {
   gWebConsole = hud;
--- a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js
+++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js
@@ -6,16 +6,23 @@
 // Test to make sure that web console commands can fire while paused at a
 // breakpoint that was triggered from a JS call.  Relies on asynchronous js
 // evaluation over the protocol - see Bug 1088861.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-eval-in-stackframe.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   yield loadTab(TEST_URI);
 
   info("open the web console");
   let hud = yield openConsole();
   let {jsterm} = hud;
 
   info("open the debugger");
--- a/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js
@@ -6,16 +6,22 @@
 // Test that makes sure web console autocomplete happens in the user-selected
 // stackframe from the js debugger.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-autocomplete-in-stackframe.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 var gStackframes;
 registerCleanupFunction(function () {
   gStackframes = null;
 });
 
 requestLongerTimeout(2);
 add_task(function* () {
   yield loadTab(TEST_URI);
--- a/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js
@@ -5,16 +5,22 @@
 
 // Tests that clicking on a function displays its source in the debugger.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-bug_1050691_click_function_to_source.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   yield loadTab(TEST_URI);
   let hud = yield openConsole();
 
   // Open the Debugger panel.
   let debuggerPanel = yield openDebugger();
   // And right after come back to the Console panel.
   yield openConsole();
--- a/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
@@ -6,16 +6,22 @@
 // Test that message source links for js errors and console API calls open in
 // the jsdebugger when clicked.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test" +
                  "/test-bug-766001-js-console-links.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function test() {
   let hud;
 
   requestLongerTimeout(2);
   Task.spawn(runner).then(finishTest);
 
   function* runner() {
     // On e10s, the exception is triggered in child process
--- a/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js
+++ b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js
@@ -8,16 +8,22 @@
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-closures.html";
 
 var gWebConsole, gJSTerm, gVariablesView;
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 function test() {
   registerCleanupFunction(() => {
     gWebConsole = gJSTerm = gVariablesView = null;
   });
 
   function fetchScopes(hud, toolbox, panelWin, deferred) {
     panelWin.once(panelWin.EVENTS.FETCHED_SCOPES, () => {
       ok(true, "Scopes were fetched");
--- a/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js
+++ b/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js
@@ -21,14 +21,14 @@ add_task(function* () {
   hud.ui.filterBox.focus();
 
   is(hud.ui.filterBox.hasAttribute("focused"), true,
      "filterBox should be focused");
 
   is(hud.jsterm.inputNode.hasAttribute("focused"), false,
      "inputNode shouldn't be focused");
 
-  yield openDebugger();
+  yield openInspector();
   hud = yield openConsole();
 
   is(hud.jsterm.inputNode.hasAttribute("focused"), true,
      "inputNode should be focused");
 });
--- a/devtools/client/webconsole/test/browser_webconsole_output_05.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_05.js
@@ -140,20 +140,21 @@ var inputTests = [
     variablesViewLabel: "Promise"
   },
 
   // 14
   {
     input: "new Object({1: 'this\\nis\\nsupposed\\nto\\nbe\\na\\nvery" +
            "\\nlong\\nstring\\n,shown\\non\\na\\nsingle\\nline', " +
            "2: 'a shorter string', 3: 100})",
-    output: 'Object { 1: "this is supposed to be a very long ' + ELLIPSIS +
-            '", 2: "a shorter string", 3: 100 }',
+    output: '[ <1 empty slot>, "this is supposed to be a very long ' + ELLIPSIS +
+            '", "a shorter string", 100 ]',
     printOutput: "[object Object]",
-    inspectable: false,
+    inspectable: true,
+    variablesViewLabel: "Object[4]"
   },
 
   // 15
   {
     input: "new Proxy({a:1},[1,2,3])",
     output: 'Proxy { <target>: Object, <handler>: Array[3] }',
     printOutput: "[object Object]",
     inspectable: true,
--- a/devtools/client/webconsole/test/browser_webconsole_output_06.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js
@@ -126,20 +126,20 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[2]",
   },
 
   // 14
   {
     input: '({0: "a", 42: "b"})',
-    output: 'Object { 0: "a", 42: "b" }',
+    output: '[ "a", <9 empty slots>, 33 more\u2026 ]',
     printOutput: "[object Object]",
     inspectable: true,
-    variablesViewLabel: "Object",
+    variablesViewLabel: "Object[43]",
   },
 
   // 15
   {
     input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' +
            '7: "h", 8: "i", 9: "j", 10: "k", 11: "l"})',
     output: 'Object [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", ' +
             "2 more\u2026 ]",
@@ -184,20 +184,20 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[0]",
   },
 
   // 20
   {
     input: '({length: 1})',
-    output: 'Object { length: 1 }',
+    output: '[ <1 empty slot> ]',
     printOutput: "[object Object]",
     inspectable: true,
-    variablesViewLabel: "Object",
+    variablesViewLabel: "Object[1]",
   },
 
   // 21
   {
     input: '({0: "a", 1: "b", length: 1})',
     output: 'Object { 1: "b", length: 1, 1 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
@@ -211,38 +211,38 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[2]",
   },
 
   // 23
   {
     input: '({0: "a", 1: "b", length: 3})',
-    output: 'Object { length: 3, 2 more\u2026 }',
+    output: '[ "a", "b", <1 empty slot> ]',
     printOutput: "[object Object]",
     inspectable: true,
-    variablesViewLabel: "Object",
+    variablesViewLabel: "Object[3]",
   },
 
   // 24
   {
     input: '({0: "a", 2: "b", length: 2})',
     output: 'Object { 2: "b", length: 2, 1 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 25
   {
     input: '({0: "a", 2: "b", length: 3})',
-    output: 'Object { length: 3, 2 more\u2026 }',
+    output: '[ "a", <1 empty slot>, "b" ]',
     printOutput: "[object Object]",
     inspectable: true,
-    variablesViewLabel: "Object",
+    variablesViewLabel: "Object[3]",
   },
 
   // 26
   {
     input: '({0: "a", b: "b", length: 1})',
     output: 'Object { b: "b", length: 1, 1 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
@@ -252,16 +252,25 @@ var inputTests = [
   // 27
   {
     input: '({0: "a", b: "b", length: 2})',
     output: 'Object { b: "b", length: 2, 1 more\u2026 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
+
+  // 28
+  {
+    input: '({42: "a"})',
+    output: 'Object { 42: "a" }',
+    printOutput: "[object Object]",
+    inspectable: true,
+    variablesViewLabel: "Object",
+  },
 ];
 
 function test() {
   requestLongerTimeout(2);
   Task.spawn(function* () {
     let {tab} = yield loadTab(TEST_URI);
     let hud = yield openConsole(tab);
     return checkOutputForInputs(hud, inputTests);
--- a/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js
@@ -16,17 +16,17 @@ add_task(function* () {
   yield loadTab("data:text/html;charset=utf8,<script>'use strict';var arguments;</script>");
 
   let hud = yield openConsole();
 
   yield waitForMessages({
     webconsole: hud,
     messages: [
       {
-        text: "SyntaxError: redefining arguments is deprecated",
+        text: "SyntaxError: 'arguments' can't be defined or assigned to in strict mode code",
         category: CATEGORY_JS,
         severity: SEVERITY_ERROR,
       },
     ],
   });
 
   if (!Services.appinfo.browserTabsRemoteAutostart) {
     expectUncaughtException();
--- a/devtools/client/webconsole/test/browser_webconsole_view_source.js
+++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js
@@ -8,16 +8,22 @@
 // have their locations opened in Debugger, we need to test a security message in
 // order to have it opened in the standard View Source window.
 
 "use strict";
 
 const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
                  "test/test-mixedcontent-securityerrors.html";
 
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
 add_task(function* () {
   yield loadTab(TEST_URI);
   let hud = yield openConsole(null);
   info("console opened");
 
   let [result] = yield waitForMessages({
     webconsole: hud,
     messages: [{
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1788,65 +1788,76 @@ DebuggerServer.ObjectActorPreviewers.Obj
       lineNumber: hooks.createValueGrip(rawObj.lineNumber),
       columnNumber: hooks.createValueGrip(rawObj.columnNumber),
     };
 
     return true;
   },
 
   function PseudoArray({obj, hooks}, grip, rawObj) {
-    let length = 0;
+    let length;
 
     let keys = obj.getOwnPropertyNames();
     if (keys.length == 0) {
       return false;
     }
 
-    // Pseudo-arrays should only have array indices and, optionally, a "length" property.
-    // Since array indices are sorted first, check if the last property is "length".
-    if(keys[keys.length-1] === "length") {
-      keys.pop();
-      // The value of "length" should equal the number of other properties. If eventually
-      // we allow sparse pseudo-arrays, we should check whether it's a Uint32 instead.
-      if(rawObj.length !== keys.length) {
-        return false;
-      }
+    // If no item is going to be displayed in preview, better display as sparse object.
+    // The first key should contain the smallest integer index (if any).
+    if(keys[0] >= OBJECT_PREVIEW_MAX_ITEMS) {
+      return false;
     }
 
-    // Ensure that the keys are consecutive integers starting at "0". If eventually we
-    // allow sparse pseudo-arrays, we should check that they are array indices, that is:
-    // `(key >>> 0) + '' === key && key !== "4294967295"`.
-    // Checking the last property first allows us to avoid useless iterations when
-    // there is any property which is not an array index.
-    if(keys.length && keys[keys.length-1] !== keys.length - 1 + '') {
+    // Pseudo-arrays should only have array indices and, optionally, a "length" property.
+    // Since integer indices are sorted first, check if the last property is "length".
+    if(keys[keys.length-1] === "length") {
+      keys.pop();
+      length = DevToolsUtils.getProperty(obj, "length");
+    } else {
+      // Otherwise, let length be the (presumably) greatest array index plus 1.
+      length = +keys[keys.length-1] + 1;
+    }
+    // Check if length is a valid array length, i.e. is a Uint32 number.
+    if(typeof length !== "number" || length >>> 0 !== length) {
       return false;
     }
-    for (let key of keys) {
-      if (key !== (length++) + '') {
+
+    // Ensure all keys are increasing array indices smaller than length. The order is not
+    // guaranteed for exotic objects but, in most cases, big array indices and properties
+    // which are not integer indices should be at the end. Then, iterating backwards
+    // allows us to return earlier when the object is not completely a pseudo-array.
+    let prev = length;
+    for(let i = keys.length - 1; i >= 0; --i) {
+      let key = keys[i];
+      let numKey = key >>> 0; // ToUint32(key)
+      if (numKey + '' !== key || numKey >= prev) {
         return false;
       }
+      prev = numKey;
     }
 
     grip.preview = {
       kind: "ArrayLike",
       length: length,
     };
 
     // Avoid recursive object grips.
     if (hooks.getGripDepth() > 1) {
       return true;
     }
 
     let items = grip.preview.items = [];
+    let numItems = Math.min(OBJECT_PREVIEW_MAX_ITEMS, length);
 
-    let i = 0;
-    for (let key of keys) {
-      if (rawObj.hasOwnProperty(key) && i++ < OBJECT_PREVIEW_MAX_ITEMS) {
-        let value = makeDebuggeeValueIfNeeded(obj, rawObj[key]);
-        items.push(hooks.createValueGrip(value));
+    for (let i = 0; i < numItems; ++i) {
+      let desc = obj.getOwnPropertyDescriptor(i);
+      if (desc && 'value' in desc) {
+        items.push(hooks.createValueGrip(desc.value));
+      } else {
+        items.push(null);
       }
     }
 
     return true;
   },
 
   GenericObject,
 ];
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -122,16 +122,32 @@ KeyframeEffect::SetTarget(const Nullable
     }
   } else if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
     // New target is null, so fall back to distribute spacing.
     KeyframeUtils::ApplyDistributeSpacing(mKeyframes);
   }
 }
 
 void
+KeyframeEffect::SetIterationComposite(
+  const IterationCompositeOperation& aIterationComposite)
+{
+  if (mEffectOptions.mIterationComposite == aIterationComposite) {
+    return;
+  }
+
+  if (mAnimation && mAnimation->IsRelevant()) {
+    nsNodeUtils::AnimationChanged(mAnimation);
+  }
+
+  mEffectOptions.mIterationComposite = aIterationComposite;
+  RequestRestyle(EffectCompositor::RestyleType::Layer);
+}
+
+void
 KeyframeEffect::SetSpacing(JSContext* aCx,
                            const nsAString& aSpacing,
                            ErrorResult& aRv)
 {
   SpacingMode spacingMode = SpacingMode::distribute;
   nsCSSPropertyID pacedProperty = eCSSProperty_UNKNOWN;
   nsAutoString invalidPacedProperty;
   KeyframeEffectParams::ParseSpacing(aSpacing,
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -62,14 +62,16 @@ public:
   // This method calls GetTargetStyleContext which is not safe to use when
   // we are in the middle of updating style. If we need to use this when
   // updating style, we should pass the nsStyleContext into this method and use
   // that to update the properties rather than calling
   // GetStyleContextForElement.
   void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
 
   void SetSpacing(JSContext* aCx, const nsAString& aSpacing, ErrorResult& aRv);
+  void SetIterationComposite(
+    const IterationCompositeOperation& aIterationComposite);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_KeyframeEffect_h
--- a/dom/animation/KeyframeEffectParams.h
+++ b/dom/animation/KeyframeEffectParams.h
@@ -4,16 +4,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_KeyframeEffectParams_h
 #define mozilla_KeyframeEffectParams_h
 
 #include "nsCSSProps.h"
 #include "nsString.h"
+// X11 has a #define for None
+#ifdef None
+#undef None
+#endif
+#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
 
 namespace mozilla {
 
 class ErrorResult;
 
 enum class SpacingMode
 {
   distribute,
@@ -46,17 +51,18 @@ struct KeyframeEffectParams
    * @param [out] aRv The error result.
    */
   static void ParseSpacing(const nsAString& aSpacing,
                            SpacingMode& aSpacingMode,
                            nsCSSPropertyID& aPacedProperty,
                            nsAString& aInvalidPacedProperty,
                            ErrorResult& aRv);
 
-  // FIXME: Bug 1216843: Add IterationCompositeOperations and
-  //        Bug 1216844: Add CompositeOperation
+  dom::IterationCompositeOperation mIterationComposite =
+    dom::IterationCompositeOperation::Replace;
+  // FIXME: Bug 1216844: Add CompositeOperation
   SpacingMode mSpacingMode = SpacingMode::distribute;
   nsCSSPropertyID mPacedProperty = eCSSProperty_UNKNOWN;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_KeyframeEffectParams_h
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -72,17 +72,17 @@ KeyframeEffectReadOnly::WrapObject(JSCon
                                    JS::Handle<JSObject*> aGivenProto)
 {
   return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
 }
 
 IterationCompositeOperation
 KeyframeEffectReadOnly::IterationComposite() const
 {
-  return IterationCompositeOperation::Replace;
+  return mEffectOptions.mIterationComposite;
 }
 
 CompositeOperation
 KeyframeEffectReadOnly::Composite() const
 {
   return CompositeOperation::Replace;
 }
 
@@ -106,35 +106,32 @@ KeyframeEffectReadOnly::NotifyAnimationT
   // animation cascade for this element whenever that changes.
   bool inEffect = IsInEffect();
   if (inEffect != mInEffectOnLastAnimationTimingUpdate) {
     MarkCascadeNeedsUpdate();
     mInEffectOnLastAnimationTimingUpdate = inEffect;
   }
 
   // Request restyle if necessary.
-  //
-  // Bug 1216843: When we implement iteration composite modes, we need to
-  // also detect if the current iteration has changed.
-  if (mAnimation &&
-      !mProperties.IsEmpty() &&
-      GetComputedTiming().mProgress != mProgressOnLastCompose) {
+  if (mAnimation && !mProperties.IsEmpty() && HasComputedTimingChanged()) {
     EffectCompositor::RestyleType restyleType =
       CanThrottle() ?
       EffectCompositor::RestyleType::Throttled :
       EffectCompositor::RestyleType::Standard;
     RequestRestyle(restyleType);
   }
 
   // If we're no longer "in effect", our ComposeStyle method will never be
-  // called and we will never have a chance to update mProgressOnLastCompose.
-  // We clear mProgressOnLastCompose here to ensure that if we later become
-  // "in effect" we will request a restyle (above).
+  // called and we will never have a chance to update mProgressOnLastCompose
+  // and mCurrentIterationOnLastCompose.
+  // We clear them here to ensure that if we later become "in effect" we will
+  // request a restyle (above).
   if (!inEffect) {
      mProgressOnLastCompose.SetNull();
+     mCurrentIterationOnLastCompose = 0;
   }
 }
 
 static bool
 KeyframesEqualIgnoringComputedOffsets(const nsTArray<Keyframe>& aLhs,
                                       const nsTArray<Keyframe>& aRhs)
 {
   if (aLhs.Length() != aRhs.Length()) {
@@ -307,16 +304,17 @@ KeyframeEffectReadOnly::UpdateProperties
 }
 
 void
 KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
                                      nsCSSPropertyIDSet& aSetProperties)
 {
   ComputedTiming computedTiming = GetComputedTiming();
   mProgressOnLastCompose = computedTiming.mProgress;
+  mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration;
 
   // If the progress is null, we don't have fill data for the current
   // time so we shouldn't animate.
   if (computedTiming.mProgress.IsNull()) {
     return;
   }
 
   for (size_t propIdx = 0, propEnd = mProperties.Length();
@@ -368,45 +366,75 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
                  prop.mSegments.Length(),
                "out of array bounds");
 
     if (!aStyleRule) {
       // Allocate the style rule now that we know we have animation data.
       aStyleRule = new AnimValuesStyleRule();
     }
 
+    StyleAnimationValue fromValue = segment->mFromValue;
+    StyleAnimationValue toValue = segment->mToValue;
+    // Iteration composition for accumulate
+    if (mEffectOptions.mIterationComposite ==
+          IterationCompositeOperation::Accumulate &&
+        computedTiming.mCurrentIteration > 0) {
+      const AnimationPropertySegment& lastSegment =
+        prop.mSegments.LastElement();
+      // FIXME: Bug 1293492: Add a utility function to calculate both of
+      // below StyleAnimationValues.
+      DebugOnly<bool> accumulateResult =
+        StyleAnimationValue::Accumulate(prop.mProperty,
+                                        fromValue,
+                                        lastSegment.mToValue,
+                                        computedTiming.mCurrentIteration);
+      // We can't check the accumulation result in case of filter property.
+      // That's because some filter property can't accumulate,
+      // e.g. 'contrast(2) brightness(2)' onto 'brightness(1) contrast(1)'
+      // because of mismatch of the order.
+      MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
+                 "could not accumulate value");
+      accumulateResult =
+        StyleAnimationValue::Accumulate(prop.mProperty,
+                                        toValue,
+                                        lastSegment.mToValue,
+                                        computedTiming.mCurrentIteration);
+      MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
+                 "could not accumulate value");
+    }
+
     // Special handling for zero-length segments
     if (segment->mToKey == segment->mFromKey) {
       if (computedTiming.mProgress.Value() < 0) {
-        aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
+        aStyleRule->AddValue(prop.mProperty, Move(fromValue));
       } else {
-        aStyleRule->AddValue(prop.mProperty, segment->mToValue);
+        aStyleRule->AddValue(prop.mProperty, Move(toValue));
       }
       continue;
     }
 
     double positionInSegment =
       (computedTiming.mProgress.Value() - segment->mFromKey) /
       (segment->mToKey - segment->mFromKey);
     double valuePosition =
       ComputedTimingFunction::GetPortion(segment->mTimingFunction,
                                          positionInSegment,
                                          computedTiming.mBeforeFlag);
 
     MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
     StyleAnimationValue val;
     if (StyleAnimationValue::Interpolate(prop.mProperty,
-                                         segment->mFromValue,
-                                         segment->mToValue,
+                                         fromValue,
+                                         toValue,
                                          valuePosition, val)) {
       aStyleRule->AddValue(prop.mProperty, Move(val));
     } else if (valuePosition < 0.5) {
-      aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
+      aStyleRule->AddValue(prop.mProperty, Move(fromValue));
     } else {
-      aStyleRule->AddValue(prop.mProperty, segment->mToValue);
+      aStyleRule->AddValue(prop.mProperty, Move(toValue));
     }
   }
 }
 
 bool
 KeyframeEffectReadOnly::IsRunningOnCompositor() const
 {
   // We consider animation is running on compositor if there is at least
@@ -477,16 +505,17 @@ KeyframeEffectParamsFromUnion(const Opti
   if (!aOptions.IsUnrestrictedDouble()) {
     const KeyframeEffectOptions& options =
       KeyframeEffectOptionsFromUnion(aOptions);
     KeyframeEffectParams::ParseSpacing(options.mSpacing,
                                        result.mSpacingMode,
                                        result.mPacedProperty,
                                        aInvalidPacedProperty,
                                        aRv);
+    result.mIterationComposite = options.mIterationComposite;
   }
   return result;
 }
 
 /* static */ Maybe<OwningAnimationTarget>
 KeyframeEffectReadOnly::ConvertTarget(
   const Nullable<ElementOrCSSPseudoElement>& aTarget)
 {
@@ -1275,10 +1304,25 @@ KeyframeEffectReadOnly::MarkCascadeNeeds
   EffectSet* effectSet = EffectSet::GetEffectSet(mTarget->mElement,
                                                  mTarget->mPseudoType);
   if (!effectSet) {
     return;
   }
   effectSet->MarkCascadeNeedsUpdate();
 }
 
+bool
+KeyframeEffectReadOnly::HasComputedTimingChanged() const
+{
+  // Typically we don't need to request a restyle if the progress hasn't
+  // changed since the last call to ComposeStyle. The one exception is if the
+  // iteration composite mode is 'accumulate' and the current iteration has
+  // changed, since that will often produce a different result.
+  ComputedTiming computedTiming = GetComputedTiming();
+  return computedTiming.mProgress != mProgressOnLastCompose ||
+         (mEffectOptions.mIterationComposite ==
+            IterationCompositeOperation::Accumulate &&
+         computedTiming.mCurrentIteration !=
+          mCurrentIterationOnLastCompose);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -370,28 +370,37 @@ protected:
   // A set of per-property value arrays, derived from |mKeyframes|.
   nsTArray<AnimationProperty> mProperties;
 
   // The computed progress last time we composed the style rule. This is
   // used to detect when the progress is not changing (e.g. due to a step
   // timing function) so we can avoid unnecessary style updates.
   Nullable<double> mProgressOnLastCompose;
 
+  // The purpose of this value is the same as mProgressOnLastCompose but
+  // this is used to detect when the current iteration is not changing
+  // in the case when iterationComposite is accumulate.
+  uint64_t mCurrentIterationOnLastCompose = 0;
+
   // We need to track when we go to or from being "in effect" since
   // we need to re-evaluate the cascade of animations when that changes.
   bool mInEffectOnLastAnimationTimingUpdate;
 
 private:
   nsChangeHint mCumulativeChangeHint;
 
   nsIFrame* GetAnimationFrame() const;
 
   bool CanThrottle() const;
   bool CanThrottleTransformChanges(nsIFrame& aFrame) const;
 
+  // Returns true if the computedTiming has changed since the last
+  // composition.
+  bool HasComputedTimingChanged() const;
+
   // Returns true unless Gecko limitations prevent performing transform
   // animations for |aFrame|. When returning true, the reason for the
   // limitation is stored in |aOutPerformanceWarning|.
   static bool CanAnimateTransformOnCompositor(
     const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning);
   static bool IsGeometricProperty(const nsCSSPropertyID aProperty);
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8162,27 +8162,31 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<
                                bool aIsWidgetEventSynthesized)
 {
   nsPoint offset;
   nsCOMPtr<nsIWidget> widget = GetWidget(aPresShell, &offset);
   if (!widget)
     return NS_ERROR_FAILURE;
 
   EventMessage msg;
+  WidgetMouseEvent::ExitFrom exitFrom = WidgetMouseEvent::eChild;
   bool contextMenuKey = false;
   if (aType.EqualsLiteral("mousedown")) {
     msg = eMouseDown;
   } else if (aType.EqualsLiteral("mouseup")) {
     msg = eMouseUp;
   } else if (aType.EqualsLiteral("mousemove")) {
     msg = eMouseMove;
   } else if (aType.EqualsLiteral("mouseover")) {
     msg = eMouseEnterIntoWidget;
   } else if (aType.EqualsLiteral("mouseout")) {
     msg = eMouseExitFromWidget;
+  } else if (aType.EqualsLiteral("mousecancel")) {
+    msg = eMouseExitFromWidget;
+    exitFrom = WidgetMouseEvent::eTopLevel;
   } else if (aType.EqualsLiteral("mouselongtap")) {
     msg = eMouseLongTap;
   } else if (aType.EqualsLiteral("contextmenu")) {
     msg = eContextMenu;
     contextMenuKey = (aButton == 0);
   } else if (aType.EqualsLiteral("MozMouseHittest")) {
     msg = eMouseHitTest;
   } else {
@@ -8204,16 +8208,17 @@ nsContentUtils::SendMouseEvent(nsCOMPtr<
   event.buttons = aButtons != nsIDOMWindowUtils::MOUSE_BUTTONS_NOT_SPECIFIED ?
                   aButtons :
                   msg == eMouseUp ? 0 : GetButtonsFlagForButton(aButton);
   event.pressure = aPressure;
   event.inputSource = aInputSourceArg;
   event.mClickCount = aClickCount;
   event.mTime = PR_IntervalNow();
   event.mFlags.mIsSynthesizedForTests = aIsDOMEventSynthesized;
+  event.mExitFrom = exitFrom;
 
   nsPresContext* presContext = aPresShell->GetPresContext();
   if (!presContext)
     return NS_ERROR_FAILURE;
 
   event.mRefPoint = ToWidgetPoint(CSSPoint(aX, aY), offset, presContext);
   event.mIgnoreRootScrollFrame = aIgnoreRootScrollFrame;
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2576,16 +2576,54 @@ nsJSContext::EnsureStatics()
 
   sIsInitialized = true;
 }
 
 void
 nsJSContext::NotifyDidPaint()
 {
   sDidPaintAfterPreviousICCSlice = true;
+  if (sICCTimer) {
+    static uint32_t sCount = 0;
+    // 16 here is the common value for refresh driver tick frequency.
+    static const uint32_t kTicksPerSliceDelay = kICCIntersliceDelay / 16;
+    if (++sCount % kTicksPerSliceDelay != 0) {
+      // Don't trigger CC slice all the time after paint, but often still.
+      // The key point is to trigger it right after paint, especially when
+      // we're running RefreshDriver constantly.
+      return;
+    }
+
+    sICCTimer->Cancel();
+    ICCTimerFired(nullptr, nullptr);
+    if (sICCTimer) {
+      sICCTimer->InitWithNamedFuncCallback(ICCTimerFired, nullptr,
+                                           kICCIntersliceDelay,
+                                           nsITimer::TYPE_REPEATING_SLACK,
+                                           "ICCTimerFired");
+    }
+  } else if (sCCTimer) {
+    static uint32_t sCount = 0;
+    static const uint32_t kTicksPerForgetSkippableDelay =
+      NS_CC_SKIPPABLE_DELAY / 16;
+    if (++sCount % kTicksPerForgetSkippableDelay != 0) {
+      // The comment above about triggering CC slice applies to forget skippable
+      // too.
+      return;
+    }
+
+    sCCTimer->Cancel();
+    CCTimerFired(nullptr, nullptr);
+    if (sCCTimer) {
+      sCCTimer->InitWithNamedFuncCallback(CCTimerFired, nullptr,
+                                          NS_CC_SKIPPABLE_DELAY,
+                                          nsITimer::TYPE_REPEATING_SLACK,
+                                          "CCTimerFired");
+    }
+  }
 }
 
 nsScriptNameSpaceManager*
 mozilla::dom::GetNameSpaceManager()
 {
   if (sDidShutdown)
     return nullptr;
 
--- a/dom/bindings/GenerateCSS2PropertiesWebIDL.py
+++ b/dom/bindings/GenerateCSS2PropertiesWebIDL.py
@@ -11,17 +11,17 @@ from mozbuild import shellutil
 
 # Generates a line of WebIDL with the given spelling of the property name
 # (whether camelCase, _underscorePrefixed, etc.) and the given array of
 # extended attributes.
 def generateLine(propName, extendedAttrs):
     return "  [%s] attribute DOMString %s;\n" % (", ".join(extendedAttrs),
                                                  propName)
 def generate(output, idlFilename, preprocessorHeader):
-    cpp = shellutil.split(buildconfig.substs['CPP'])
+    cpp = list(buildconfig.substs['CPP'])
     cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
     cpp.append(preprocessorHeader)
     preprocessed = subprocess.check_output(cpp)
 
     propList = eval(preprocessed)
     props = ""
     for [name, prop, id, flags, pref, proptype] in propList:
         if "CSS_PROPERTY_INTERNAL" in flags:
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1903,16 +1903,18 @@ CanvasRenderingContext2D::ReturnTarget(b
         }
       }
 
       if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
         // With the cairo backend we pushed an extra clip rect which we have to
         // balance out here. See the comment in RestoreClipsAndTransformToTarget.
         mTarget->PopClip();
       }
+
+      mTarget->SetTransform(Matrix());
     }
 
     mBufferProvider->ReturnDrawTarget(mTarget.forget());
   }
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::InitializeWithDrawTarget(nsIDocShell* aShell,
--- a/dom/events/test/pointerevents/mochitest.ini
+++ b/dom/events/test/pointerevents/mochitest.ini
@@ -56,17 +56,16 @@ support-files =
 [test_pointerevent_pointerleave_descendants-manual.html]
   support-files = pointerevent_pointerleave_descendants-manual.html
 [test_pointerevent_pointerleave_does_not_bubble-manual.html]
   support-files = pointerevent_pointerleave_does_not_bubble-manual.html
 [test_pointerevent_pointerleave_mouse-manual.html]
   support-files = pointerevent_pointerleave_mouse-manual.html
 [test_pointerevent_pointerleave_pen-manual.html]
   support-files = pointerevent_pointerleave_pen-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerleave_touch-manual.html]
   support-files = pointerevent_pointerleave_touch-manual.html
 [test_pointerevent_pointermove-manual.html]
   support-files = pointerevent_pointermove-manual.html
 [test_pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html]
   support-files = pointerevent_pointermove_isprimary_same_as_pointerdown-manual.html
 [test_pointerevent_pointermove-on-chorded-mouse-button.html]
   support-files = pointerevent_pointermove-on-chorded-mouse-button.html
@@ -75,17 +74,16 @@ support-files =
 [test_pointerevent_pointerout-manual.html]
   support-files = pointerevent_pointerout-manual.html
 [test_pointerevent_pointerout_after_pointercancel_touch-manual.html]
   support-files = pointerevent_pointerout_after_pointercancel_touch-manual.html
 [test_pointerevent_pointerout_after_pointerup_nohover-manual.html]
   support-files = pointerevent_pointerout_after_pointerup_nohover-manual.html
 [test_pointerevent_pointerout_pen-manual.html]
   support-files = pointerevent_pointerout_pen-manual.html
-  disabled = should be investigated
 [test_pointerevent_pointerout_received_once-manual.html]
   support-files = pointerevent_pointerout_received_once-manual.html
 [test_pointerevent_pointerover-manual.html]
   support-files = pointerevent_pointerover-manual.html
 [test_pointerevent_pointertype_mouse-manual.html]
   support-files = pointerevent_pointertype_mouse-manual.html
 [test_pointerevent_pointertype_pen-manual.html]
   support-files = pointerevent_pointertype_pen-manual.html
--- a/dom/events/test/pointerevents/mochitest_support_external.js
+++ b/dom/events/test/pointerevents/mochitest_support_external.js
@@ -37,36 +37,16 @@ if(testObj["status"] != testObj["PASS"])
 function completion_function() {
   console.log("w3c tests have been finished");
   if(!SimpleTest._stopOnLoad) {
     console.log("Finishing Mochitest system");
     SimpleTest.finish();
   }
 }
 
-// Helper function to send PointerEvent with different parameters
-function sendPointerEvent(int_win, elemId, pointerEventType, inputSource, params) {
-  var elem = int_win.document.getElementById(elemId);
-  if(!!elem) {
-    var rect = elem.getBoundingClientRect();
-    var eventObj = {type: pointerEventType, inputSource: inputSource};
-    if(params && "button" in params)
-      eventObj.button = params.button;
-    if(params && "isPrimary" in params)
-      eventObj.isPrimary = params.isPrimary;
-    else if(MouseEvent.MOZ_SOURCE_MOUSE == inputSource)
-      eventObj.isPrimary = true;
-    console.log(elemId, eventObj);
-    var salt = ("pointermove" == pointerEventType) ? 1 : 2;
-    synthesizePointer(elem, rect.width*salt/5, rect.height/2, eventObj, int_win);
-  } else {
-    is(!!elem, true, "Document should have element with id: " + elemId);
-  }
-}
-
 // Helper function to send MouseEvent with different parameters
 function sendMouseEvent(int_win, elemId, mouseEventType, params) {
   var elem = int_win.document.getElementById(elemId);
   if(!!elem) {
     var rect = elem.getBoundingClientRect();
     var eventObj = {type: mouseEventType};
     if(params && "button" in params)
       eventObj.button = params.button;
--- a/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_capture_mouse-manual.html
@@ -13,22 +13,22 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_capture_mouse-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "btnCapture", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target0",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointerup",   MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
+        sendMouseEvent(int_win, "btnCapture", "mousemove");
+        sendMouseEvent(int_win, "target0",    "mousemove");
+        sendMouseEvent(int_win, "target1",    "mousemove");
+        sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
+        sendMouseEvent(int_win, "target1",    "mousemove");
+        sendMouseEvent(int_win, "target1",    "mouseup");
+        sendMouseEvent(int_win, "target1",    "mousemove");
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_capture_suppressing_mouse-manual.html
@@ -13,23 +13,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_capture_suppressing_mouse-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE, {button:1});
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target0",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointerup",   MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1",    "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
+        sendMouseEvent(int_win, "target0",    "mousemove");
+        sendMouseEvent(int_win, "target1",    "mousemove");
+        sendMouseEvent(int_win, "btnCapture", "mousedown", {button:1});
+        sendMouseEvent(int_win, "target1",    "mousemove");
+        sendMouseEvent(int_win, "target0",    "mousemove");
+        sendMouseEvent(int_win, "target1",    "mousemove");
+        sendMouseEvent(int_win, "target1",    "mouseup");
+        sendMouseEvent(int_win, "target1",    "mousemove");
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_descendants-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_pointerleave_descendants-manual.html
@@ -13,17 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_pointerleave_descendants-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "log",     "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
+        sendMouseEvent(int_win, "target0", "mousemove");
+        sendMouseEvent(int_win, "log",     "mousemove");
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_pointerleave_pen-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_pointerleave_pen-manual.html
@@ -13,18 +13,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_pointerleave_pen-manual.html";
       }
       function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown",  {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-        sendMouseEvent(int_win, "target0", "mouseup",    {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-        sendMouseEvent(int_win, "target0", "mouseleave", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mousedown",   {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mouseup",     {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_pointerout_pen-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_pointerout_pen-manual.html
@@ -13,18 +13,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_pointerout_pen-manual.html";
       }
       function executeTest(int_win) {
-        sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-        sendMouseEvent(int_win, "target0", "mouseup",   {inputSource:MouseEvent.MOZ_SOURCE_PEN});
-        sendMouseEvent(int_win, "target0", "mouseout",  {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mousedown",   {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mouseup",     {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+        sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_releasepointercapture_onpointerup_mouse-manual.html
@@ -13,17 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_releasepointercapture_onpointerup_mouse-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "btnCapture", "pointerdown", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "btnCapture", "pointerup",   MouseEvent.MOZ_SOURCE_MOUSE);
+        sendMouseEvent(int_win, "btnCapture", "mousedown");
+        sendMouseEvent(int_win, "btnCapture", "mouseup");
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/events/test/pointerevents/test_pointerevent_setpointercapture_inactive_button_mouse-manual.html
+++ b/dom/events/test/pointerevents/test_pointerevent_setpointercapture_inactive_button_mouse-manual.html
@@ -13,18 +13,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     <script type="text/javascript">
       SimpleTest.waitForExplicitFinish();
       function startTest() {
         var iframe = document.getElementById("testFrame");
         iframe.src = "pointerevent_setpointercapture_inactive_button_mouse-manual.html";
       }
       function executeTest(int_win) {
-        sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target0", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
-        sendPointerEvent(int_win, "target1", "pointermove", MouseEvent.MOZ_SOURCE_MOUSE);
+        sendMouseEvent(int_win, "target1", "mousemove");
+        sendMouseEvent(int_win, "target0", "mousemove");
+        sendMouseEvent(int_win, "target1", "mousemove");
       }
     </script>
   </head>
   <body>
     <iframe id="testFrame" height="800" width="1000"></iframe>
   </body>
 </html>
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1671,20 +1671,19 @@ nsGenericHTMLElement::GetURIAttr(nsIAtom
                                             attr->GetStringValue(),
                                             OwnerDoc(), baseURI);
   return true;
 }
 
 /* static */ bool
 nsGenericHTMLElement::IsScrollGrabAllowed(JSContext*, JSObject*)
 {
-  // Only allow scroll grabbing in chrome and certified apps.
+  // Only allow scroll grabbing in chrome
   nsIPrincipal* prin = nsContentUtils::SubjectPrincipal();
-  return nsContentUtils::IsSystemPrincipal(prin) ||
-    prin->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
+  return nsContentUtils::IsSystemPrincipal(prin);
 }
 
 nsresult
 nsGenericHTMLElement::GetURIListAttr(nsIAtom* aAttr, nsAString& aResult)
 {
   aResult.Truncate();
 
   nsAutoString value;
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -275,18 +275,18 @@ interface nsIDOMWindowUtils : nsISupport
   const long MODIFIER_FNLOCK     = 0x0080;
   const long MODIFIER_NUMLOCK    = 0x0100;
   const long MODIFIER_SCROLLLOCK = 0x0200;
   const long MODIFIER_SYMBOL     = 0x0400;
   const long MODIFIER_SYMBOLLOCK = 0x0800;
   const long MODIFIER_OS         = 0x1000;
 
   /** Synthesize a mouse event. The event types supported are:
-   *    mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
-   *    MozMouseHittest
+   *    mousedown, mouseup, mousemove, mouseover, mouseout, mousecancel,
+   *    contextmenu, MozMouseHittest
    *
    * Events are sent in coordinates offset by aX and aY from the window.
    *
    * Note that additional events may be fired as a result of this call. For
    * instance, typically a click event will be fired as a result of a
    * mousedown and mouseup in sequence.
    *
    * Normally at this level of events, the mouseover and mouseout events are
@@ -296,16 +296,21 @@ interface nsIDOMWindowUtils : nsISupport
    *
    * Cannot be accessed from unprivileged context (not content-accessible)
    * Will throw a DOM security error if called without chrome privileges.
    *
    * The event is dispatched via the toplevel window, so it could go to any
    * window under the toplevel window, in some cases it could never reach this
    * window at all.
    *
+   * NOTE: mousecancel is used to represent the vanishing of an input device
+   * such as a pen leaving its digitizer by synthesizing a WidgetMouseEvent,
+   * whose mMessage is eMouseExitFromWidget and mExitFrom is
+   * WidgetMouseEvent::eTopLevel.
+   *
    * @param aType event type
    * @param aX x offset in CSS pixels
    * @param aY y offset in CSS pixels
    * @param aButton button to synthesize
    * @param aClickCount number of clicks that have been performed
    * @param aModifiers modifiers pressed, using constants defined as MODIFIER_*
    * @param aIgnoreRootScrollFrame whether the event should ignore viewport bounds
    *                           during dispatch
--- a/dom/media/test/test_trackelementevent.html
+++ b/dom/media/test/test_trackelementevent.html
@@ -27,31 +27,39 @@ SpecialPowers.pushPrefEnv({"set": [["med
     trackTwo.src = "bad-signature.vtt";
     trackTwo.kind = "captions";
 
     var trackThree = document.createElement("track");
     trackThree.src = "bad.vtt";
     trackThree.kind = "chapters";
 
     var events = 0;
-    function countEvent() {
-      ok(true, "A load or error event should have happened.");
+    function trackOneEvent() {
+      ok(true, "A load event for trackOne should have happened.");
+      events++ && events == 3 && SimpleTest.finish();
+    }
+    function trackTwoEvent() {
+      ok(true, "An error event for trackTwo should have happened.");
+      events++ && events == 3 && SimpleTest.finish();
+    }
+    function trackThreeEvent() {
+      ok(true, "An error event for trackThree should have happened.");
       events++ && events == 3 && SimpleTest.finish();
     }
 
     function shouldNotBeCalled() {
       ok(false, "Event should not have been called.");
     }
 
-    trackOne.addEventListener("load", countEvent);
+    trackOne.addEventListener("load", trackOneEvent);
     trackOne.addEventListener("error", shouldNotBeCalled)
     trackTwo.addEventListener("load", shouldNotBeCalled);
-    trackTwo.addEventListener("error", countEvent);
+    trackTwo.addEventListener("error", trackTwoEvent);
     trackThree.addEventListener("load", shouldNotBeCalled);
-    trackThree.addEventListener("error", countEvent);
+    trackThree.addEventListener("error", trackThreeEvent);
 
     document.getElementById("content").appendChild(video);
     video.appendChild(trackOne);
     video.appendChild(trackTwo);
     video.appendChild(trackThree);
   }
 );
 </script>
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -64,17 +64,16 @@ partial interface KeyframeEffectReadOnly
 };
 
 [Func="nsDocument::IsWebAnimationsEnabled",
  Constructor ((Element or CSSPseudoElement)? target,
               object? keyframes,
               optional (unrestricted double or KeyframeEffectOptions) options)]
 interface KeyframeEffect : KeyframeEffectReadOnly {
   inherit attribute (Element or CSSPseudoElement)? target;
-  // Bug 1216843 - implement animation composition
-  // inherit attribute IterationCompositeOperation iterationComposite;
+  inherit attribute IterationCompositeOperation    iterationComposite;
   // Bug 1216844 - implement additive animation
   // inherit attribute CompositeOperation          composite;
   [SetterThrows]
   inherit attribute DOMString                   spacing;
   [Throws]
   void setKeyframes (object? keyframes);
 };
--- a/dom/webidl/XMLHttpRequest.webidl
+++ b/dom/webidl/XMLHttpRequest.webidl
@@ -68,17 +68,17 @@ interface XMLHttpRequest : XMLHttpReques
 
   readonly attribute unsigned short readyState;
 
   // request
   [Throws]
   void open(ByteString method, DOMString url);
   [Throws]
   void open(ByteString method, DOMString url, boolean async,
-            optional DOMString? user, optional DOMString? password);
+            optional DOMString? user=null, optional DOMString? password=null);
   [Throws]
   void setRequestHeader(ByteString header, ByteString value);
 
   [SetterThrows]
   attribute unsigned long timeout;
 
   [SetterThrows]
   attribute boolean withCredentials;
--- a/dom/xhr/XMLHttpRequest.h
+++ b/dom/xhr/XMLHttpRequest.h
@@ -48,19 +48,18 @@ public:
 
   virtual uint16_t
   ReadyState() const = 0;
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, ErrorResult& aRv) = 0;
 
   virtual void
-  Open(const nsACString& aMethod, const nsAString& aUrl,
-       bool aAsync, const Optional<nsAString>& aUser,
-       const Optional<nsAString>& aPassword, ErrorResult& aRv) = 0;
+  Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
+       const nsAString& aUser, const nsAString& aPassword, ErrorResult& aRv) = 0;
 
   virtual void
   SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,
                    ErrorResult& aRv) = 0;
 
   virtual uint32_t
   Timeout() const = 0;
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1395,50 +1395,54 @@ XMLHttpRequestMainThread::Open(const nsA
   Optional<nsAString> username;
   if (optional_argc > 1) {
     username = &aUsername;
   }
   Optional<nsAString> password;
   if (optional_argc > 2) {
     password = &aPassword;
   }
-  return OpenInternal(aMethod, aUrl, async, username, password);
+  return Open(aMethod, aUrl, async, username, password);
 }
 
 // This case is hit when the async parameter is outright omitted, which
 // should set it to true (and the username and password to null).
 void
 XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
                                ErrorResult& aRv)
 {
-  aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
-                     Optional<nsAString>(), Optional<nsAString>());
+  aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), Optional<bool>(true),
+             Optional<nsAString>(), Optional<nsAString>());
 }
 
 // This case is hit when the async parameter is specified, even if the
 // JS value was "undefined" (which due to legacy reasons should be
 // treated as true, which is how it will already be passed in here).
 void
 XMLHttpRequestMainThread::Open(const nsACString& aMethod,
                                const nsAString& aUrl,
                                bool aAsync,
-                               const Optional<nsAString>& aUsername,
-                               const Optional<nsAString>& aPassword,
+                               const nsAString& aUsername,
+                               const nsAString& aPassword,
                                ErrorResult& aRv)
 {
-  aRv = OpenInternal(aMethod, NS_ConvertUTF16toUTF8(aUrl),
-                     Optional<bool>(aAsync), aUsername, aPassword);
+  Optional<nsAString> username;
+  username = &aUsername;
+  Optional<nsAString> password;
+  password = &aPassword;
+  aRv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl),
+             Optional<bool>(aAsync), username, password);
 }
 
 nsresult
-XMLHttpRequestMainThread::OpenInternal(const nsACString& aMethod,
-                                       const nsACString& aUrl,
-                                       const Optional<bool>& aAsync,
-                                       const Optional<nsAString>& aUsername,
-                                       const Optional<nsAString>& aPassword)
+XMLHttpRequestMainThread::Open(const nsACString& aMethod,
+                               const nsACString& aUrl,
+                               const Optional<bool>& aAsync,
+                               const Optional<nsAString>& aUsername,
+                               const Optional<nsAString>& aPassword)
 {
   bool async = aAsync.WasPassed() ? aAsync.Value() : true;
 
   // Gecko-specific
   if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
       GetOwner()->GetExtantDoc()) {
     GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
   }
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -244,20 +244,26 @@ public:
                          nsACString& aUploadContentType);
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl,
        ErrorResult& aRv) override;
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
-       const Optional<nsAString>& aUser,
-       const Optional<nsAString>& aPassword,
+       const nsAString& aUsername, const nsAString& aPassword,
        ErrorResult& aRv) override;
 
+  nsresult
+  Open(const nsACString& aMethod,
+       const nsACString& aUrl,
+       const Optional<bool>& aAsync,
+       const Optional<nsAString>& aUsername,
+       const Optional<nsAString>& aPassword);
+
   virtual void
   SetRequestHeader(const nsACString& aName, const nsACString& aValue,
                    ErrorResult& aRv) override
   {
     aRv = SetRequestHeader(aName, aValue);
   }
 
   virtual uint32_t
@@ -586,22 +592,16 @@ protected:
   void OnBodyParseEnd();
   void ChangeStateToDone();
 
   void StartProgressEventTimer();
   void StopProgressEventTimer();
 
   nsresult OnRedirectVerifyCallback(nsresult result);
 
-  nsresult OpenInternal(const nsACString& aMethod,
-                        const nsACString& aUrl,
-                        const Optional<bool>& aAsync,
-                        const Optional<nsAString>& aUsername,
-                        const Optional<nsAString>& aPassword);
-
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   nsCOMPtr<nsISupports> mContext;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIChannel> mChannel;
   nsCString mRequestMethod;
   nsCOMPtr<nsIURI> mRequestURL;
   nsCOMPtr<nsIDocument> mResponseXML;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1421,26 +1421,25 @@ OpenRunnable::MainThreadRunInternal()
   if (mTimeout) {
     rv = mProxy->mXHR->SetTimeout(mTimeout);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   MOZ_ASSERT(!mProxy->mInOpen);
   mProxy->mInOpen = true;
 
-  ErrorResult rv2;
-  mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
+  rv = mProxy->mXHR->Open(mMethod, NS_ConvertUTF16toUTF8(mURL),
+                          Optional<bool>(true), mUser, mPassword);
 
   MOZ_ASSERT(mProxy->mInOpen);
   mProxy->mInOpen = false;
 
-  if (rv2.Failed()) {
-    return rv2.StealNSResult();
-  }
-
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  ErrorResult rv2;
   mProxy->mXHR->SetResponseType(mResponseType, rv2);
   if (rv2.Failed()) {
     return rv2.StealNSResult();
   }
 
   return NS_OK;
 }
 
@@ -1844,19 +1843,21 @@ XMLHttpRequestWorker::Notify(Status aSta
     mCanceled = true;
     ReleaseProxy(WorkerIsGoingAway);
   }
 
   return true;
 }
 
 void
-XMLHttpRequestWorker::Open(const nsACString& aMethod, const nsAString& aUrl,
-                           bool aAsync, const Optional<nsAString>& aUser,
-                           const Optional<nsAString>& aPassword, ErrorResult& aRv)
+XMLHttpRequestWorker::Open(const nsACString& aMethod,
+                           const nsAString& aUrl, bool aAsync,
+                           const Optional<nsAString>& aUser,
+                           const Optional<nsAString>& aPassword,
+                           ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (mCanceled) {
     aRv.ThrowUncatchableException();
     return;
   }
 
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -92,18 +92,30 @@ public:
        ErrorResult& aRv) override
   {
     Open(aMethod, aUrl, true, Optional<nsAString>(),
          Optional<nsAString>(), aRv);
   }
 
   virtual void
   Open(const nsACString& aMethod, const nsAString& aUrl, bool aAsync,
-       const Optional<nsAString>& aUser, const Optional<nsAString>& aPassword,
-       ErrorResult& aRv) override;
+       const nsAString& aUsername, const nsAString& aPassword,
+       ErrorResult& aRv) override
+  {
+    Optional<nsAString> username;
+    username = &aUsername;
+    Optional<nsAString> password;
+    password = &aPassword;
+    Open(aMethod, aUrl, aAsync, username, password, aRv);
+  }
+
+  void
+  Open(const nsACString& aMethod, const nsAString& aUrl,
+       bool aAsync, const Optional<nsAString>& aUser,
+       const Optional<nsAString>& aPassword, ErrorResult& aRv);
 
   virtual void
   SetRequestHeader(const nsACString& aHeader, const nsACString& aValue,
                    ErrorResult& aRv) override;
 
   virtual uint32_t
   Timeout() const override
   {
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -11,16 +11,17 @@
 #include "LayerManagerComposite.h"      // for LayerManagerComposite, etc
 #include "Layers.h"                     // for Layer, ContainerLayer, etc
 #include "gfxPoint.h"                   // for gfxPoint, gfxSize
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
 #include "mozilla/WidgetUtils.h"        // for ComputeTransformForRotation
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
+#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/APZUtils.h"    // for CompleteAsyncTransform
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
 #include "mozilla/layers/CompositorThread.h"
@@ -548,28 +549,55 @@ AsyncCompositionManager::AlignFixedAndSt
         //       all of the translation correctly. In such a case,
         //       |a[Previous|Current]TransformForRoot| would need to be adjusted
         //       to reflect only the unconsumed part of the translation.
         return translationConsumed ? TraversalFlag::Skip : TraversalFlag::Continue;
       });
 }
 
 static void
-SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
-            StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
+SampleValue(float aPortion, Animation& aAnimation,
+            const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
+            const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
+            Animatable* aValue, Layer* aLayer)
 {
-  StyleAnimationValue interpolatedValue;
   NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
                aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
                aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
                "Must have same unit");
+
+  StyleAnimationValue startValue = aStart;
+  StyleAnimationValue endValue = aEnd;
+  // Iteration composition for accumulate
+  if (static_cast<dom::IterationCompositeOperation>
+        (aAnimation.iterationComposite()) ==
+          dom::IterationCompositeOperation::Accumulate &&
+      aCurrentIteration > 0) {
+    // FIXME: Bug 1293492: Add a utility function to calculate both of
+    // below StyleAnimationValues.
+    DebugOnly<bool> accumulateResult =
+      StyleAnimationValue::Accumulate(aAnimation.property(),
+                                      startValue,
+                                      aLastValue,
+                                      aCurrentIteration);
+    MOZ_ASSERT(accumulateResult, "could not accumulate value");
+    accumulateResult =
+      StyleAnimationValue::Accumulate(aAnimation.property(),
+                                      endValue,
+                                      aLastValue,
+                                      aCurrentIteration);
+    MOZ_ASSERT(accumulateResult, "could not accumulate value");
+  }
+
+  StyleAnimationValue interpolatedValue;
   // This should never fail because we only pass transform and opacity values
   // to the compositor and they should never fail to interpolate.
   DebugOnly<bool> uncomputeResult =
-    StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
+    StyleAnimationValue::Interpolate(aAnimation.property(),
+                                     startValue, endValue,
                                      aPortion, interpolatedValue);
   MOZ_ASSERT(uncomputeResult, "could not uncompute value");
 
   if (aAnimation.property() == eCSSProperty_opacity) {
     *aValue = interpolatedValue.GetFloatValue();
     return;
   }
 
@@ -678,18 +706,22 @@ SampleAnimations(Layer* aLayer, TimeStam
 
           double portion =
             ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
                                                positionInSegment,
                                            computedTiming.mBeforeFlag);
 
           // interpolate the property
           Animatable interpolatedValue;
-          SampleValue(portion, animation, animData.mStartValues[segmentIndex],
-                      animData.mEndValues[segmentIndex], &interpolatedValue, layer);
+          SampleValue(portion, animation,
+                      animData.mStartValues[segmentIndex],
+                      animData.mEndValues[segmentIndex],
+                      animData.mEndValues.LastElement(),
+                      computedTiming.mCurrentIteration,
+                      &interpolatedValue, layer);
           LayerComposite* layerComposite = layer->AsLayerComposite();
           switch (animation.property()) {
           case eCSSProperty_opacity:
           {
             layerComposite->SetShadowOpacity(interpolatedValue.get_float());
             layerComposite->SetShadowOpacitySetByAnimation(true);
             break;
           }
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -199,16 +199,17 @@ struct Animation {
   float iterationStart;
   // This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
   uint8_t direction;
   nsCSSPropertyID property;
   AnimationData data;
   float playbackRate;
   // This is used in the transformed progress calculation.
   TimingFunction easingFunction;
+  uint8_t iterationComposite;
 };
 
 // Change a layer's attributes
 struct CommonLayerAttributes {
   IntRect layerBounds;
   LayerIntRegion visibleRegion;
   EventRegions eventRegions;
   TransformMatrix transform;
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -30,16 +30,26 @@ template <class> struct DefaultHasher;
 template <class, class> class HashMapEntry;
 namespace detail {
     template <class T> class HashTableEntry;
     template <class T, class HashPolicy, class AllocPolicy> class HashTable;
 } // namespace detail
 
 /*****************************************************************************/
 
+// The "generation" of a hash table is an opaque value indicating the state of
+// modification of the hash table through its lifetime.  If the generation of
+// a hash table compares equal at times T1 and T2, then lookups in the hash
+// table, pointers to (or into) hash table entries, etc. at time T1 are valid
+// at time T2.  If the generation compares unequal, these computations are all
+// invalid and must be performed again to be used.
+//
+// Generations are meaningfully comparable only with respect to a single hash
+// table.  It's always nonsensical to compare the generation of distinct hash
+// tables H1 and H2.
 using Generation = mozilla::Opaque<uint64_t>;
 
 // A JS-friendly, STL-like container providing a hash-based map from keys to
 // values. In particular, HashMap calls constructors and destructors of all
 // objects added so non-PODs may be used safely.
 //
 // Key/Value requirements:
 //  - movable, destructible, assignable
@@ -205,18 +215,16 @@ class HashMap
     // guarantee that |impl| is the first field in HashMap.
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return impl.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     }
 
-    // If |generation()| is the same before and after a HashMap operation,
-    // pointers into the table remain valid.
     Generation generation() const {
         return impl.generation();
     }
 
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return impl.lookup(l).found();
@@ -446,18 +454,16 @@ class HashSet
     // guarantee that |impl| is the first field in HashSet.
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return impl.sizeOfExcludingThis(mallocSizeOf);
     }
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + impl.sizeOfExcludingThis(mallocSizeOf);
     }
 
-    // If |generation()| is the same before and after a HashSet operation,
-    // pointers into the table remain valid.
     Generation generation() const {
         return impl.generation();
     }
 
     /************************************************** Shorthand operations */
 
     bool has(const Lookup& l) const {
         return impl.lookup(l).found();
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -214,16 +214,24 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool finalizeInBackground(Value priv) const {
         /*
          * Called on creation of a proxy to determine whether its finalize
          * method can be finalized on the background thread.
          */
         return true;
     }
 
+    virtual bool canNurseryAllocate() const {
+        /*
+         * Nursery allocation is allowed if and only if it is safe to not
+         * run |finalize| when the ProxyObject dies.
+         */
+        return false;
+    }
+
     /* Policy enforcement methods.
      *
      * enter() allows the policy to specify whether the caller may perform |act|
      * on the proxy's |id| property. In the case when |act| is CALL, |id| is
      * generally JSID_VOID.
      *
      * The |act| parameter to enter() specifies the action being performed.
      * If |bp| is false, the method suggests that the caller throw (though it
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1716,16 +1716,19 @@ JS_PUBLIC_API(void) HeapValuePostBarrier
 
 template <>
 struct GCPolicy<JS::Value>
 {
     static Value initial() { return UndefinedValue(); }
     static void trace(JSTracer* trc, Value* v, const char* name) {
         js::UnsafeTraceManuallyBarrieredEdge(trc, v, name);
     }
+    static bool isTenured(const Value& thing) {
+        return !thing.isGCThing() || !IsInsideNursery(thing.toGCThing());
+    }
 };
 
 } // namespace JS
 
 namespace js {
 
 template <>
 struct BarrierMethods<JS::Value>
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -6961,30 +6961,25 @@ CheckStatement(FunctionValidator& f, Par
 static bool
 ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
 {
     TokenStream& tokenStream = m.tokenStream();
 
     tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
     *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
 
-    RootedPropertyName name(m.cx());
-
     TokenKind tk;
     if (!tokenStream.getToken(&tk, TokenStream::Operand))
         return false;
-    if (tk == TOK_NAME) {
-        name = tokenStream.currentName();
-    } else if (tk == TOK_YIELD) {
-        if (!m.parser().checkYieldNameValidity())
-            return false;
-        name = m.cx()->names().yield;
-    } else {
+    if (tk != TOK_NAME && tk != TOK_YIELD)
         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
-    }
+
+    RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
+    if (!name)
+        return false;
 
     ParseNode* fn = m.parser().handler.newFunctionDefinition();
     if (!fn)
         return false;
 
     RootedFunction& fun = m.dummyFunction();
     fun->setAtom(name);
     fun->setArgCount(0);
--- a/js/src/asmjs/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -226,17 +226,16 @@ CodeSegment::create(JSContext* cx,
     uint8_t* codeBase = cs->base();
 
     cs->functionCodeLength_ = linkData.functionCodeLength;
     cs->codeLength_ = bytecode.length();
     cs->globalDataLength_ = linkData.globalDataLength;
     cs->interruptCode_ = codeBase + linkData.interruptOffset;
     cs->outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset;
     cs->unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset;
-    cs->badIndirectCallCode_ = codeBase + linkData.badIndirectCallOffset;
 
     {
         JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread()));
         AutoFlushICache afc("CodeSegment::create");
         AutoFlushICache::setRange(uintptr_t(codeBase), cs->codeLength());
 
         memcpy(codeBase, bytecode.begin(), bytecode.length());
         StaticallyLink(*cs, linkData, cx);
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -49,17 +49,16 @@ class CodeSegment
     uint8_t* bytes_;
     uint32_t functionCodeLength_;
     uint32_t codeLength_;
     uint32_t globalDataLength_;
 
     // These are pointers into code for stubs used for asynchronous
     // signal-handler control-flow transfer.
     uint8_t* interruptCode_;
-    uint8_t* badIndirectCallCode_;
     uint8_t* outOfBoundsCode_;
     uint8_t* unalignedAccessCode_;
 
     // The profiling mode may be changed dynamically.
     bool profilingEnabled_;
 
     CodeSegment() { PodZero(this); }
     template <class> friend struct js::MallocProvider;
@@ -79,17 +78,16 @@ class CodeSegment
 
     uint8_t* base() const { return bytes_; }
     uint8_t* globalData() const { return bytes_ + codeLength_; }
     uint32_t codeLength() const { return codeLength_; }
     uint32_t globalDataLength() const { return globalDataLength_; }
     uint32_t totalLength() const { return codeLength_ + globalDataLength_; }
 
     uint8_t* interruptCode() const { return interruptCode_; }
-    uint8_t* badIndirectCallCode() const { return badIndirectCallCode_; }
     uint8_t* outOfBoundsCode() const { return outOfBoundsCode_; }
     uint8_t* unalignedAccessCode() const { return unalignedAccessCode_; }
 
     // The range [0, functionBytes) is a subrange of [0, codeBytes) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
--- a/js/src/asmjs/WasmFrameIterator.cpp
+++ b/js/src/asmjs/WasmFrameIterator.cpp
@@ -380,22 +380,22 @@ wasm::GenerateFunctionPrologue(MacroAsse
     // Generate table entry thunk:
     masm.haltingAlign(CodeAlignment);
     offsets->tableEntry = masm.currentOffset();
     switch (sigId.kind()) {
       case SigIdDesc::Kind::Global: {
         Register scratch = WasmTableCallScratchReg;
         masm.loadWasmGlobalPtr(sigId.globalDataOffset(), scratch);
         masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, scratch,
-                      JumpTarget::BadIndirectCall);
+                      JumpTarget::IndirectCallBadSig);
         break;
       }
       case SigIdDesc::Kind::Immediate:
         masm.branch32(Assembler::Condition::NotEqual, WasmTableCallSigReg, Imm32(sigId.immediate()),
-                      JumpTarget::BadIndirectCall);
+                      JumpTarget::IndirectCallBadSig);
         break;
       case SigIdDesc::Kind::None:
         break;
     }
     offsets->tableProfilingJump = masm.nopPatchableToNearJump().offset();
 
     // Generate normal prologue:
     masm.nopAlign(CodeAlignment);
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -503,17 +503,16 @@ ModuleGenerator::finishCodegen()
     if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit))
         return false;
 
     // Fill in LinkData with the offsets of these stubs.
 
     linkData_.interruptOffset = interruptExit.begin;
     linkData_.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin;
     linkData_.unalignedAccessOffset = jumpTargets[JumpTarget::UnalignedAccess].begin;
-    linkData_.badIndirectCallOffset = jumpTargets[JumpTarget::BadIndirectCall].begin;
 
     // Only call convertOutOfRangeBranchesToThunks after all other codegen that may
     // emit new jumps to JumpTargets has finished.
 
     if (!convertOutOfRangeBranchesToThunks())
         return false;
 
     // Now that all thunks have been generated, patch all the thunks.
--- a/js/src/asmjs/WasmInstance.cpp
+++ b/js/src/asmjs/WasmInstance.cpp
@@ -557,16 +557,22 @@ ReadCustomDoubleNaNObject(JSContext* cx,
         return false;
     u64 |= uint32_t(i32);
 
     BitwiseCast(u64, ret);
     return true;
 }
 
 WasmInstanceObject*
+Instance::objectUnbarriered() const
+{
+    return object_.unbarrieredGet();
+}
+
+WasmInstanceObject*
 Instance::object() const
 {
     return object_;
 }
 
 bool
 Instance::callExport(JSContext* cx, uint32_t funcDefIndex, CallArgs args)
 {
@@ -838,28 +844,30 @@ Instance::ensureProfilingState(JSContext
         FuncImportTls& import = funcImportTls(fi);
         if (import.obj && import.obj->is<WasmInstanceObject>()) {
             Code& code = import.obj->as<WasmInstanceObject>().instance().code();
             UpdateEntry(code, newProfilingEnabled, &import.code);
         }
     }
 
     for (const SharedTable& table : tables_) {
-        if (!table->isTypedFunction() || !table->initialized())
+        if (!table->isTypedFunction())
             continue;
 
         // This logic will have to be generalized to match the import logic
         // above if wasm can create typed function tables since a single table
         // can contain elements from multiple instances.
         MOZ_ASSERT(metadata().kind == ModuleKind::AsmJS);
 
         void** array = table->internalArray();
         uint32_t length = table->length();
-        for (size_t i = 0; i < length; i++)
-            UpdateEntry(*code_, newProfilingEnabled, &array[i]);
+        for (size_t i = 0; i < length; i++) {
+            if (array[i])
+                UpdateEntry(*code_, newProfilingEnabled, &array[i]);
+        }
     }
 
     return true;
 }
 
 void
 Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                         Metadata::SeenSet* seenMetadata,
--- a/js/src/asmjs/WasmInstance.h
+++ b/js/src/asmjs/WasmInstance.h
@@ -64,16 +64,20 @@ class Instance
     // Only WasmInstanceObject can call the private trace function.
     friend class js::WasmInstanceObject;
     void tracePrivate(JSTracer* trc);
 
     // Only WasmMemoryObject can call the private onMovingGrow notification.
     friend class js::WasmMemoryObject;
     void onMovingGrow(uint8_t* prevMemoryBase);
 
+    // Called by WasmTableObject to barrier table writes.
+    friend class Table;
+    WasmInstanceObject* objectUnbarriered() const;
+
   public:
     Instance(JSContext* cx,
              HandleWasmInstanceObject object,
              UniqueCode code,
              HandleWasmMemoryObject memory,
              SharedTableVector&& tables,
              Handle<FunctionVector> funcImports,
              const ValVector& globalImports);
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -529,16 +529,18 @@ WasmInstanceObject::create(JSContext* cx
     AutoSetNewObjectMetadata metadata(cx);
     RootedWasmInstanceObject obj(cx, NewObjectWithGivenProto<WasmInstanceObject>(cx, proto));
     if (!obj)
         return nullptr;
 
     obj->setReservedSlot(EXPORTS_SLOT, PrivateValue(exports.release()));
     MOZ_ASSERT(obj->isNewborn());
 
+    MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
+
     // Root the Instance via WasmInstanceObject before any possible GC.
     auto* instance = cx->new_<Instance>(cx,
                                         obj,
                                         Move(code),
                                         memory,
                                         Move(tables),
                                         funcImports,
                                         globalImports);
@@ -1195,34 +1197,29 @@ WasmTableObject::getImpl(JSContext* cx, 
     if (indexDbl < 0 || indexDbl >= table.length()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
         return false;
     }
 
     uint32_t index = uint32_t(indexDbl);
     MOZ_ASSERT(double(index) == indexDbl);
 
-    if (!table.initialized()) {
+    ExternalTableElem& elem = table.externalArray()[index];
+    if (!elem.code) {
         args.rval().setNull();
         return true;
     }
 
-    ExternalTableElem& elem = table.externalArray()[index];
     Instance& instance = *elem.tls->instance;
-    const CodeRange* codeRange = instance.code().lookupRange(elem.code);
-
-    // A non-function code range means the bad-indirect-call stub, so a null element.
-    if (!codeRange || !codeRange->isFunction()) {
-        args.rval().setNull();
-        return true;
-    }
+    const CodeRange& codeRange = *instance.code().lookupRange(elem.code);
+    MOZ_ASSERT(codeRange.isFunction());
 
     RootedWasmInstanceObject instanceObj(cx, instance.object());
     RootedFunction fun(cx);
-    if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange->funcDefIndex(), &fun))
+    if (!instanceObj->getExportedFunction(cx, instanceObj, codeRange.funcDefIndex(), &fun))
         return false;
 
     args.rval().setObject(*fun);
     return true;
 }
 
 /* static */ bool
 WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp)
@@ -1253,25 +1250,16 @@ WasmTableObject::setImpl(JSContext* cx, 
     MOZ_ASSERT(double(index) == indexDbl);
 
     RootedFunction value(cx);
     if (!IsExportedFunction(args[1], &value) && !args[1].isNull()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_TABLE_VALUE);
         return false;
     }
 
-    if (!table.initialized()) {
-        if (!value) {
-            args.rval().setUndefined();
-            return true;
-        }
-
-        table.init(ExportedFunctionToInstance(value));
-    }
-
     if (value) {
         RootedWasmInstanceObject instanceObj(cx, ExportedFunctionToInstanceObject(value));
         uint32_t funcDefIndex = ExportedFunctionToDefinitionIndex(value);
 
 #ifdef DEBUG
         RootedFunction f(cx);
         MOZ_ASSERT(instanceObj->getExportedFunction(cx, instanceObj, funcDefIndex, &f));
         MOZ_ASSERT(value == f);
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -457,23 +457,16 @@ Module::initSegments(JSContext* cx,
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT, "data", "memory");
                 return false;
             }
         }
     } else {
         MOZ_ASSERT(dataSegments_.empty());
     }
 
-    // Ensure all tables are initialized before storing into them.
-
-    for (const SharedTable& table : tables) {
-        if (!table->initialized())
-            table->init(instance);
-    }
-
     // Now that initialization can't fail partway through, write data/elem
     // segments into memories/tables.
 
     for (const ElemSegment& seg : elemSegments_) {
         Table& table = *tables[seg.tableIndex];
         uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
         bool profilingEnabled = instance.code().profilingEnabled();
         const CodeRangeVector& codeRanges = metadata().codeRanges;
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -46,17 +46,16 @@ ReadI64Object(JSContext* cx, HandleValue
 
 struct LinkDataCacheablePod
 {
     uint32_t functionCodeLength;
     uint32_t globalDataLength;
     uint32_t interruptOffset;
     uint32_t outOfBoundsOffset;
     uint32_t unalignedAccessOffset;
-    uint32_t badIndirectCallOffset;
 
     LinkDataCacheablePod() { mozilla::PodZero(this); }
 };
 
 struct LinkData : LinkDataCacheablePod
 {
     LinkDataCacheablePod& pod() { return *this; }
     const LinkDataCacheablePod& pod() const { return *this; }
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -911,17 +911,18 @@ GenerateThrow(MacroAssembler& masm)
 Offsets
 wasm::GenerateJumpTarget(MacroAssembler& masm, JumpTarget target)
 {
     switch (target) {
       case JumpTarget::StackOverflow:
         return GenerateStackOverflow(masm);
       case JumpTarget::Throw:
         return GenerateThrow(masm);
-      case JumpTarget::BadIndirectCall:
+      case JumpTarget::IndirectCallToNull:
+      case JumpTarget::IndirectCallBadSig:
       case JumpTarget::OutOfBounds:
       case JumpTarget::UnalignedAccess:
       case JumpTarget::Unreachable:
       case JumpTarget::IntegerOverflow:
       case JumpTarget::InvalidConversionToInteger:
       case JumpTarget::IntegerDivideByZero:
       case JumpTarget::ImpreciseSimdConversion:
         return GenerateTrapStub(masm, Trap(target));
--- a/js/src/asmjs/WasmTable.cpp
+++ b/js/src/asmjs/WasmTable.cpp
@@ -43,61 +43,42 @@ Table::create(JSContext* cx, const Table
         array = cx->pod_calloc<void*>(desc.initial);
     if (!array)
         return nullptr;
 
     table->maybeObject_.set(maybeObject);
     table->array_.reset((uint8_t*)array);
     table->kind_ = desc.kind;
     table->length_ = desc.initial;
-    table->initialized_ = false;
     table->external_ = desc.external;
     return table;
 }
 
 void
-Table::init(Instance& instance)
-{
-    MOZ_ASSERT(!initialized());
-    initialized_ = true;
-
-    void* code = instance.codeSegment().badIndirectCallCode();
-    if (external_) {
-        ExternalTableElem* array = externalArray();
-        TlsData* tls = &instance.tlsData();
-        for (uint32_t i = 0; i < length_; i++) {
-            array[i].code = code;
-            array[i].tls = tls;
-        }
-    } else {
-        void** array = internalArray();
-        for (uint32_t i = 0; i < length_; i++)
-            array[i] = code;
-    }
-}
-
-void
 Table::tracePrivate(JSTracer* trc)
 {
     // If this table has a WasmTableObject, then this method is only called by
     // WasmTableObject's trace hook so maybeObject_ must already be marked.
     // TraceEdge is called so that the pointer can be updated during a moving
     // GC. TraceWeakEdge may sound better, but it is less efficient given that
     // we know object_ is already marked.
     if (maybeObject_) {
         MOZ_ASSERT(!gc::IsAboutToBeFinalized(&maybeObject_));
         TraceEdge(trc, &maybeObject_, "wasm table object");
     }
 
-    if (!initialized_ || !external_)
-        return;
-
-    ExternalTableElem* array = externalArray();
-    for (uint32_t i = 0; i < length_; i++)
-        array[i].tls->instance->trace(trc);
+    if (external_) {
+        ExternalTableElem* array = externalArray();
+        for (uint32_t i = 0; i < length_; i++) {
+            if (array[i].tls)
+                array[i].tls->instance->trace(trc);
+            else
+                MOZ_ASSERT(!array[i].code);
+        }
+    }
 }
 
 void
 Table::trace(JSTracer* trc)
 {
     // The trace hook of WasmTableObject will call Table::tracePrivate at
     // which point we can mark the rest of the children. If there is no
     // WasmTableObject, call Table::tracePrivate directly. Redirecting through
@@ -107,46 +88,53 @@ Table::trace(JSTracer* trc)
         TraceEdge(trc, &maybeObject_, "wasm table object");
     else
         tracePrivate(trc);
 }
 
 void**
 Table::internalArray() const
 {
-    MOZ_ASSERT(initialized_);
     MOZ_ASSERT(!external_);
     return (void**)array_.get();
 }
 
 ExternalTableElem*
 Table::externalArray() const
 {
-    MOZ_ASSERT(initialized_);
     MOZ_ASSERT(external_);
     return (ExternalTableElem*)array_.get();
 }
 
 void
 Table::set(uint32_t index, void* code, Instance& instance)
 {
     if (external_) {
         ExternalTableElem& elem = externalArray()[index];
+        if (elem.tls)
+            JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
+
         elem.code = code;
         elem.tls = &instance.tlsData();
+
+        MOZ_ASSERT(elem.tls->instance->objectUnbarriered()->isTenured(), "no writeBarrierPost");
     } else {
         internalArray()[index] = code;
     }
 }
 
 void
 Table::setNull(uint32_t index)
 {
     // Only external tables can set elements to null after initialization.
     ExternalTableElem& elem = externalArray()[index];
-    elem.code = elem.tls->instance->codeSegment().badIndirectCallCode();
+    if (elem.tls)
+        JSObject::writeBarrierPre(elem.tls->instance->objectUnbarriered());
+
+    elem.code = nullptr;
+    elem.tls = nullptr;
 }
 
 size_t
 Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return mallocSizeOf(array_.get());
 }
--- a/js/src/asmjs/WasmTable.h
+++ b/js/src/asmjs/WasmTable.h
@@ -31,42 +31,34 @@ namespace wasm {
 class Table : public ShareableBase<Table>
 {
     typedef UniquePtr<uint8_t[], JS::FreePolicy> UniqueByteArray;
 
     ReadBarrieredWasmTableObject maybeObject_;
     UniqueByteArray              array_;
     TableKind                    kind_;
     uint32_t                     length_;
-    bool                         initialized_;
     bool                         external_;
 
     void tracePrivate(JSTracer* trc);
     friend class js::WasmTableObject;
 
   public:
     static RefPtr<Table> create(JSContext* cx, const TableDesc& desc,
                                 HandleWasmTableObject maybeObject);
     void trace(JSTracer* trc);
 
-    // These accessors may be used before initialization.
-
     bool external() const { return external_; }
     bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
     uint32_t length() const { return length_; }
     uint8_t* base() const { return array_.get(); }
 
-    // A Table must be initialized before any dependent instance can execute.
-
-    bool initialized() const { return initialized_; }
-    void init(Instance& instance);
-
-    // After initialization, elements must be accessed. All updates must go
-    // through a set() function with the exception of (profiling) updates to the
-    // callee pointer that do not change which logical function is being called.
+    // All updates must go through a set() function with the exception of
+    // (profiling) updates to the callee pointer that do not change which
+    // logical function is being called.
 
     void** internalArray() const;
     ExternalTableElem* externalArray() const;
     void set(uint32_t index, void* code, Instance& instance);
     void setNull(uint32_t index);
 
     // about:memory reporting:
 
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -112,18 +112,21 @@ HandleTrap(int32_t trapIndex)
         errorNumber = JSMSG_WASM_INTEGER_OVERFLOW;
         break;
       case Trap::InvalidConversionToInteger:
         errorNumber = JSMSG_WASM_INVALID_CONVERSION;
         break;
       case Trap::IntegerDivideByZero:
         errorNumber = JSMSG_WASM_INT_DIVIDE_BY_ZERO;
         break;
-      case Trap::BadIndirectCall:
-        errorNumber = JSMSG_WASM_BAD_IND_CALL;
+      case Trap::IndirectCallToNull:
+        errorNumber = JSMSG_WASM_IND_CALL_TO_NULL;
+        break;
+      case Trap::IndirectCallBadSig:
+        errorNumber = JSMSG_WASM_IND_CALL_BAD_SIG;
         break;
       case Trap::ImpreciseSimdConversion:
         errorNumber = JSMSG_SIMD_FAILED_CONVERSION;
         break;
       case Trap::OutOfBounds:
         errorNumber = JSMSG_BAD_INDEX;
         break;
       case Trap::UnalignedAccess:
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -870,18 +870,20 @@ enum class Trap
     // Trying to coerce NaN to an integer.
     InvalidConversionToInteger,
     // Integer division by zero.
     IntegerDivideByZero,
     // Out of bounds on wasm memory accesses and asm.js SIMD/atomic accesses.
     OutOfBounds,
     // Unaligned memory access.
     UnalignedAccess,
-    // Bad signature for an indirect call.
-    BadIndirectCall,
+    // call_indirect to null.
+    IndirectCallToNull,
+    // call_indirect signature mismatch.
+    IndirectCallBadSig,
 
     // (asm.js only) SIMD float to int conversion failed because the input
     // wasn't in bounds.
     ImpreciseSimdConversion,
 
     Limit
 };
 
@@ -894,17 +896,18 @@ enum class JumpTarget
 {
     // Traps
     Unreachable = unsigned(Trap::Unreachable),
     IntegerOverflow = unsigned(Trap::IntegerOverflow),
     InvalidConversionToInteger = unsigned(Trap::InvalidConversionToInteger),
     IntegerDivideByZero = unsigned(Trap::IntegerDivideByZero),
     OutOfBounds = unsigned(Trap::OutOfBounds),
     UnalignedAccess = unsigned(Trap::UnalignedAccess),
-    BadIndirectCall = unsigned(Trap::BadIndirectCall),
+    IndirectCallToNull = unsigned(Trap::IndirectCallToNull),
+    IndirectCallBadSig = unsigned(Trap::IndirectCallBadSig),
     ImpreciseSimdConversion = unsigned(Trap::ImpreciseSimdConversion),
     // Non-traps
     StackOverflow,
     Throw,
     Limit
 };
 
 typedef EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -712,16 +712,18 @@ PromiseObject::create(JSContext* cx, Han
         usedProto = CheckedUnwrap(proto);
         if (!usedProto)
             return nullptr;
     }
 
 
     // Steps 3-7.
     Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx, usedProto, wrappedProto));
+    if (!promise)
+        return nullptr;
 
     RootedValue promiseVal(cx, ObjectValue(*promise));
     if (wrappedProto && !cx->compartment()->wrap(cx, &promiseVal))
         return nullptr;
 
     // Step 8.
     // The resolving functions are created in the compartment active when the
     // (maybe wrapped) Promise constructor was called. They contain checks and
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -19,18 +19,18 @@
 
 #include "jsobjinlines.h"
 
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
+using mozilla::ArrayLength;
 using mozilla::CheckedInt;
-using mozilla::ArrayLength;
 using mozilla::Maybe;
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 16-25.
  */
 bool
 js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
@@ -182,18 +182,28 @@ CheckPatternSyntax(JSContext* cx, Handle
 
 enum RegExpSharedUse {
     UseRegExpShared,
     DontUseRegExpShared
 };
 
 /*
  * ES 2016 draft Mar 25, 2016 21.2.3.2.2.
- * Because this function only ever returns |obj| in the spec, provided by the
- * user, we omit it and just return the usual success/failure.
+ *
+ * Steps 14-15 set |obj|'s "lastIndex" property to zero.  Some of
+ * RegExpInitialize's callers have a fresh RegExp not yet exposed to script:
+ * in these cases zeroing "lastIndex" is infallible.  But others have a RegExp
+ * whose "lastIndex" property might have been made non-writable: here, zeroing
+ * "lastIndex" can fail.  We efficiently solve this problem by completely
+ * removing "lastIndex" zeroing from the provided function.
+ *
+ * CALLERS MUST HANDLE "lastIndex" ZEROING THEMSELVES!
+ *
+ * Because this function only ever returns a user-provided |obj| in the spec,
+ * we omit it and just return the usual success/failure.
  */
 static bool
 RegExpInitializeIgnoringLastIndex(JSContext* cx, Handle<RegExpObject*> obj,
                                   HandleValue patternValue, HandleValue flagsValue,
                                   RegExpSharedUse sharedUse = DontUseRegExpShared)
 {
     RootedAtom pattern(cx);
     if (patternValue.isUndefined()) {
@@ -342,16 +352,20 @@ regexp_compile_impl(JSContext* cx, const
         RootedValue P(cx, patternValue);
         RootedValue F(cx, args.get(1));
 
         // Step 5, minus lastIndex zeroing.
         if (!RegExpInitializeIgnoringLastIndex(cx, regexp, P, F))
             return false;
     }
 
+    // The final niggling bit of step 5.
+    //
+    // |regexp| is user-exposed, but if its "lastIndex" property hasn't been
+    // made non-writable, we can still use a fast path to zero it.
     if (regexp->lookupPure(cx->names().lastIndex)->writable()) {
         regexp->zeroLastIndex(cx);
     } else {
         RootedValue zero(cx, Int32Value(0));
         if (!SetProperty(cx, regexp, cx->names().lastIndex, zero))
             return false;
     }
 
new file mode 100755
--- /dev/null
+++ b/js/src/devtools/octane-csv.sh
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+
+set -e -o pipefail
+
+function echo_to_stderr {
+    echo "$1" 1>&2
+}
+
+function usage_and_exit {
+    echo_to_stderr "Usage:"
+    echo_to_stderr "    $0 <path-to-js> <number-of-iterations>"
+    echo_to_stderr
+    echo_to_stderr "Run octane <number-of-iterations> times, and aggregate the results"
+    echo_to_stderr "into one CSV file, which is written to stdout."
+    echo_to_stderr
+    echo_to_stderr "See the js/src/devtools/plot-octane.R script for plotting the"
+    echo_to_stderr "results."
+    echo_to_stderr
+    echo_to_stderr "Complete example usage with plotting:"
+    echo_to_stderr
+    echo_to_stderr "    \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > control.csv"
+    echo_to_stderr
+    echo_to_stderr "    Next, apply some patch you'd like to test."
+    echo_to_stderr
+    echo_to_stderr "    \$ ./js/src/devtools/octane-csv.sh path/to/js 20 > variable.csv"
+    echo_to_stderr "    \$ ./js/src/devtools/plot-octane.R control.csv variable.csv"
+    echo_to_stderr
+    echo_to_stderr "    Open Rplots.pdf to view the results."
+    exit 1
+}
+
+if [[ "$#" != "2" ]]; then
+    usage_and_exit
+fi
+
+# Get the absolute, normalized $JS path, and ensure its an executable.
+
+JS_DIR=$(dirname $1)
+if [[ ! -d "$JS_DIR" ]]; then
+    echo_to_stderr "error: no such directory $JS_DIR"
+    echo_to_stderr
+    usage_and_exit
+fi
+
+JS=$(basename $1)
+cd "$JS_DIR" > /dev/null
+JS="$(pwd)/$JS"
+if [[ ! -e "$JS" ]]; then
+    echo_to_stderr "error: '$JS' is not executable"
+    echo_to_stderr
+    usage_and_exit
+fi
+cd - > /dev/null
+
+# Go to the js/src/octane directory.
+
+cd $(dirname $0)/../octane > /dev/null
+
+# Run octane and transform the results into CSV.
+#
+# Run once as a warm up, and to grab the column headers. Then run the benchmark
+# $ITERS times, grabbing just the data rows.
+
+echo_to_stderr "Warm up"
+"$JS" ./run.js | grep -v -- "----" | cut -f 1 -d ':' | tr '\n' ','
+echo
+
+ITERS=$2
+while [[ "$ITERS" -ge "1" ]]; do
+    echo_to_stderr "Iterations left: $ITERS"
+    "$JS" ./run.js | grep -v -- "----" | cut -f 2 -d ':' | tr '\n' ','
+    echo
+    ITERS=$((ITERS - 1))
+done
+
+echo_to_stderr "All done :)"
new file mode 100755
--- /dev/null
+++ b/js/src/devtools/plot-octane.R
@@ -0,0 +1,38 @@
+#!/usr/bin/env Rscript
+
+# Usage:
+#
+#     octane.R control.csv variable.csv
+#
+# Output will be placed in Rplots.pdf
+#
+# Remember: on Octane, higher is better!
+
+library(ggplot2)
+
+args <- commandArgs(trailingOnly = TRUE)
+
+# Reading in data.
+control <- read.table(args[1], sep=",", header=TRUE)
+variable <- read.table(args[2], sep=",", header=TRUE)
+
+# Pulling out columns that we want to plot.
+# Not totally necessary.
+ctrl <- control$Score..version.9.
+var <- variable$Score..version.9.
+
+# Concatenating the values we want to plot.
+score <- c(ctrl, var)
+# Creating a vector of labels for the data points.
+label <- c(rep("control", length(ctrl)), rep("variable", length(var)))
+
+# Creating a data frame of the score and label.
+data <- data.frame(label, score)
+
+# Now plotting!
+ggplot(data, aes(label, score, color=label, pch=label)) +
+  # Adding boxplot without the outliers.
+  geom_boxplot(outlier.shape=NA) +
+  # Adding jitter plot on top of the boxplot. If you want to spread the points
+  # more, increase jitter.
+  geom_jitter(position=position_jitter(width=0.05)) 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -840,17 +840,22 @@ Parser<ParseHandler>::reportBadReturn(No
  */
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkStrictBinding(PropertyName* name, TokenPos pos)
 {
     if (!pc->sc()->needStrictChecks())
         return true;
 
-    if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
+    if (name == context->names().eval ||
+        name == context->names().arguments ||
+        name == context->names().let ||
+        name == context->names().static_ ||
+        IsKeyword(name))
+    {
         JSAutoByteString bytes;
         if (!AtomToPrintableString(context, name, &bytes))
             return false;
         return reportWithOffset(ParseStrictError, pc->sc()->strict(), pos.begin,
                                 JSMSG_BAD_BINDING, bytes.ptr());
     }
 
     return true;
@@ -2634,60 +2639,50 @@ Parser<ParseHandler>::functionArguments(
                     return false;
 
                 if (!noteDestructuredPositionalFormalParameter(funcpn, destruct))
                     return false;
 
                 break;
               }
 
-              case TOK_YIELD:
-                if (!checkYieldNameValidity())
-                    return false;
-                MOZ_ASSERT(yieldHandling == YieldIsName);
-                goto TOK_NAME;
-
-              case TOK_TRIPLEDOT: {
+              case TOK_TRIPLEDOT:
                 if (IsSetterKind(kind)) {
                     report(ParseError, false, null(),
                            JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
                     return false;
                 }
 
                 hasRest = true;
                 funbox->function()->setHasRest();
 
-                if (!tokenStream.getToken(&tt))
-                    return false;
-                // FIXME: This fails to handle a rest parameter named |yield|
-                //        correctly outside of generators: that is,
-                //        |var f = (...yield) => 42;| should be valid code!
-                //        When this is fixed, make sure to consult both
-                //        |yieldHandling| and |checkYieldNameValidity| for
-                //        correctness until legacy generator syntax is removed.
-                if (tt != TOK_NAME) {
-                    report(ParseError, false, null(), JSMSG_NO_REST_NAME);
-                    return false;
-                }
                 disallowDuplicateParams = true;
                 if (duplicatedParam) {
                     // Has duplicated args before the rest parameter.
                     report(ParseError, false, null(), JSMSG_BAD_DUP_ARGS);
                     return false;
                 }
 
-                goto TOK_NAME;
-              }
-
-              TOK_NAME:
-              case TOK_NAME: {
+                if (!tokenStream.getToken(&tt))
+                    return false;
+                if (tt != TOK_NAME && tt != TOK_YIELD) {
+                    report(ParseError, false, null(), JSMSG_NO_REST_NAME);
+                    return false;
+                }
+                MOZ_FALLTHROUGH;
+
+              case TOK_NAME:
+              case TOK_YIELD: {
                 if (parenFreeArrow)
                     funbox->setStart(tokenStream);
 
-                RootedPropertyName name(context, tokenStream.currentName());
+                RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+                if (!name)
+                    return false;
+
                 if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
                                                    &duplicatedParam))
                 {
                     return false;
                 }
 
                 break;
               }
@@ -3337,29 +3332,16 @@ Parser<ParseHandler>::functionFormalPara
     handler.setEndPosition(body, pos().begin);
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
 
 template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkYieldNameValidity()
-{
-    // In star generators and in JS >= 1.7, yield is a keyword.  Otherwise in
-    // strict mode, yield is a future reserved word.
-    if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc()->strict()) {
-        report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
-        return false;
-    }
-    return true;
-}
-
-template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionStmt(YieldHandling yieldHandling, DefaultHandling defaultHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     // Annex B.3.4 says we can parse function declarations unbraced under if
     // or else as if it were braced. That is, |if (x) function f() {}| is
     // parsed as |if (x) { function f() {} }|.
@@ -3382,22 +3364,20 @@ Parser<ParseHandler>::functionStmt(Yield
         return null();
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
-    if (tt == TOK_NAME) {
-        name = tokenStream.currentName();
-    } else if (tt == TOK_YIELD) {
-        if (!checkYieldNameValidity())
-            return null();
-        name = tokenStream.currentName();
+    if (tt == TOK_NAME || tt == TOK_YIELD) {
+        name = bindingIdentifier(yieldHandling);
+        if (!name)
+            return null();
     } else if (defaultHandling == AllowDefaultName) {
         name = context->names().starDefaultStar;
         tokenStream.ungetToken();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
@@ -3431,22 +3411,20 @@ Parser<ParseHandler>::functionExpr(Invok
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
         if (!tokenStream.getToken(&tt))
             return null();
     }
 
     RootedPropertyName name(context);
-    if (tt == TOK_NAME) {
-        name = tokenStream.currentName();
-    } else if (tt == TOK_YIELD) {
-        if (!checkYieldNameValidity())
-            return null();
-        name = tokenStream.currentName();
+    if (tt == TOK_NAME || tt == TOK_YIELD) {
+        name = bindingIdentifier(YieldIsName);
+        if (!name)
+            return null();
     } else {
         tokenStream.ungetToken();
     }
 
     YieldHandling yieldHandling = generatorKind != NotGenerator ? YieldIsKeyword : YieldIsName;
     return functionDefinition(InAllowed, yieldHandling, name, Expression, generatorKind, invoked);
 }
 
@@ -3683,29 +3661,22 @@ Parser<ParseHandler>::condition(InHandli
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label)
 {
     TokenKind tt = TOK_EOF;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
 
-    if (tt == TOK_NAME) {
-        tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
-        MOZ_ASSERT_IF(tokenStream.currentName() == context->names().yield,
-                      yieldHandling == YieldIsName);
-        label.set(tokenStream.currentName());
-    } else if (tt == TOK_YIELD) {
-        // We might still consider |yield| to be valid here, contrary to ES6.
-        // Fix bug 1104014, then stop shipping legacy generators in chrome
-        // code, then remove this check!
-        tokenStream.consumeKnownToken(TOK_YIELD, TokenStream::Operand);
-        if (!checkYieldNameValidity())
+    if (tt == TOK_NAME || tt == TOK_YIELD) {
+        tokenStream.consumeKnownToken(tt, TokenStream::Operand);
+
+        label.set(labelIdentifier(yieldHandling));
+        if (!label)
             return false;
-        label.set(tokenStream.currentName());
     } else {
         label.set(nullptr);
     }
     return true;
 }
 
 template <typename ParseHandler>
 Parser<ParseHandler>::PossibleError::PossibleError(Parser<ParseHandler>& parser)
@@ -4195,29 +4166,26 @@ Parser<ParseHandler>::initializerInNameD
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::declarationName(Node decl, DeclarationKind declKind, TokenKind tt,
                                       bool initialDeclaration, YieldHandling yieldHandling,
                                       ParseNodeKind* forHeadKind, Node* forInOrOfExpression)
 {
-    if (tt != TOK_NAME) {
-        // Anything other than TOK_YIELD or TOK_NAME is an error.
-        if (tt != TOK_YIELD) {
-            report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
-            return null();
-        }
-
-        // TOK_YIELD is only okay if it's treated as a name.
-        if (!checkYieldNameValidity())
-            return null();
-    }
-
-    RootedPropertyName name(context, tokenStream.currentName());
+    // Anything other than TOK_YIELD or TOK_NAME is an error.
+    if (tt != TOK_NAME && tt != TOK_YIELD) {
+        report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+        return null();
+    }
+
+    RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+    if (!name)
+        return null();
+
     Node binding = newName(name);
     if (!binding)
         return null();
 
     TokenPos namePos = pos();
 
     // The '=' context after a variable name in a declaration is an opportunity
     // for ASI, and thus for the next token to start an ExpressionStatement:
@@ -4380,48 +4348,68 @@ Parser<FullParseHandler>::namedImportsOr
 
             if (tt == TOK_RC)
                 break;
 
             // If the next token is a keyword, the previous call to
             // peekToken matched it as a TOK_NAME, and put it in the
             // lookahead buffer, so this call will match keywords as well.
             MUST_MATCH_TOKEN_MOD(TOK_NAME, TokenStream::KeywordIsName, JSMSG_NO_IMPORT_NAME);
-            Node importName = newName(tokenStream.currentName());
-            if (!importName)
-                return false;
-
-            bool foundAs;
-            if (!tokenStream.matchContextualKeyword(&foundAs, context->names().as))
-                return false;
-
-            if (foundAs) {
-                MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
+            Rooted<PropertyName*> importName(context, tokenStream.currentName());
+            TokenPos importNamePos = pos();
+
+            TokenKind maybeAs;
+            if (!tokenStream.peekToken(&maybeAs))
+                return null();
+
+            if (maybeAs == TOK_NAME &&
+                tokenStream.nextName() == context->names().as)
+            {
+                tokenStream.consumeKnownToken(TOK_NAME);
+
+                if (!checkUnescapedName())
+                    return false;
+
+                TokenKind afterAs;
+                if (!tokenStream.getToken(&afterAs))
+                    return false;
+
+                if (afterAs != TOK_NAME && afterAs != TOK_YIELD) {
+                    report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
+                    return false;
+                }
             } else {
                 // Keywords cannot be bound to themselves, so an import name
                 // that is a keyword is a syntax error if it is not followed
                 // by the keyword 'as'.
                 // See the ImportSpecifier production in ES6 section 15.2.2.
-                if (IsKeyword(importName->name())) {
+                if (IsKeyword(importName)) {
                     JSAutoByteString bytes;
-                    if (!AtomToPrintableString(context, importName->name(), &bytes))
+                    if (!AtomToPrintableString(context, importName, &bytes))
                         return false;
                     report(ParseError, false, null(), JSMSG_AS_AFTER_RESERVED_WORD, bytes.ptr());
                     return false;
                 }
             }
 
-            RootedPropertyName bindingAtom(context, tokenStream.currentName());
+            RootedPropertyName bindingAtom(context, importedBinding());
+            if (!bindingAtom)
+                return false;
+
             Node bindingName = newName(bindingAtom);
             if (!bindingName)
                 return false;
             if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
                 return false;
 
-            Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
+            Node importNameNode = newName(importName, importNamePos);
+            if (!importNameNode)
+                return false;
+
+            Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importNameNode, bindingName);
             if (!importSpec)
                 return false;
 
             handler.addList(importSpecSet, importSpec);
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_COMMA))
                 return false;
@@ -4451,17 +4439,19 @@ Parser<FullParseHandler>::namedImportsOr
         Node importName = newName(context->names().star);
         if (!importName)
             return false;
 
         // Namespace imports are are not indirect bindings but lexical
         // definitions that hold a module namespace object. They are treated
         // as const variables which are initialized during the
         // ModuleDeclarationInstantiation step.
-        RootedPropertyName bindingName(context, tokenStream.currentName());
+        RootedPropertyName bindingName(context, importedBinding());
+        if (!bindingName)
+            return false;
         Node bindingNameNode = newName(bindingName);
         if (!bindingNameNode)
             return false;
         if (!noteDeclaredName(bindingName, DeclarationKind::Const, pos()))
             return false;
 
         // The namespace import name is currently required to live on the
         // environment.
@@ -4510,20 +4500,24 @@ Parser<FullParseHandler>::importDeclarat
             // Handle the form |import a from 'b'|, by adding a single import
             // specifier to the list, with 'default' as the import name and
             // 'a' as the binding name. This is equivalent to
             // |import { default as a } from 'b'|.
             Node importName = newName(context->names().default_);
             if (!importName)
                 return null();
 
-            RootedPropertyName bindingAtom(context, tokenStream.currentName());
+            RootedPropertyName bindingAtom(context, importedBinding());
+            if (!bindingAtom)
+                return null();
+
             Node bindingName = newName(bindingAtom);
             if (!bindingName)
                 return null();
+
             if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos()))
                 return null();
 
             Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
             if (!importSpec)
                 return null();
 
             handler.addList(importSpecSet, importSpec);
@@ -4865,25 +4859,38 @@ Parser<FullParseHandler>::exportDeclarat
         ParseNode* node = handler.newExportDefaultDeclaration(kid, nameNode,
                                                               TokenPos(begin, pos().end));
         if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
             return null();
 
         return node;
       }
 
-      case TOK_LET:
       case TOK_CONST:
-        kid = lexicalDeclaration(YieldIsName, tt == TOK_CONST);
+        kid = lexicalDeclaration(YieldIsName, /* isConst = */ true);
         if (!kid)
             return null();
         if (!checkExportedNamesForDeclaration(kid))
             return null();
         break;
 
+      case TOK_NAME:
+        if (tokenStream.currentName() == context->names().let) {
+            if (!checkUnescapedName())
+                return null();
+
+            kid = lexicalDeclaration(YieldIsName, /* isConst = */ false);
+            if (!kid)
+                return null();
+            if (!checkExportedNamesForDeclaration(kid))
+                return null();
+            break;
+        }
+        MOZ_FALLTHROUGH;
+
       default:
         report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_EXPORT);
         return null();
     }
 
     ParseNode* node = handler.newExportDeclaration(kid, TokenPos(begin, pos().end));
     if (!node || !pc->sc()->asModuleContext()->builder.processExport(node))
         return null();
@@ -5119,26 +5126,25 @@ Parser<ParseHandler>::forHeadStart(Yield
         return *forInitialPart != null();
     }
 
     // Otherwise we have a lexical declaration or an expression.
 
     // For-in loop backwards compatibility requires that |let| starting a
     // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code,
     // parse as an identifier.  (|let| in for-of is always a declaration.)
-    // Thus we must can't just sniff out TOK_CONST/TOK_LET here.  :-(
     bool parsingLexicalDeclaration = false;
     bool letIsIdentifier = false;
-    if (tt == TOK_LET || tt == TOK_CONST) {
+    if (tt == TOK_CONST) {
         parsingLexicalDeclaration = true;
         tokenStream.consumeKnownToken(tt, TokenStream::Operand);
-    } else if (tt == TOK_NAME && tokenStream.nextName() == context->names().let) {
-        MOZ_ASSERT(!pc->sc()->strict(),
-                   "should parse |let| as TOK_LET in strict mode code");
-
+    } else if (tt == TOK_NAME &&
+               tokenStream.nextName() == context->names().let &&
+               !tokenStream.nextNameContainsEscape())
+    {
         // We could have a {For,Lexical}Declaration, or we could have a
         // LeftHandSideExpression with lookahead restrictions so it's not
         // ambiguous with the former.  Check for a continuation of the former
         // to decide which we have.
         tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
 
         TokenKind next;
         if (!tokenStream.peekToken(&next))
@@ -5870,28 +5876,31 @@ Parser<ParseHandler>::labeledItem(YieldH
     tokenStream.ungetToken();
     return statement(yieldHandling);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::labeledStatement(YieldHandling yieldHandling)
 {
-    uint32_t begin = pos().begin;
-    RootedPropertyName label(context, tokenStream.currentName());
+    RootedPropertyName label(context, labelIdentifier(yieldHandling));
+    if (!label)
+        return null();
 
     auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) {
         return stmt->label() == label;
     };
 
     if (pc->findInnermostStatement<ParseContext::LabelStatement>(hasSameLabel)) {
         report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
         return null();
     }
 
+    uint32_t begin = pos().begin;
+
     tokenStream.consumeKnownToken(TOK_COLON);
 
     /* Push a label struct and parse the statement. */
     ParseContext::LabelStatement stmt(pc, label);
     Node pn = labeledItem(yieldHandling);
     if (!pn)
         return null();
 
@@ -6016,29 +6025,22 @@ Parser<ParseHandler>::tryStatement(Yield
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
                 catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
                                                      yieldHandling, tt);
                 if (!catchName)
                     return null();
                 break;
-              case TOK_YIELD:
-                if (yieldHandling == YieldIsKeyword) {
-                    report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
+
+              case TOK_NAME:
+              case TOK_YIELD: {
+                RootedPropertyName param(context, bindingIdentifier(yieldHandling));
+                if (!param)
                     return null();
-                }
-
-                // Even if yield is *not* necessarily a keyword, we still must
-                // check its validity for legacy generators.
-                if (!checkYieldNameValidity())
-                    return null();
-                MOZ_FALLTHROUGH;
-              case TOK_NAME: {
-                RootedPropertyName param(context, tokenStream.currentName());
                 catchName = newName(param);
                 if (!catchName)
                     return null();
                 if (!noteDeclaredName(param, DeclarationKind::SimpleCatchParameter, pos()))
                     return null();
                 break;
               }
 
@@ -6228,23 +6230,20 @@ Parser<ParseHandler>::classDefinition(Yi
 
     bool savedStrictness = setLocalStrictMode(true);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
     RootedPropertyName name(context);
-    if (tt == TOK_NAME) {
-        name = tokenStream.currentName();
-    } else if (tt == TOK_YIELD) {
-        if (!checkYieldNameValidity())
-            return null();
-        MOZ_ASSERT(yieldHandling != YieldIsKeyword);
-        name = tokenStream.currentName();
+    if (tt == TOK_NAME || tt == TOK_YIELD) {
+        name = bindingIdentifier(yieldHandling);
+        if (!name)
+            return null();
     } else if (classContext == ClassStatement) {
         if (defaultHandling == AllowDefaultName) {
             name = context->names().starDefaultStar;
             tokenStream.ungetToken();
         } else {
             // Class statements must have a bound name
             report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT);
             return null();
@@ -6303,16 +6302,23 @@ Parser<ParseHandler>::classDefinition(Yi
         if (tt == TOK_RC)
             break;
 
         if (tt == TOK_SEMI)
             continue;
 
         bool isStatic = false;
         if (tt == TOK_NAME && tokenStream.currentName() == context->names().static_) {
+            MOZ_ASSERT(pc->sc()->strict(), "classes are always strict");
+
+            // Strict mode forbids "class" as Identifier, so it can only be the
+            // unescaped keyword.
+            if (!checkUnescapedName())
+                return null();
+
             if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
                 return null();
             if (tt == TOK_RC) {
                 tokenStream.consumeKnownToken(tt, TokenStream::KeywordIsName);
                 report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                        "property name", TokenKindToDesc(tt));
                 return null();
             }
@@ -6433,60 +6439,61 @@ Parser<ParseHandler>::classDefinition(Yi
 
     return handler.newClass(nameNode, classHeritage, methodsOrBlock);
 }
 
 template <class ParseHandler>
 bool
 Parser<ParseHandler>::nextTokenContinuesLetDeclaration(TokenKind next, YieldHandling yieldHandling)
 {
-    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME),
-               "TOK_LET should have been summarily considered a "
-               "LexicalDeclaration");
+    MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
     MOZ_ASSERT(tokenStream.currentName() == context->names().let);
+    MOZ_ASSERT(!tokenStream.currentToken().nameContainsEscape());
 
 #ifdef DEBUG
     TokenKind verify;
     MOZ_ALWAYS_TRUE(tokenStream.peekToken(&verify));
     MOZ_ASSERT(next == verify);
 #endif
 
     // Destructuring is (for once) the easy case.
     if (next == TOK_LB || next == TOK_LC)
         return true;
 
     // Otherwise a let declaration must have a name.
     if (next == TOK_NAME) {
+        MOZ_ASSERT(tokenStream.nextName() != context->names().yield,
+                   "token stream should interpret 'yield' as TOK_YIELD");
+
         // One non-"yield" TOK_NAME edge case deserves special comment.
         // Consider this:
         //
         //   let     // not an ASI opportunity
         //   let;
         //
         // Static semantics in ยง13.3.1.1 turn a LexicalDeclaration that binds
         // "let" into an early error.  Does this retroactively permit ASI so
         // that we should parse this as two ExpressionStatements?   No.  ASI
         // resolves during parsing.  Static semantics only apply to the full
         // parse tree with ASI applied.  No backsies!
-        if (tokenStream.nextName() != context->names().yield)
-            return true;
-    } else if (next != TOK_YIELD) {
-        return false;
-    }
-
-    // We have the name "yield": the grammar parameter exactly states whether
-    // this is okay.  Even if YieldIsKeyword, the code might be valid if ASI
-    // induces a preceding semicolon.  If YieldIsName, the code is valid
-    // outside strict mode, and declaration-parsing code will enforce strict
-    // mode restrictions.
-    //
-    // No checkYieldNameValidity for TOK_YIELD is needed here.  It'll happen
-    // when TOK_YIELD is consumed as BindingIdentifier or as start of a fresh
-    // Statement.
-    return yieldHandling == YieldIsName;
+        return true;
+    }
+
+    // If we have the name "yield", the grammar parameter exactly states
+    // whether this is okay.  (This wasn't true for SpiderMonkey's ancient
+    // legacy generator syntax, but that's dead now.)  If YieldIsName,
+    // declaration-parsing code will (if necessary) enforce a strict mode
+    // restriction on defining "yield".  If YieldIsKeyword, consider this the
+    // end of the declaration, in case ASI induces a semicolon that makes the
+    // "yield" valid.
+    if (next == TOK_YIELD)
+        return yieldHandling == YieldIsName;
+
+    // Otherwise not a let declaration.
+    return false;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::variableStatement(YieldHandling yieldHandling)
 {
     Node vars = declarationList(yieldHandling, PNK_VAR);
     if (!vars)
@@ -6530,48 +6537,43 @@ Parser<ParseHandler>::statement(YieldHan
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
-        if (next == TOK_COLON) {
-            if (!checkYieldNameValidity())
-                return null();
+
+        if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
-        }
+
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NAME: {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
-#ifdef DEBUG
-        if (tokenStream.currentName() == context->names().let) {
-            MOZ_ASSERT(!pc->sc()->strict(),
-                       "observing |let| as TOK_NAME and not TOK_LET implies "
-                       "non-strict code (and the edge case of 'use strict' "
-                       "immediately followed by |let| on a new line only "
-                       "applies to StatementListItems, not to Statements)");
-        }
-#endif
-
-        // Statement context forbids LexicalDeclaration.
-        if ((next == TOK_LB || next == TOK_LC || next == TOK_NAME) &&
+        // |let| here can only be an Identifier, not a declaration.  Give nicer
+        // errors for declaration-looking typos.
+        if (!tokenStream.currentToken().nameContainsEscape() &&
             tokenStream.currentName() == context->names().let)
         {
             bool forbiddenLetDeclaration = false;
-            if (next == TOK_LB) {
-                // ExpressionStatement has a 'let [' lookahead restriction.
+
+            if (pc->sc()->strict() || versionNumber() >= JSVERSION_1_7) {
+                // |let| can't be an Identifier in strict mode code.  Ditto for
+                // non-standard JavaScript 1.7+.
                 forbiddenLetDeclaration = true;
-            } else {
+            } else if (next == TOK_LB) {
+                // Enforce ExpressionStatement's 'let [' lookahead restriction.
+                forbiddenLetDeclaration = true;
+            } else if (next == TOK_LC || next == TOK_NAME) {
                 // 'let {' and 'let foo' aren't completely forbidden, if ASI
                 // causes 'let' to be the entire Statement.  But if they're
                 // same-line, we can aggressively give a better error message.
                 //
                 // Note that this ignores 'yield' as TOK_YIELD: we'll handle it
                 // correctly but with a worse error message.
                 TokenKind nextSameLine;
                 if (!tokenStream.peekTokenSameLine(&nextSameLine))
@@ -6689,24 +6691,16 @@ Parser<ParseHandler>::statement(YieldHan
       case TOK_CATCH:
         report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
         return null();
 
       case TOK_FINALLY:
         report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
-      // TOK_LET implies we're in strict mode code where static semantics
-      // forbid IdentifierName to be "let": a stronger restriction than
-      // Statement's lookahead restriction on |let [|.  Provide a better error
-      // message here than the default case would.
-      case TOK_LET:
-        report(ParseError, false, null(), JSMSG_FORBIDDEN_AS_STATEMENT, "let declarations");
-        return null();
-
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statementListItem(YieldHandling yieldHandling,
                                         bool canHaveDirectives /* = false */)
@@ -6752,46 +6746,33 @@ Parser<ParseHandler>::statementListItem(
         if (yieldExpressionsSupported())
             modifier = TokenStream::Operand;
         else
             modifier = TokenStream::None;
 
         TokenKind next;
         if (!tokenStream.peekToken(&next, modifier))
             return null();
-        if (next == TOK_COLON) {
-            if (!checkYieldNameValidity())
-                return null();
+
+        if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
-        }
+
         return expressionStatement(yieldHandling);
       }
 
       case TOK_NAME: {
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
 
-        if (tokenStream.currentName() == context->names().let) {
-            if (nextTokenContinuesLetDeclaration(next, yieldHandling))
-                return lexicalDeclaration(yieldHandling, /* isConst = */ false);
-
-            // IdentifierName can't be "let" in strict mode code.  |let| in
-            // strict mode code is usually TOK_LET, but in this one weird case
-            // in global code it's TOK_NAME:
-            //
-            //   "use strict"              // ExpressionStatement ended by ASI
-            //   let <...whatever else...> // a fresh StatementListItem
-            //
-            // Carefully reject strict mode |let| non-declarations.
-            if (pc->sc()->strict()) {
-                report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
-                       "declaration pattern", TokenKindToDesc(next));
-                return null();
-            }
+        if (!tokenStream.currentToken().nameContainsEscape() &&
+            tokenStream.currentName() == context->names().let &&
+            nextTokenContinuesLetDeclaration(next, yieldHandling))
+        {
+            return lexicalDeclaration(yieldHandling, /* isConst = */ false);
         }
 
         if (next == TOK_COLON)
             return labeledStatement(yieldHandling);
 
         return expressionStatement(yieldHandling);
       }
 
@@ -6867,21 +6848,20 @@ Parser<ParseHandler>::statementListItem(
         return functionStmt(yieldHandling, NameRequired);
 
       //   ClassDeclaration[?Yield, ~Default]
       case TOK_CLASS:
         return classDefinition(yieldHandling, ClassStatement, NameRequired);
 
       //   LexicalDeclaration[In, ?Yield]
       //     LetOrConst BindingList[?In, ?Yield]
-      case TOK_LET:
       case TOK_CONST:
         // [In] is the default behavior, because for-loops specially parse
         // their heads to handle |in| in this situation.
-        return lexicalDeclaration(yieldHandling, /* isConst = */ tt == TOK_CONST);
+        return lexicalDeclaration(yieldHandling, /* isConst = */ true);
 
       // ImportDeclaration (only inside modules)
       case TOK_IMPORT:
         return importDeclaration();
 
       // ExportDeclaration (only inside modules)
       case TOK_EXPORT:
         return exportDeclaration();
@@ -7243,18 +7223,23 @@ Parser<ParseHandler>::assignExpr(InHandl
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
 
     bool endsExpr;
 
     if (tt == TOK_NAME) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
-        if (endsExpr)
-            return identifierName(yieldHandling);
+        if (endsExpr) {
+            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+            if (!name)
+                return null();
+
+            return identifierReference(name);
+        }
     }
 
     if (tt == TOK_NUMBER) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return newNumber(tokenStream.currentToken());
     }
@@ -8253,28 +8238,109 @@ Parser<ParseHandler>::newName(PropertyNa
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName* name, TokenPos pos)
 {
     return handler.newName(name, pos, context);
 }
 
 template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::labelOrIdentifierReference(YieldHandling yieldHandling)
+{
+    PropertyName* ident;
+    const Token& tok = tokenStream.currentToken();
+    if (tok.type == TOK_NAME) {
+        ident = tok.name();
+        MOZ_ASSERT(ident != context->names().yield,
+                   "tokenizer should have treated 'yield' as TOK_YIELD");
+
+        if (pc->sc()->strict()) {
+            const char* badName = ident == context->names().let
+                                  ? "let"
+                                  : ident == context->names().static_
+                                  ? "static"
+                                  : nullptr;
+            if (badName) {
+                report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
+                return nullptr;
+            }
+        }
+    } else {
+        MOZ_ASSERT(tok.type == TOK_YIELD);
+
+        if (yieldHandling == YieldIsKeyword ||
+            pc->sc()->strict() ||
+            pc->isStarGenerator() ||
+            versionNumber() >= JSVERSION_1_7)
+        {
+            report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
+            return nullptr;
+        }
+
+        ident = context->names().yield;
+    }
+
+    return ident;
+}
+
+template <typename ParseHandler>
+PropertyName*
+Parser<ParseHandler>::bindingIdentifier(YieldHandling yieldHandling)
+{
+    PropertyName* ident;
+    const Token& tok = tokenStream.currentToken();
+    if (tok.type == TOK_NAME) {
+        ident = tok.name();
+        MOZ_ASSERT(ident != context->names().yield,
+                   "tokenizer should have treated 'yield' as TOK_YIELD");
+
+        if (pc->sc()->strict()) {
+            const char* badName = ident == context->names().arguments
+                                  ? "arguments"
+                                  : ident == context->names().eval
+                                  ? "eval"
+                                  : nullptr;
+            if (badName) {
+                report(ParseError, false, null(), JSMSG_BAD_STRICT_ASSIGN, badName);
+                return nullptr;
+            }
+
+            badName = ident == context->names().let
+                      ? "let"
+                      : ident == context->names().static_
+                      ? "static"
+                      : nullptr;
+            if (badName) {
+                report(ParseError, false, null(), JSMSG_RESERVED_ID, badName);
+                return nullptr;
+            }
+        }
+    } else {
+        MOZ_ASSERT(tok.type == TOK_YIELD);
+
+        if (yieldHandling == YieldIsKeyword ||
+            pc->sc()->strict() ||
+            pc->isStarGenerator() ||
+            versionNumber() >= JSVERSION_1_7)
+        {
+            report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
+            return nullptr;
+        }
+
+        ident = context->names().yield;
+    }
+
+    return ident;
+}
+
+template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
-{
-    RootedPropertyName name(context, tokenStream.currentName());
-    if (yieldHandling == YieldIsKeyword && name == context->names().yield) {
-        report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
-        return null();
-    }
-
-    // If we're inside a function that later becomes a legacy generator, then
-    // a |yield| identifier name here will be detected by a subsequent
-    // |checkYieldNameValidity| call.
+Parser<ParseHandler>::identifierReference(Handle<PropertyName*> name)
+{
     Node pn = newName(name);
     if (!pn)
         return null();
 
     if (!pc->inDestructuringDecl && !noteUsedName(name))
         return null();
 
     return pn;
@@ -8665,40 +8731,48 @@ Parser<ParseHandler>::objectLiteral(Yiel
         } else if (propType == PropertyType::Shorthand) {
             /*
              * Support, e.g., |var {x, y} = o| as destructuring shorthand
              * for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
              */
             if (!tokenStream.checkForKeyword(propAtom, nullptr))
                 return null();
 
-            Node nameExpr = identifierName(yieldHandling);
+            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+            if (!name)
+                return null();
+
+            Node nameExpr = identifierReference(name);
             if (!nameExpr)
                 return null();
 
             if (!handler.addShorthand(literal, propName, nameExpr))
                 return null();
         } else if (propType == PropertyType::CoverInitializedName) {
             /*
              * Support, e.g., |var {x=1, y=2} = o| as destructuring shorthand
              * with default values, as per ES6 12.14.5 (2016/2/4)
              */
             if (!tokenStream.checkForKeyword(propAtom, nullptr))
                 return null();
 
-            Node lhs = identifierName(yieldHandling);
+            Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+            if (!name)
+                return null();
+
+            Node lhs = identifierReference(name);
             if (!lhs)
                 return null();
 
             tokenStream.consumeKnownToken(TOK_ASSIGN);
 
             Node rhs;
             {
                 // Clearing `inDestructuringDecl` allows name use to be noted
-                // in `identifierName`. See bug 1255167.
+                // in Parser::identifierReference. See bug 1255167.
                 AutoClearInDestructuringDecl autoClear(pc);
                 rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!rhs)
                     return null();
             }
 
             Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP);
             if (!propExpr)
@@ -8895,21 +8969,23 @@ Parser<ParseHandler>::primaryExpr(YieldH
 
       case TOK_NO_SUBS_TEMPLATE:
         return noSubstitutionTemplate();
 
       case TOK_STRING:
         return stringLiteral();
 
       case TOK_YIELD:
-        if (!checkYieldNameValidity())
-            return null();
-        MOZ_FALLTHROUGH;
-      case TOK_NAME:
-        return identifierName(yieldHandling);
+      case TOK_NAME: {
+        Rooted<PropertyName*> name(context, identifierReference(yieldHandling));
+        if (!name)
+            return null();
+
+        return identifierReference(name);
+      }
 
       case TOK_REGEXP:
         return newRegExp();
 
       case TOK_NUMBER:
         return newNumber(tokenStream.currentToken());
 
       case TOK_TRUE:
@@ -8941,22 +9017,21 @@ Parser<ParseHandler>::primaryExpr(YieldH
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "expression", TokenKindToDesc(tt));
             return null();
         }
 
         TokenKind next;
         if (!tokenStream.getToken(&next))
             return null();
-        // FIXME: This fails to handle a rest parameter named |yield| correctly
-        //        outside of generators: |var f = (...yield) => 42;| should be
-        //        valid code!  When this is fixed, make sure to consult both
-        //        |yieldHandling| and |checkYieldNameValidity| for correctness
-        //        until legacy generator syntax is removed.
-        if (next != TOK_NAME) {
+
+        // This doesn't check that the provided name is allowed, e.g. if the
+        // enclosing code is strict mode code, any of "arguments", "let", or
+        // "yield" should be prohibited.  Argument-parsing code handles that.
+        if (next != TOK_NAME && next != TOK_YIELD) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "rest argument name", TokenKindToDesc(next));
             return null();
         }
 
         if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_RP) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -976,21 +976,25 @@ class Parser final : private JS::AutoGCR
     // Parse a function's formal parameters and its body assuming its function
     // ParseContext is already on the stack.
     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
                                          Node pn, FunctionSyntaxKind kind);
 
     // Determine whether |yield| is a valid name in the current context, or
     // whether it's prohibited due to strictness, JS version, or occurrence
     // inside a star generator.
-    bool checkYieldNameValidity();
     bool yieldExpressionsSupported() {
         return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
     }
 
+    // Match the current token against the BindingIdentifier production with
+    // the given Yield parameter.  If there is no match, report a syntax
+    // error.
+    PropertyName* bindingIdentifier(YieldHandling yieldHandling);
+
     virtual bool strictMode() { return pc->sc()->strict(); }
     bool setLocalStrictMode(bool strict) {
         MOZ_ASSERT(tokenStream.debugHasNoLookahead());
         return pc->sc()->setLocalStrictMode(strict);
     }
 
     const ReadOnlyCompileOptions& options() const {
         return tokenStream.options();
@@ -1201,17 +1205,31 @@ class Parser final : private JS::AutoGCR
     bool namedImportsOrNamespaceImport(TokenKind tt, Node importSpecSet);
     bool checkExportedName(JSAtom* exportName);
     bool checkExportedNamesForDeclaration(Node node);
 
     enum ClassContext { ClassStatement, ClassExpression };
     Node classDefinition(YieldHandling yieldHandling, ClassContext classContext,
                          DefaultHandling defaultHandling);
 
-    Node identifierName(YieldHandling yieldHandling);
+    PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling);
+
+    PropertyName* labelIdentifier(YieldHandling yieldHandling) {
+        return labelOrIdentifierReference(yieldHandling);
+    }
+
+    PropertyName* identifierReference(YieldHandling yieldHandling) {
+        return labelOrIdentifierReference(yieldHandling);
+    }
+
+    PropertyName* importedBinding() {
+        return bindingIdentifier(YieldIsName);
+    }
+
+    Node identifierReference(Handle<PropertyName*> name);
 
     bool matchLabel(YieldHandling yieldHandling, MutableHandle<PropertyName*> label);
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return versionNumber() >= JSVERSION_1_6;
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -102,17 +102,16 @@
     macro(NEW,          "keyword 'new'") \
     macro(DELETE,       "keyword 'delete'") \
     macro(TRY,          "keyword 'try'") \
     macro(CATCH,        "keyword 'catch'") \
     macro(FINALLY,      "keyword 'finally'") \
     macro(THROW,        "keyword 'throw'") \
     macro(DEBUGGER,     "keyword 'debugger'") \
     macro(YIELD,        "keyword 'yield'") \
-    macro(LET,          "keyword 'let'") \
     macro(EXPORT,       "keyword 'export'") \
     macro(IMPORT,       "keyword 'import'") \
     macro(CLASS,        "keyword 'class'") \
     macro(EXTENDS,      "keyword 'extends'") \
     macro(SUPER,        "keyword 'super'") \
     macro(RESERVED,     "reserved keyword") \
     /* reserved keywords in strict mode */ \
     macro(STRICT_RESERVED, "reserved keyword") \
@@ -234,18 +233,12 @@ TokenKindIsShift(TokenKind tt)
 }
 
 inline bool
 TokenKindIsAssignment(TokenKind tt)
 {
     return TOK_ASSIGNMENT_START <= tt && tt <= TOK_ASSIGNMENT_LAST;
 }
 
-inline bool
-TokenKindIsDecl(TokenKind tt)
-{
-    return tt == TOK_VAR || tt == TOK_LET;
-}
-
 } // namespace frontend
 } // namespace js
 
 #endif /* frontend_TokenKind_h */
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -976,21 +976,16 @@ bool
 TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
 {
     if (kw->tokentype == TOK_RESERVED)
         return reportError(JSMSG_RESERVED_ID, kw->chars);
 
     if (kw->tokentype == TOK_STRICT_RESERVED)
         return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
 
-    // Treat 'let' as an identifier and contextually a keyword in sloppy mode.
-    // It is always a keyword in strict mode.
-    if (kw->tokentype == TOK_LET && !strictMode())
-        return true;
-
     // Working keyword.
     if (ttp) {
         *ttp = kw->tokentype;
         return true;
     }
 
     return reportError(JSMSG_RESERVED_ID, kw->chars);
 }
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -346,16 +346,23 @@ class MOZ_STACK_CLASS TokenStream
 
     PropertyName* nextName() const {
         if (nextToken().type == TOK_YIELD)
             return cx->names().yield;
         MOZ_ASSERT(nextToken().type == TOK_NAME);
         return nextToken().name();
     }
 
+    bool nextNameContainsEscape() const {
+        if (nextToken().type == TOK_YIELD)
+            return false;
+        MOZ_ASSERT(nextToken().type == TOK_NAME);
+        return nextToken().nameContainsEscape();
+    }
+
     bool isCurrentTokenAssignment() const {
         return TokenKindIsAssignment(currentToken().type);
     }
 
     // Flag methods.
     bool isEOF() const { return flags.isEOF; }
     bool sawOctalEscape() const { return flags.sawOctalEscape; }
     bool hadError() const { return flags.hadError; }
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2524,21 +2524,24 @@ js::TenuringTracer::moveObjectToTenured(
     if (src->is<InlineTypedObject>()) {
         InlineTypedObject::objectMovedDuringMinorGC(this, dst, src);
     } else if (src->is<TypedArrayObject>()) {
         tenuredSize += TypedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
     } else if (src->is<UnboxedArrayObject>()) {
         tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(this, dst, src, dstKind);
     } else if (src->is<ArgumentsObject>()) {
         tenuredSize += ArgumentsObject::objectMovedDuringMinorGC(this, dst, src);
+    } else if (src->is<ProxyObject>()) {
+        tenuredSize += ProxyObject::objectMovedDuringMinorGC(this, dst, src);
     } else if (JSObjectMovedOp op = dst->getClass()->extObjectMovedOp()) {
         op(dst, src);
-    } else if (src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE) {
-        // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
-        // to ensure any additional nursery buffers they hold are moved.
+    } else if (src->getClass()->hasFinalize()) {
+        // Such objects need to be handled specially above to ensure any
+        // additional nursery buffers they hold are moved.
+        MOZ_RELEASE_ASSERT(CanNurseryAllocateFinalizedClass(src->getClass()));
         MOZ_CRASH("Unhandled JSCLASS_SKIP_NURSERY_FINALIZE Class");
     }
 
     return tenuredSize;
 }
 
 size_t
 js::TenuringTracer::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
@@ -2800,40 +2803,16 @@ FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_HEAP_TRACE_FUNCTIONS)
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 } /* namespace gc */
 } /* namespace js */
 
 
-/*** Type Marking *********************************************************************************/
-
-void
-TypeSet::MarkTypeRoot(JSTracer* trc, TypeSet::Type* v, const char* name)
-{
-    AssertRootMarkingPhase(trc);
-    MarkTypeUnbarriered(trc, v, name);
-}
-
-void
-TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
-{
-    if (v->isSingletonUnchecked()) {
-        JSObject* obj = v->singletonNoBarrier();
-        DispatchToTracer(trc, &obj, name);
-        *v = TypeSet::ObjectType(obj);
-    } else if (v->isGroupUnchecked()) {
-        ObjectGroup* group = v->groupNoBarrier();
-        DispatchToTracer(trc, &group, name);
-        *v = TypeSet::ObjectType(group);
-    }
-}
-
-
 /*** Cycle Collector Barrier Implementation *******************************************************/
 
 #ifdef DEBUG
 struct AssertNonGrayTracer : public JS::CallbackTracer {
     explicit AssertNonGrayTracer(JSRuntime* rt) : JS::CallbackTracer(rt) {}
     void onChild(const JS::GCCellPtr& thing) override {
         MOZ_ASSERT_IF(thing.asCell()->isTenured(),
                       !thing.asCell()->asTenured().isMarked(js::gc::GRAY));
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -249,33 +249,29 @@ js::Nursery::leaveZealMode() {
 #endif // JS_GC_ZEAL
 
 JSObject*
 js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp)
 {
     /* Ensure there's enough space to replace the contents with a RelocationOverlay. */
     MOZ_ASSERT(size >= sizeof(RelocationOverlay));
 
-    /*
-     * Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer
-     * called if they are nursery allocated and not promoted to the tenured
-     * heap. The finalizers for these classes must do nothing except free data
-     * which was allocated via Nursery::allocateBuffer.
-     */
-    MOZ_ASSERT_IF(clasp->hasFinalize(), clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
+    /* Sanity check the finalizer. */
+    MOZ_ASSERT_IF(clasp->hasFinalize(), CanNurseryAllocateFinalizedClass(clasp) ||
+                                        clasp->isProxy());
 
     /* Make the object allocation. */
     JSObject* obj = static_cast<JSObject*>(allocate(size));
     if (!obj)
         return nullptr;
 
     /* If we want external slots, add them. */
     HeapSlot* slots = nullptr;
     if (numDynamic) {
-        MOZ_ASSERT(clasp->isNative());
+        MOZ_ASSERT(clasp->isNative() || clasp->isProxy());
         slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot)));
         if (!slots) {
             /*
              * It is safe to leave the allocated object uninitialized, since we
              * do not visit unallocated things in the nursery.
              */
             return nullptr;
         }
@@ -695,17 +691,17 @@ js::Nursery::doCollection(JSRuntime* rt,
     // objects are left to move. That is, we iterate to a fixed point.
     maybeStartProfile(ProfileKey::CollectToFP);
     collectToFixedPoint(mover, tenureCounts);
     maybeEndProfile(ProfileKey::CollectToFP);
 
     // Sweep compartments to update the array buffer object's view lists.
     maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
-        c->sweepAfterMinorGC();
+        c->sweepAfterMinorGC(&mover);
     maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     maybeStartProfile(ProfileKey::UpdateJitActivations);
     js::jit::UpdateJitActivationsForMinorGC(rt, &mover);
     forwardedBuffers.finish();
     maybeEndProfile(ProfileKey::UpdateJitActivations);
 
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -107,16 +107,30 @@ class TenuringTracer : public JSTracer
     JSObject* moveToTenured(JSObject* src);
     size_t moveObjectToTenured(JSObject* dst, JSObject* src, gc::AllocKind dstKind);
     size_t moveElementsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind);
     size_t moveSlotsToTenured(NativeObject* dst, NativeObject* src, gc::AllocKind dstKind);
 
     void traceSlots(JS::Value* vp, JS::Value* end);
 };
 
+/*
+ * Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
+ * CROSS_COMPARTMENT flags will not have their finalizer called if they are
+ * nursery allocated and not promoted to the tenured heap. The finalizers for
+ * these classes must do nothing except free data which was allocated via
+ * Nursery::allocateBuffer.
+ */
+inline bool
+CanNurseryAllocateFinalizedClass(const js::Class* const clasp)
+{
+    MOZ_ASSERT(clasp->hasFinalize());
+    return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
+}
+
 class Nursery
 {
   public:
     static const size_t Alignment = gc::ChunkSize;
     static const size_t ChunkShift = gc::ChunkShift;
 
     explicit Nursery(JSRuntime* rt);
     ~Nursery();
new file mode 100644
--- /dev/null
+++ b/js/src/gc/NurseryAwareHashMap.h
@@ -0,0 +1,178 @@
+/* -*- 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/. */
+
+#ifndef gc_NurseryAwareHashMap_h
+#define gc_NurseryAwareHashMap_h
+
+namespace js {
+
+namespace detail {
+// This class only handles the incremental case and does not deal with nursery
+// pointers. The only users should be for NurseryAwareHashMap; it is defined
+// externally because we need a GCPolicy for its use in the contained map.
+template <typename T>
+class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
+{
+  public:
+    UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
+    MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
+    explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
+    UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
+      : ReadBarrieredBase<T>(mozilla::Forward<UnsafeBareReadBarriered<T>>(v))
+    {}
+
+    UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) {
+        this->value = v.value;
+        return *this;
+    }
+
+    UnsafeBareReadBarriered& operator=(const T& v) {
+        this->value = v;
+        return *this;
+    }
+
+    const T get() const {
+        if (!InternalBarrierMethods<T>::isMarkable(this->value))
+            return JS::GCPolicy<T>::initial();
+        this->read();
+        return this->value;
+    }
+
+    explicit operator bool() const {
+        return bool(this->value);
+    }
+
+    const T unbarrieredGet() const { return this->value; }
+    T* unsafeGet() { return &this->value; }
+    T const* unsafeGet() const { return &this->value; }
+};
+} // namespace detail
+
+// The "nursery aware" hash map is a special case of GCHashMap that is able to
+// treat nursery allocated members weakly during a minor GC: e.g. it allows for
+// nursery allocated objects to be collected during nursery GC where a normal
+// hash table treats such edges strongly.
+//
+// Doing this requires some strong constraints on what can be stored in this
+// table and how it can be accessed. At the moment, this table assumes that
+// all values contain a strong reference to the key. It also requires the
+// policy to contain an |isTenured| and |needsSweep| members, which is fairly
+// non-standard. This limits its usefulness to the CrossCompartmentMap at the
+// moment, but might serve as a useful base for other tables in future.
+template <typename Key,
+          typename Value,
+          typename HashPolicy = DefaultHasher<Key>,
+          typename AllocPolicy = TempAllocPolicy>
+class NurseryAwareHashMap
+{
+    using BarrieredValue = detail::UnsafeBareReadBarriered<Value>;
+    using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
+    MapType map;
+
+    // Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false.
+    // This lets us avoid a full traveral of the map on each minor GC, keeping
+    // the minor GC times proportional to the nursery heap size.
+    Vector<Key, 0, AllocPolicy> nurseryEntries;
+
+  public:
+    using Lookup = typename MapType::Lookup;
+    using Ptr = typename MapType::Ptr;
+    using Range = typename MapType::Range;
+
+    explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {}
+
+    MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); }
+
+    bool empty() const { return map.empty(); }
+    Ptr lookup(const Lookup& l) const { return map.lookup(l); }
+    void remove(Ptr p) { map.remove(p); }
+    Range all() const { return map.all(); }
+    struct Enum : public MapType::Enum {
+        explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {}
+    };
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return map.sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return map.sizeOfIncludingThis(mallocSizeOf);
+    }
+
+    MOZ_MUST_USE bool put(const Key& k, const Value& v) {
+        auto p = map.lookupForAdd(k);
+        if (p) {
+            if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
+                if (!nurseryEntries.append(k))
+                    return false;
+            }
+            p->value() = v;
+            return true;
+        }
+
+        bool ok = map.add(p, k, v);
+        if (!ok)
+            return false;
+
+        if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
+            if (!nurseryEntries.append(k)) {
+                map.remove(k);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    void sweepAfterMinorGC(JSTracer* trc) {
+        for (auto& key : nurseryEntries) {
+            auto p = map.lookup(key);
+            if (!p)
+                continue;
+
+            // Drop the entry if the value is not marked.
+            if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
+                map.remove(key);
+                continue;
+            }
+
+            // Update and relocate the key, if the value is still needed.
+            //
+            // Note that this currently assumes that all Value will contain a
+            // strong reference to Key, as per its use as the
+            // CrossCompartmentWrapperMap. We may need to make the following
+            // behavior more dynamic if we use this map in other nursery-aware
+            // contexts.
+            Key copy(key);
+            mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(&copy);
+            MOZ_ASSERT(!sweepKey);
+            map.rekeyIfMoved(key, copy);
+        }
+        nurseryEntries.clear();
+    }
+
+    void sweep() {
+        MOZ_ASSERT(nurseryEntries.empty());
+        map.sweep();
+    }
+};
+
+} // namespace js
+
+namespace JS {
+template <typename T>
+struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>>
+{
+    static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp,
+                      const char* name)
+    {
+        js::TraceEdge(trc, thingp, name);
+    }
+    static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) {
+        return js::gc::IsAboutToBeFinalized(thingp);
+    }
+};
+} // namespace JS
+
+#endif // gc_NurseryAwareHashMap_h
--- a/js/src/jit-test/tests/basic/destructuring-default.js
+++ b/js/src/jit-test/tests/basic/destructuring-default.js
@@ -148,42 +148,107 @@ var evaled = false;
 ({a: {} = (evaled = true, {})} = {});
 assertEq(evaled, true);
 evaled = false;
 assertThrowsInstanceOf(() => { [[] = (evaled = true, 2)] = [] }, TypeError);
 assertEq(evaled, true);
 
 assertThrowsInstanceOf(() => new Function('var [...rest = defaults] = [];'), SyntaxError);
 
-// bug 1124480: destructuring defaults should work correctly as part of for-in
-var defaultsSupportedInForVar = false;
-try
-{
-  new Function('for (var [a, b = 10] in " ") {}');
-  defaultsSupportedInForVar = true;
-}
-catch (e)
-{
-}
+new Function(`
+  b = undefined;
+  for (var [a, b = 10] in " ") {}
+  assertEq(b, 10);`)();
+
+new Function(`
+  b = undefined;
+  for (let [a, c = 10] in " ") { b = c; }
+  assertEq(b, 10);`)();
+
+new Function(`
+  b = undefined;
+  for (let [a, c = (x => y)] in " ") { b = c; }
+  assertEq(typeof b, "function");`)();
+
+new Function(`
+  b = undefined;
+  for (let [a, __proto__ = 10] in " ") { b = __proto__; }
+  assertEq(b, 10);`)();
 
-if (defaultsSupportedInForVar) {
-  new Function(`
-    b = undefined;
-    for (var [a, b = 10] in " ") {}
-    assertEq(b, 10);
+new Function(`
+  b = undefined;
+  for (let [a, __proto__ = (x => y)] in " ") { b = __proto__; }
+  assertEq(typeof b, "function");`)();
+
+new Function(`
+  b = undefined;
+  for (var {1: b = 10} in " ") {}
+  assertEq(b, 10);`)();
+
+new Function(`
+  b = undefined;
+  for (let {1: c = 10} in " ") { b = c; }
+  assertEq(b, 10);`)();
+
+new Function(`
+  b = undefined;
+  for (let { c = 10 } in " ") { b = c; }
+  assertEq(b, 10);`)();
+
+new Function(`
+  b = undefined;
+  assertEq(Number.prototype.a, undefined);
+  for (var { a: c = (x => y) } in [{}]) { b = c; }
+  assertEq(typeof b, "function");`)();
 
-    b = undefined;
-    for (let [a, c = 10] in " ") { b = c; }
-    assertEq(b, 10);
+new Function(`
+  b = undefined;
+  Object.defineProperty(String.prototype, "__proto__",
+                        { value: undefined, configurable: true });
+  for (var { __proto__: c = (x => y) } in [{}]) { b = c; }
+  delete String.prototype.__proto__;
+  assertEq(typeof b, "function");`)();
 
-    b = undefined;
-    for (var {1: b = 10} in " ") {}
-    assertEq(b, 10);
+new Function(`
+  b = undefined;
+  for (var { a: c = (x => y) } of [{ a: undefined }]) { b = c; }
+  assertEq(typeof b, "function");`)();
+
+new Function(`
+  b = undefined;
+  for (var { __proto__: c = (x => y) } of [{ ["__proto__"]: undefined }]) { b = c; }
+  assertEq(typeof b, "function");`)();
 
-    b = undefined;
-    for (let {1: c = 10} in " ") { b = c; }
-    assertEq(b, 10);
+new Function(`
+  b = undefined;
+  var ts = Function.prototype.toString;
+  Function.prototype.toString = () => 'hi';
+  String.prototype.hi = 42;
+  for (var { [(x => y)]: c } in [0]) { b = c; }
+  Function.prototype.toString = ts;
+  delete String.prototype.hi;
+  assertEq(b, 42);`)();
 
-    b = undefined;
-    for (let {c = 10} in " ") { b = c; }
-    assertEq(b, 10);
-  `)();
-}
+new Function(`
+  b = undefined;
+  var ts = Function.prototype.toString;
+  Function.prototype.toString = () => 'hi';
+  String.prototype.hi = 42;
+  for (var { [(x => y)]: __proto__ } in [0]) { b = __proto__; }
+  Function.prototype.toString = ts;
+  delete String.prototype.hi;
+  assertEq(b, 42);`)();
+
+new Function(`
+  b = undefined;
+  var ts = Function.prototype.toString;
+  Function.prototype.toString = () => 'hi';
+  for (var { [(x => y)]: c } of [{ 'hi': 42 }]) { b = c; }
+  Function.prototype.toString = ts;
+  assertEq(b, 42);`)();
+
+new Function(`
+  b = undefined;
+  var ts = Function.prototype.toString;
+  Function.prototype.toString = () => 'hi';
+  for (var { [(x => y)]: __proto__ } of [{ hi: 42 }]) { b = __proto__; }
+  Function.prototype.toString = ts;
+  assertEq(b, 42);`)();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/promise/bug-1298776.js
@@ -0,0 +1,2 @@
+if (typeof oomTest === 'function')
+    oomTest(Function(`new Promise(res=>res)`));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/regexp/clone-statics.js
@@ -0,0 +1,21 @@
+if (helperThreadCount() === 0)
+  quit(0);
+
+offThreadCompileScript(`
+  function foo(x, {}) {
+    do {
+      re = /erwe/;
+      if (x === 1)
+        re.x = 1;
+      else
+        re.x = "a";
+      assertEq(re.x.length, (x === 1) ? undefined : 1);
+    } while (!inIon());
+  }
+
+  foo(0, 0);
+  RegExp.multiline = 1;
+  foo(1, 0);
+`);
+
+runOffThreadScript();
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -362,38 +362,38 @@ var {v2i, i2i, i2v} = wasmEvalText(`(mod
     (func (param i32) (result i32) (call_indirect 0 (get_local 0)))
     (func (param i32) (param i32) (result i32) (call_indirect 1 (get_local 0) (get_local 1)))
     (func (param i32) (call_indirect 2 (get_local 0) (i32.const 0)))
     (export "v2i" 6)
     (export "i2i" 7)
     (export "i2v" 8)
 )`);
 
-const badIndirectCall = /bad wasm indirect call/;
+const signatureMismatch = /indirect call signature mismatch/;
 
 assertEq(v2i(0), 13);
 assertEq(v2i(1), 42);
-assertErrorMessage(() => v2i(2), Error, badIndirectCall);
-assertErrorMessage(() => v2i(3), Error, badIndirectCall);
-assertErrorMessage(() => v2i(4), Error, badIndirectCall);
-assertErrorMessage(() => v2i(5), Error, badIndirectCall);
+assertErrorMessage(() => v2i(2), Error, signatureMismatch);
+assertErrorMessage(() => v2i(3), Error, signatureMismatch);
+assertErrorMessage(() => v2i(4), Error, signatureMismatch);
+assertErrorMessage(() => v2i(5), Error, signatureMismatch);
 
-assertErrorMessage(() => i2i(0), Error, badIndirectCall);
-assertErrorMessage(() => i2i(1), Error, badIndirectCall);
+assertErrorMessage(() => i2i(0), Error, signatureMismatch);
+assertErrorMessage(() => i2i(1), Error, signatureMismatch);
 assertEq(i2i(2, 100), 101);
 assertEq(i2i(3, 100), 102);
 assertEq(i2i(4, 100), 103);
 assertEq(i2i(5, 100), 104);
 
-assertErrorMessage(() => i2v(0), Error, badIndirectCall);
-assertErrorMessage(() => i2v(1), Error, badIndirectCall);
-assertErrorMessage(() => i2v(2), Error, badIndirectCall);
-assertErrorMessage(() => i2v(3), Error, badIndirectCall);
-assertErrorMessage(() => i2v(4), Error, badIndirectCall);
-assertErrorMessage(() => i2v(5), Error, badIndirectCall);
+assertErrorMessage(() => i2v(0), Error, signatureMismatch);
+assertErrorMessage(() => i2v(1), Error, signatureMismatch);
+assertErrorMessage(() => i2v(2), Error, signatureMismatch);
+assertErrorMessage(() => i2v(3), Error, signatureMismatch);
+assertErrorMessage(() => i2v(4), Error, signatureMismatch);
+assertErrorMessage(() => i2v(5), Error, signatureMismatch);
 
 {
     enableSPSProfiling();
 
     var stack;
     assertEq(wasmEvalText(
         `(module
             (type $v2v (func))
--- a/js/src/jit-test/tests/wasm/table-gc.js
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -17,17 +17,17 @@ var callee = i => `(func $f${i} (type $v
 // hold instances alive and instances hold imported tables alive. Nothing
 // should hold the export object alive.
 resetFinalizeCount();
 var i = evalText(`(module (table (resizable 2)) (export "tbl" table) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`);
 var e = i.exports;
 var t = e.tbl;
 var f = t.get(0);
 assertEq(f(), e.call(0));
-assertErrorMessage(() => e.call(1), Error, /bad wasm indirect call/);
+assertErrorMessage(() => e.call(1), Error, /indirect call to null/);
 assertErrorMessage(() => e.call(2), Error, /out-of-range/);
 assertEq(finalizeCount(), 0);
 i.edge = makeFinalizeObserver();
 e.edge = makeFinalizeObserver();
 t.edge = makeFinalizeObserver();
 f.edge = makeFinalizeObserver();
 gc();
 assertEq(finalizeCount(), 0);
@@ -74,34 +74,33 @@ gc();
 assertEq(finalizeCount(), 2);
 i = null;
 gc();
 assertEq(finalizeCount(), 2);
 t = null;
 gc();
 assertEq(finalizeCount(), 4);
 
-// The bad-indirect-call stub should (currently, could be changed later) keep
-// the instance containing that stub alive.
+// Null elements shouldn't keep anything alive.
 resetFinalizeCount();
 var i = evalText(`(module (table (resizable 2)) (export "tbl" table) ${caller})`);
 var e = i.exports;
 var t = e.tbl;
 i.edge = makeFinalizeObserver();
 e.edge = makeFinalizeObserver();
 t.edge = makeFinalizeObserver();
 gc();
 assertEq(finalizeCount(), 0);
 i.exports = null;
 e = null;
 gc();
 assertEq(finalizeCount(), 1);
 i = null;
 gc();
-assertEq(finalizeCount(), 1);
+assertEq(finalizeCount(), 2);
 t = null;
 gc();
 assertEq(finalizeCount(), 3);
 
 // Before initialization, a table is not bound to any instance.
 resetFinalizeCount();
 var i = evalText(`(module (func $f0 (result i32) (i32.const 0)) (export "f0" $f0))`);
 var t = new Table({initial:4, element:"anyfunc"});
@@ -139,24 +138,23 @@ t.get(0).edge = makeFinalizeObserver();
 gc();
 assertEq(finalizeCount(), 2);
 i = null;
 gc();
 assertEq(finalizeCount(), 2);
 t.set(0, null);
 assertEq(t.get(0), null);
 gc();
-assertEq(finalizeCount(), 2);
+assertEq(finalizeCount(), 3);
 t = null;
 gc();
 assertEq(finalizeCount(), 4);
 
-// Once all of an instance's elements in a Table (including the
-// bad-indirect-call stub) have been clobbered, the Instance should not be
-// rooted.
+// Once all of an instance's elements in a Table have been clobbered, the
+// Instance should not be reachable.
 resetFinalizeCount();
 var i1 = evalText(`(module (func $f1 (result i32) (i32.const 13)) (export "f1" $f1))`);
 var i2 = evalText(`(module (func $f2 (result i32) (i32.const 42)) (export "f2" $f2))`);
 var f1 = i1.exports.f1;
 var f2 = i2.exports.f2;
 var t = new Table({initial:2, element:"anyfunc"});
 i1.edge = makeFinalizeObserver();
 i2.edge = makeFinalizeObserver();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/table-pre-barrier.js
@@ -0,0 +1,24 @@
+load(libdir + "wasm.js");
+
+const Module = WebAssembly.Module;
+const Instance = WebAssembly.Instance;
+const Table = WebAssembly.Table;
+
+var i42 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 42)) (export "f" 0))`)));
+var i13 = new Instance(new Module(textToBinary(`(module (func (result i32) (i32.const 13)) (export "f" 0))`)));
+var t = new Table({element:"anyfunc", initial:1});
+t.set(0, i42.exports.f);
+
+assertEq(t.get(0)(), 42);
+
+verifyprebarriers();
+t.set(0, i13.exports.f);
+verifyprebarriers();
+
+assertEq(t.get(0)(), 13);
+
+verifyprebarriers();
+t.set(0, null);
+verifyprebarriers();
+
+assertEq(t.get(0), null);
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -35,46 +35,46 @@ var m = new Module(textToBinary(`
 var tbl = new Table({initial:50, element:"anyfunc"});
 assertEq(new Instance(m, {globals:{a:20, table:tbl}}) instanceof Instance, true);
 assertErrorMessage(() => new Instance(m, {globals:{a:50, table:tbl}}), RangeError, /elem segment does not fit/);
 
 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
 var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;
 
 var call = evalText(`(module (table (resizable 10)) ${callee(0)} ${caller})`).exports.call;
-assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(0), Error, /indirect call to null/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0)) ${callee(0)} ${caller})`).exports.call;
-assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(0), Error, /indirect call to null/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var call = evalText(`(module (table (resizable 10)) (elem (i32.const 0) $f0) ${callee(0)} ${caller})`).exports.call;
 assertEq(call(0), 0);
-assertErrorMessage(() => call(1), Error, /bad wasm indirect call/);
-assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(1), Error, /indirect call to null/);
+assertErrorMessage(() => call(2), Error, /indirect call to null/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var call = evalText(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f1) (elem (i32.const 4) $f0 $f2) ${callee(0)} ${callee(1)} ${callee(2)} ${caller})`).exports.call;
-assertErrorMessage(() => call(0), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(0), Error, /indirect call to null/);
 assertEq(call(1), 0);
 assertEq(call(2), 1);
-assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(3), Error, /indirect call to null/);
 assertEq(call(4), 0);
 assertEq(call(5), 2);
-assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(6), Error, /indirect call to null/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
 var tbl = new Table({initial:3, element:"anyfunc"});
 var call = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
 assertEq(tbl.get(0)(), 0);
 assertEq(tbl.get(1)(), 1);
-assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(2), Error, /indirect call to null/);
 assertEq(tbl.get(2), null);
 
 var exp = evalText(`(module (import "a" "b" (table 3)) (export "tbl" table) (elem (i32.const 2) $f2) ${callee(2)} ${caller})`, {a:{b:tbl}}).exports;
 assertEq(exp.tbl, tbl);
 assertEq(exp.call(0), 0);
 assertEq(exp.call(1), 1);
 assertEq(exp.call(2), 2);
 assertEq(call(0), 0);
@@ -85,17 +85,17 @@ assertEq(tbl.get(1)(), 1);
 assertEq(tbl.get(2)(), 2);
 
 var exp1 = evalText(`(module (table (resizable 10)) (export "tbl" table) (elem (i32.const 0) $f0 $f0) ${callee(0)} (export "f0" $f0) ${caller})`).exports
 assertEq(exp1.tbl.get(0), exp1.f0);
 assertEq(exp1.tbl.get(1), exp1.f0);
 assertEq(exp1.tbl.get(2), null);
 assertEq(exp1.call(0), 0);
 assertEq(exp1.call(1), 0);
-assertErrorMessage(() => exp1.call(2), Error, /bad wasm indirect call/);
+assertErrorMessage(() => exp1.call(2), Error, /indirect call to null/);
 var exp2 = evalText(`(module (import "a" "b" (table 10)) (export "tbl" table) (elem (i32.const 1) $f1 $f1) ${callee(1)} (export "f1" $f1) ${caller})`, {a:{b:exp1.tbl}}).exports
 assertEq(exp1.tbl, exp2.tbl);
 assertEq(exp2.tbl.get(0), exp1.f0);
 assertEq(exp2.tbl.get(1), exp2.f1);
 assertEq(exp2.tbl.get(2), exp2.f1);
 assertEq(exp1.call(0), 0);
 assertEq(exp1.call(1), 1);
 assertEq(exp1.call(2), 1);
@@ -107,17 +107,17 @@ var tbl = new Table({initial:3, element:
 var e1 = evalText(`(module (func $f (result i32) (i32.const 42)) (export "f" $f))`).exports;
 var e2 = evalText(`(module (func $g (result f32) (f32.const 10)) (export "g" $g))`).exports;
 var e3 = evalText(`(module (func $h (result i32) (i32.const 13)) (export "h" $h))`).exports;
 tbl.set(0, e1.f);
 tbl.set(1, e2.g);
 tbl.set(2, e3.h);
 var e4 = evalText(`(module (import "a" "b" (table 3)) ${caller})`, {a:{b:tbl}}).exports;
 assertEq(e4.call(0), 42);
-assertErrorMessage(() => e4.call(1), Error, /bad wasm indirect call/);
+assertErrorMessage(() => e4.call(1), Error, /indirect call signature mismatch/);
 assertEq(e4.call(2), 13);
 
 var m = new Module(textToBinary(`(module
     (type $i2i (func (param i32) (result i32)))
     (import "a" "mem" (memory 1))
     (import "a" "tbl" (table 10))
     (import $imp "a" "imp" (result i32))
     (func $call (param $i i32) (result i32)
@@ -159,17 +159,17 @@ var call = evalText(`(module
     (func $a (type $v2i1) (result i32) (i32.const 0))
     (func $b (type $v2i2) (result i32) (i32.const 1))
     (func $c (type $i2v) (param i32))
     (func $call (param i32) (result i32) (call_indirect $v2i1 (get_local 0)))
     (export "call" $call)
 )`).exports.call;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
-assertErrorMessage(() => call(2), Error, /bad wasm indirect call/);
+assertErrorMessage(() => call(2), Error, /indirect call signature mismatch/);
 
 var call = evalText(`(module
     (type $A (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
     (type $B (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
     (type $C (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
     (type $D (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
     (type $E (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
     (type $F (func (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (param i32) (result i32)))
@@ -183,10 +183,10 @@ var call = evalText(`(module
     (func $f (type $F) (get_local 12))
     (func $g (type $G) (get_local 13))
     (func $call (param i32) (result i32)
         (call_indirect $A (get_local 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 42)))
     (export "call" $call)
 )`).exports.call;
 assertEq(call(0), 42);
 for (var i = 1; i < 7; i++)
-    assertErrorMessage(() => call(i), Error, /bad wasm indirect call/);
+    assertErrorMessage(() => call(i), Error, /indirect call signature mismatch/);
 assertErrorMessage(() => call(7), Error, /out-of-range/);
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -19,16 +19,18 @@
 #include "gc/Statistics.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitSpewer.h"
 #include "js/Vector.h"
 #include "vm/SPSProfiler.h"
 
 #include "jsscriptinlines.h"
 
+#include "vm/TypeInference-inl.h"
+
 using mozilla::Maybe;
 
 namespace js {
 namespace jit {
 
 static inline JitcodeRegionEntry
 RegionAtAddr(const JitcodeGlobalEntry::IonEntry& entry, void* ptr,
              uint32_t* ptrOffset)
@@ -893,17 +895,17 @@ JitcodeGlobalEntry::IonEntry::mark(JSTra
 
     if (!optsAllTypes_)
         return markedAny;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
          iter != optsAllTypes_->end(); iter++)
     {
         if (ShouldMarkProvider::ShouldMark(&iter->type)) {
-            TypeSet::MarkTypeUnbarriered(trc, &iter->type, "jitcodeglobaltable-ionentry-type");
+            iter->type.trace(trc);
             markedAny = true;
         }
         if (iter->hasAllocationSite() && ShouldMarkProvider::ShouldMark(&iter->script)) {
             TraceManuallyBarrieredEdge(trc, &iter->script,
                                        "jitcodeglobaltable-ionentry-type-addendum-script");
             markedAny = true;
         } else if (iter->hasConstructor() && ShouldMarkProvider::ShouldMark(&iter->constructor)) {
             TraceManuallyBarrieredEdge(trc, &iter->constructor,
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2813,21 +2813,24 @@ MacroAssembler::wasmCallIndirect(const w
         if (sizeof(wasm::ExternalTableElem) == 8) {
             computeEffectiveAddress(BaseIndex(scratch, index, TimesEight), scratch);
         } else {
             lshift32(Imm32(4), index);
             addPtr(index, scratch);
         }
 
         loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, tls)), WasmTlsReg);
+        branchTest32(Assembler::Zero, WasmTlsReg, WasmTlsReg, wasm::JumpTarget::IndirectCallToNull);
+
         loadWasmPinnedRegsFromTls();
 
         loadPtr(Address(scratch, offsetof(wasm::ExternalTableElem, code)), scratch);
     } else {
         loadPtr(BaseIndex(scratch, index, ScalePointer), scratch);
+        branchTest32(Assembler::Zero, scratch, scratch, wasm::JumpTarget::IndirectCallToNull);
     }
 
     call(desc, scratch);
 }
 
 //}}} check_macroassembler_style
 
 void
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -280,16 +280,17 @@ CodeGeneratorX86::visitLoadTypedArrayEle
             masm.j(Assembler::AboveOrEqual, ool->entry());
         else
             bailoutIf(Assembler::AboveOrEqual, ins->snapshot());
     }
 
     Operand srcAddr(ptr, int32_t(mir->base().asValue()) + int32_t(offset));
     switch (accessType) {
       case Scalar::Int8:         masm.movsblWithPatch(srcAddr, out.gpr()); break;
+      case Scalar::Uint8Clamped:
       case Scalar::Uint8:        masm.movzblWithPatch(srcAddr, out.gpr()); break;
       case Scalar::Int16:        masm.movswlWithPatch(srcAddr, out.gpr()); break;
       case Scalar::Uint16:       masm.movzwlWithPatch(srcAddr, out.gpr()); break;
       case Scalar::Int32:
       case Scalar::Uint32:       masm.movlWithPatch(srcAddr, out.gpr()); break;
       case Scalar::Float32:      masm.vmovssWithPatch(srcAddr, out.fpu()); break;
       case Scalar::Float64:      masm.vmovsdWithPatch(srcAddr, out.fpu()); break;
       default:                   MOZ_CRASH("Unexpected type");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -199,17 +199,17 @@ MSG_DEF(JSMSG_LET_STARTING_FOROF_LHS,  0
 MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    1, JSEXN_TYPEERR,   "generator function {0} returns a value")
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,         1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
 MSG_DEF(JSMSG_BAD_INCOP_OPERAND,       0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
 MSG_DEF(JSMSG_BAD_METHOD_DEF,          0, JSEXN_SYNTAXERR, "bad method definition")
 MSG_DEF(JSMSG_BAD_OCTAL,               1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
 MSG_DEF(JSMSG_BAD_OPERAND,             1, JSEXN_SYNTAXERR, "invalid {0} operand")
 MSG_DEF(JSMSG_BAD_PROP_ID,             0, JSEXN_SYNTAXERR, "invalid property id")
 MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD,     1, JSEXN_SYNTAXERR, "{0} not in function")
-MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,       1, JSEXN_SYNTAXERR, "'{0}' can't be defined or assigned to in strict mode code")
 MSG_DEF(JSMSG_BAD_SWITCH,              0, JSEXN_SYNTAXERR, "invalid switch statement")
 MSG_DEF(JSMSG_BAD_SUPER,               0, JSEXN_SYNTAXERR, "invalid use of keyword 'super'")
 MSG_DEF(JSMSG_BAD_SUPERPROP,           1, JSEXN_SYNTAXERR, "use of super {0} accesses only valid within methods or eval code within methods")
 MSG_DEF(JSMSG_BAD_SUPERCALL,           0, JSEXN_SYNTAXERR, "super() is only valid in derived class constructors")
 MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
 MSG_DEF(JSMSG_BRACKET_AFTER_LIST,      0, JSEXN_SYNTAXERR, "missing ] after element list")
 MSG_DEF(JSMSG_BRACKET_IN_INDEX,        0, JSEXN_SYNTAXERR, "missing ] in index expression")
 MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,     0, JSEXN_SYNTAXERR, "catch after unconditional catch")
@@ -343,17 +343,18 @@ MSG_DEF(JSMSG_ESCAPED_KEYWORD,         0
 MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,       1, JSEXN_TYPEERR, "asm.js type error: {0}")
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,       1, JSEXN_TYPEERR, "asm.js link error: {0}")
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,         1, JSEXN_WARN,    "Successfully compiled asm.js code ({0})")
 
 // wasm
 MSG_DEF(JSMSG_WASM_FAIL,               1, JSEXN_TYPEERR,     "wasm error: {0}")
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR,     "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
-MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,         "bad wasm indirect call")
+MSG_DEF(JSMSG_WASM_IND_CALL_TO_NULL,   0, JSEXN_ERR,         "indirect call to null")
+MSG_DEF(JSMSG_WASM_IND_CALL_BAD_SIG,   0, JSEXN_ERR,         "indirect call signature mismatch")
 MSG_DEF(JSMSG_WASM_BAD_GROW,           0, JSEXN_ERR,         "failed to grow memory")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
 MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_DESC_ARG,       1, JSEXN_TYPEERR,     "first argument must be a {0} descriptor")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_TYPEERR,     "imported {0} with incompatible size")
 MSG_DEF(JSMSG_WASM_BAD_SIZE,           2, JSEXN_TYPEERR,     "bad {0} {1} size")
 MSG_DEF(JSMSG_WASM_BAD_ELEMENT,        0, JSEXN_TYPEERR,     "\"element\" property of table descriptor must be \"anyfunc\"")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
--- a/js/src/jsapi-tests/testGCMarking.cpp
+++ b/js/src/jsapi-tests/testGCMarking.cpp
@@ -1,21 +1,80 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi.h"
+#include "jscompartment.h"
 
 #include "js/RootingAPI.h"
 #include "js/SliceBudget.h"
 #include "jsapi-tests/tests.h"
 
+static bool
+ConstructCCW(JSContext* cx, const JSClass* globalClasp,
+             JS::HandleObject global1, JS::MutableHandleObject wrapper,
+             JS::MutableHandleObject global2, JS::MutableHandleObject wrappee)
+{
+    if (!global1) {
+        fprintf(stderr, "null initial global");
+        return false;
+    }
+
+    // Define a second global in a different zone.
+    JS::CompartmentOptions options;
+    global2.set(JS_NewGlobalObject(cx, globalClasp, nullptr,
+                                   JS::FireOnNewGlobalHook, options));
+    if (!global2) {
+        fprintf(stderr, "failed to create second global");
+        return false;
+    }
+
+    // This should always be false, regardless.
+    if (global1->compartment() == global2->compartment()) {
+        fprintf(stderr, "second global claims to be in global1's compartment");
+        return false;
+    }
+
+    // This checks that the API obeys the implicit zone request.
+    if (global1->zone() == global2->zone()) {
+        fprintf(stderr, "global2 is in global1's zone");
+        return false;
+    }
+
+    // Define an object in compartment 2, that is wrapped by a CCW into compartment 1.
+    {
+        JSAutoCompartment ac(cx, global2);
+        wrappee.set(JS_NewPlainObject(cx));
+        if (wrappee->compartment() != global2->compartment()) {
+            fprintf(stderr, "wrappee in wrong compartment");
+            return false;
+        }
+    }
+
+    wrapper.set(wrappee);
+    if (!JS_WrapObject(cx, wrapper)) {
+        fprintf(stderr, "failed to wrap");
+        return false;
+    }
+    if (wrappee == wrapper) {
+        fprintf(stderr, "expected wrapping");
+        return false;
+    }
+    if (wrapper->compartment() != global1->compartment()) {
+        fprintf(stderr, "wrapper in wrong compartment");
+        return false;
+    }
+
+    return true;
+}
+
 class CCWTestTracer : public JS::CallbackTracer {
     void onChild(const JS::GCCellPtr& thing) override {
         numberOfThingsTraced++;
 
         printf("*thingp         = %p\n", thing.asCell());
         printf("*expectedThingp = %p\n", *expectedThingp);
 
         printf("kind         = %d\n", static_cast<int>(thing.kind()));
@@ -37,54 +96,191 @@ class CCWTestTracer : public JS::Callbac
         numberOfThingsTraced(0),
         expectedThingp(expectedThingp),
         expectedKind(expectedKind)
     { }
 };
 
 BEGIN_TEST(testTracingIncomingCCWs)
 {
-    // Get two globals, in two different zones.
+#ifdef JS_GC_ZEAL
+    // Disable zeal modes because this test needs to control exactly when the GC happens.
+    JS_SetGCZeal(cx, 0, 100);
+#endif
+    JS_GC(cx);
 
     JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
-    CHECK(global1);
-    JS::CompartmentOptions options;
-    JS::RootedObject global2(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr,
-                                                    JS::FireOnNewGlobalHook, options));
-    CHECK(global2);
-    CHECK(global1->compartment() != global2->compartment());
+    JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
+    JS_GC(cx);
+    CHECK(!js::gc::IsInsideNursery(wrappee));
+    CHECK(!js::gc::IsInsideNursery(wrapper));
 
-    // Define an object in one compartment, that is wrapped by a CCW in another
-    // compartment.
-
-    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
-    CHECK(obj->compartment() == global1->compartment());
-
-    JSAutoCompartment ac(cx, global2);
-    JS::RootedObject wrapper(cx, obj);
-    CHECK(JS_WrapObject(cx, &wrapper));
     JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
-    CHECK(JS_SetProperty(cx, global2, "ccw", v));
+    CHECK(JS_SetProperty(cx, global1, "ccw", v));
 
     // Ensure that |TraceIncomingCCWs| finds the object wrapped by the CCW.
 
     JS::CompartmentSet compartments;
     CHECK(compartments.init());
-    CHECK(compartments.put(global1->compartment()));
+    CHECK(compartments.put(global2->compartment()));
 
-    void* thing = obj.get();
+    void* thing = wrappee.get();
     CCWTestTracer trc(cx, &thing, JS::TraceKind::Object);
     JS::TraceIncomingCCWs(&trc, compartments);
     CHECK(trc.numberOfThingsTraced == 1);
     CHECK(trc.okay);
 
     return true;
 }
 END_TEST(testTracingIncomingCCWs)
 
+static size_t
+countWrappers(JSCompartment* comp)
+{
+    size_t count = 0;
+    for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront())
+        ++count;
+    return count;
+}
+
+BEGIN_TEST(testDeadNurseryCCW)
+{
+#ifdef JS_GC_ZEAL
+    // Disable zeal modes because this test needs to control exactly when the GC happens.
+    JS_SetGCZeal(cx, 0, 100);
+#endif
+    JS_GC(cx);
+
+    JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
+    CHECK(js::gc::IsInsideNursery(wrappee));
+    CHECK(js::gc::IsInsideNursery(wrapper));
+
+    // Now let the obj and wrapper die.
+    wrappee = wrapper = nullptr;
+
+    // Now a GC should clear the CCW.
+    CHECK(countWrappers(global1->compartment()) == 1);
+    cx->gc.evictNursery();
+    CHECK(countWrappers(global1->compartment()) == 0);
+
+    // Check for corruption of the CCW table by doing a full GC to force sweeping.
+    JS_GC(cx);
+
+    return true;
+}
+END_TEST(testDeadNurseryCCW)
+
+BEGIN_TEST(testLiveNurseryCCW)
+{
+#ifdef JS_GC_ZEAL
+    // Disable zeal modes because this test needs to control exactly when the GC happens.
+    JS_SetGCZeal(cx, 0, 100);
+#endif
+    JS_GC(cx);
+
+    JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
+    CHECK(js::gc::IsInsideNursery(wrappee));
+    CHECK(js::gc::IsInsideNursery(wrapper));
+
+    // Now a GC should not kill the CCW.
+    CHECK(countWrappers(global1->compartment()) == 1);
+    cx->gc.evictNursery();
+    CHECK(countWrappers(global1->compartment()) == 1);
+
+    CHECK(!js::gc::IsInsideNursery(wrappee));
+    CHECK(!js::gc::IsInsideNursery(wrapper));
+
+    // Check for corruption of the CCW table by doing a full GC to force sweeping.
+    JS_GC(cx);
+
+    return true;
+}
+END_TEST(testLiveNurseryCCW)
+
+BEGIN_TEST(testLiveNurseryWrapperCCW)
+{
+#ifdef JS_GC_ZEAL
+    // Disable zeal modes because this test needs to control exactly when the GC happens.
+    JS_SetGCZeal(cx, 0, 100);
+#endif
+    JS_GC(cx);
+
+    JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
+    CHECK(js::gc::IsInsideNursery(wrappee));
+    CHECK(js::gc::IsInsideNursery(wrapper));
+
+    // The wrapper contains a strong reference to the wrappee, so just dropping
+    // the reference to the wrappee will not drop the CCW table entry as long
+    // as the wrapper is held strongly. Thus, the minor collection here must
+    // tenure both the wrapper and the wrappee and keep both in the table.
+    wrappee = nullptr;
+
+    // Now a GC should not kill the CCW.
+    CHECK(countWrappers(global1->compartment()) == 1);
+    cx->gc.evictNursery();
+    CHECK(countWrappers(global1->compartment()) == 1);
+
+    CHECK(!js::gc::IsInsideNursery(wrapper));
+
+    // Check for corruption of the CCW table by doing a full GC to force sweeping.
+    JS_GC(cx);
+
+    return true;
+}
+END_TEST(testLiveNurseryWrapperCCW)
+
+BEGIN_TEST(testLiveNurseryWrappeeCCW)
+{
+#ifdef JS_GC_ZEAL
+    // Disable zeal modes because this test needs to control exactly when the GC happens.
+    JS_SetGCZeal(cx, 0, 100);
+#endif
+    JS_GC(cx);
+
+    JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrapper(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject global2(cx, JS::CurrentGlobalOrNull(cx));
+    JS::RootedObject wrappee(cx, JS::CurrentGlobalOrNull(cx));
+    CHECK(ConstructCCW(cx, getGlobalClass(), global1, &wrapper, &global2, &wrappee));
+    CHECK(js::gc::IsInsideNursery(wrappee));
+    CHECK(js::gc::IsInsideNursery(wrapper));
+
+    // Let the wrapper die. The wrapper should drop from the table when we GC,
+    // even though there are other non-cross-compartment edges to it.
+    wrapper = nullptr;
+
+    // Now a GC should not kill the CCW.
+    CHECK(countWrappers(global1->compartment()) == 1);
+    cx->gc.evictNursery();
+    CHECK(countWrappers(global1->compartment()) == 0);
+
+    CHECK(!js::gc::IsInsideNursery(wrappee));
+
+    // Check for corruption of the CCW table by doing a full GC to force sweeping.
+    JS_GC(cx);
+
+    return true;
+}
+END_TEST(testLiveNurseryWrappeeCCW)
+
 BEGIN_TEST(testIncrementalRoots)
 {
     JSRuntime* rt = cx->runtime();
 
 #ifdef JS_GC_ZEAL
     // Disable zeal modes because this test needs to control exactly when the GC happens.
     JS_SetGCZeal(cx, 0, 100);
 #endif
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -186,16 +186,21 @@ JSObject* newCCW(JS::HandleObject source
         if (!object)
             return nullptr;
     }
     {
         JSAutoCompartment ac(cx, sourceZone);
         if (!JS_WrapObject(cx, &object))
             return nullptr;
     }
+
+    // In order to test the SCC algorithm, we need the wrapper/wrappee to be
+    // tenured.
+    cx->gc.evictNursery();
+
     return object;
 }
 
 JSObject* newDelegate()
 {
     static const js::ClassOps delegateClassOps = {
         nullptr, /* addProperty */
         nullptr, /* delProperty */
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -228,44 +228,24 @@ JSCompartment::checkWrapperMapAfterMovin
         e.front().mutableKey().applyToDebugger(CheckGCThingAfterMovingGCFunctor());
 
         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(e.front().key());
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
 #endif
 
-namespace {
-struct IsInsideNurseryFunctor {
-    template <class T> bool operator()(T tp) { return IsInsideNursery(*tp); }
-};
-} // namespace (anonymous)
-
 bool
 JSCompartment::putWrapper(JSContext* cx, const CrossCompartmentKey& wrapped,
                           const js::Value& wrapper)
 {
     MOZ_ASSERT(wrapped.is<JSString*>() == wrapper.isString());
     MOZ_ASSERT_IF(!wrapped.is<JSString*>(), wrapper.isObject());
 
-    /* There's no point allocating wrappers in the nursery since we will tenure them anyway. */
-    MOZ_ASSERT(!IsInsideNursery(static_cast<gc::Cell*>(wrapper.toGCThing())));
-
-    bool isNuseryKey =
-        const_cast<CrossCompartmentKey&>(wrapped).applyToWrapped(IsInsideNurseryFunctor()) ||
-        const_cast<CrossCompartmentKey&>(wrapped).applyToDebugger(IsInsideNurseryFunctor());
-
-    if (isNuseryKey && !nurseryCCKeys.append(wrapped)) {
-        ReportOutOfMemory(cx);
-        return false;
-    }
-
-    if (!crossCompartmentWrappers.put(wrapped, ReadBarriered<Value>(wrapper))) {
-        if (isNuseryKey)
-            nurseryCCKeys.popBack();
+    if (!crossCompartmentWrappers.put(wrapped, wrapper)) {
         ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
 static JSString*
@@ -601,26 +581,16 @@ JSCompartment::trace(JSTracer* trc)
 {
     savedStacks_.trace(trc);
 
     // Atoms are always tenured.
     if (!trc->runtime()->isHeapMinorCollecting())
         varNames_.trace(trc);
 }
 
-struct TraceFunctor {
-    JSTracer* trc_;
-    const char* name_;
-    TraceFunctor(JSTracer *trc, const char* name)
-      : trc_(trc), name_(name) {}
-    template <class T> void operator()(T* t) {
-        TraceManuallyBarrieredEdge(trc_, t, name_);
-    }
-};
-
 void
 JSCompartment::traceRoots(JSTracer* trc, js::gc::GCRuntime::TraceOrMarkRuntime traceOrMark)
 {
     if (objectMetadataState.is<PendingMetadata>()) {
         TraceRoot(trc,
                   &objectMetadataState.as<PendingMetadata>(),
                   "on-stack object pending metadata");
     }
@@ -682,28 +652,16 @@ JSCompartment::traceRoots(JSTracer* trc,
             TraceRoot(trc, &script, "profilingScripts");
             MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
         }
     }
 
     if (nonSyntacticLexicalEnvironments_)
         nonSyntacticLexicalEnvironments_->trace(trc);
 
-    // In a minor GC we need to mark nursery objects that are the targets of
-    // cross compartment wrappers.
-    if (trc->runtime()->isHeapMinorCollecting()) {
-        for (auto key : nurseryCCKeys) {
-            CrossCompartmentKey prior = key;
-            key.applyToWrapped(TraceFunctor(trc, "ccw wrapped"));
-            key.applyToDebugger(TraceFunctor(trc, "ccw debugger"));
-            crossCompartmentWrappers.rekeyIfMoved(prior, key);
-        }
-        nurseryCCKeys.clear();
-    }
-
     wasm.trace(trc);
 }
 
 void
 JSCompartment::finishRoots()
 {
     if (watchpointMap)
         watchpointMap->clear();
@@ -719,22 +677,24 @@ JSCompartment::finishRoots()
 
     clearScriptCounts();
 
     if (nonSyntacticLexicalEnvironments_)
         nonSyntacticLexicalEnvironments_->clear();
 }
 
 void
-JSCompartment::sweepAfterMinorGC()
+JSCompartment::sweepAfterMinorGC(JSTracer* trc)
 {
     globalWriteBarriered = 0;
 
     if (innerViews.needsSweepAfterMinorGC())
         innerViews.sweepAfterMinorGC();
+
+    crossCompartmentWrappers.sweepAfterMinorGC(trc);
 }
 
 void
 JSCompartment::sweepSavedStacks()
 {
     savedStacks_.sweep();
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -12,16 +12,17 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/Variant.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
 #include "asmjs/WasmCompartment.h"
 #include "builtin/RegExp.h"
 #include "gc/Barrier.h"
+#include "gc/NurseryAwareHashMap.h"
 #include "gc/Zone.h"
 #include "vm/GlobalObject.h"
 #include "vm/PIC.h"
 #include "vm/SavedStacks.h"
 #include "vm/Time.h"
 
 namespace js {
 
@@ -63,17 +64,17 @@ class DtoaCache {
         this->s = s;
     }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkCacheAfterMovingGC() { MOZ_ASSERT(!s || !IsForwarded(s)); }
 #endif
 };
 
-struct CrossCompartmentKey
+class CrossCompartmentKey
 {
   public:
     enum DebuggerObjectKind : uint8_t { DebuggerSource, DebuggerEnvironment, DebuggerObject,
                                         DebuggerWasmScript, DebuggerWasmSource };
     using DebuggerAndObject = mozilla::Tuple<NativeObject*, JSObject*, DebuggerObjectKind>;
     using DebuggerAndScript = mozilla::Tuple<NativeObject*, JSScript*>;
     using WrappedType = mozilla::Variant<
         JSObject*,
@@ -164,26 +165,38 @@ struct CrossCompartmentKey
         static HashNumber hash(const CrossCompartmentKey& key) {
             return key.wrapped.match(HashFunctor());
         }
 
         static bool match(const CrossCompartmentKey& l, const CrossCompartmentKey& k) {
             return l.wrapped == k.wrapped;
         }
     };
+
+    bool isTenured() const {
+        struct IsTenuredFunctor {
+            using ReturnType = bool;
+            ReturnType operator()(JSObject** tp) { return !IsInsideNursery(*tp); }
+            ReturnType operator()(JSScript** tp) { return true; }
+            ReturnType operator()(JSString** tp) { return true; }
+        };
+        return const_cast<CrossCompartmentKey*>(this)->applyToWrapped(IsTenuredFunctor());
+    }
+
     void trace(JSTracer* trc);
     bool needsSweep();
 
   private:
     CrossCompartmentKey() = delete;
     WrappedType wrapped;
 };
 
-using WrapperMap = GCRekeyableHashMap<CrossCompartmentKey, ReadBarrieredValue,
-                                      CrossCompartmentKey::Hasher, SystemAllocPolicy>;
+
+using WrapperMap = NurseryAwareHashMap<CrossCompartmentKey, JS::Value,
+                                       CrossCompartmentKey::Hasher, SystemAllocPolicy>;
 
 // We must ensure that all newly allocated JSObjects get their metadata
 // set. However, metadata builders may require the new object be in a sane
 // state (eg, have its reserved slots initialized so they can get the
 // sizeOfExcludingThis of the object). Therefore, for objects of certain
 // JSClasses (those marked with JSCLASS_DELAY_METADATA_BUILDER), it is not safe
 // for the allocation paths to call the object metadata builder
 // immediately. Instead, the JSClass-specific "constructor" C++ function up the
@@ -612,17 +625,17 @@ struct JSCompartment
      * when compacting to update cross-compartment pointers.
      */
     void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
     static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
 
     /* Whether to preserve JIT code on non-shrinking GCs. */
     bool preserveJitCode() { return creationOptions_.preserveJitCode(); }
 
-    void sweepAfterMinorGC();
+    void sweepAfterMinorGC(JSTracer* trc);
 
     void sweepCrossCompartmentWrappers();
     void sweepSavedStacks();
     void sweepGlobalObject(js::FreeOp* fop);
     void sweepSelfHostingScriptSource();
     void sweepJitCompartment(js::FreeOp* fop);
     void sweepRegExps();
     void sweepDebugEnvironments();
@@ -1061,9 +1074,16 @@ class MOZ_RAII AutoSuppressAllocationMet
 
     ~AutoSuppressAllocationMetadataBuilder() {
         zone->suppressAllocationMetadataBuilder = saved;
     }
 };
 
 } /* namespace js */
 
+namespace JS {
+template <>
+struct GCPolicy<js::CrossCompartmentKey> : public StructGCPolicy<js::CrossCompartmentKey> {
+    static bool isTenured(const js::CrossCompartmentKey& key) { return key.isTenured(); }
+};
+} // namespace JS
+
 #endif /* jscompartment_h */
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -105,17 +105,17 @@ JSCompartment::wrap(JSContext* cx, JS::M
 #ifdef DEBUG
     JS::RootedObject cacheResult(cx);
 #endif
     JS::RootedValue v(cx, vp);
     if (js::WrapperMap::Ptr p = crossCompartmentWrappers.lookup(js::CrossCompartmentKey(v))) {
 #ifdef DEBUG
         cacheResult = &p->value().get().toObject();
 #else
-        vp.set(p->value());
+        vp.set(p->value().get());
         return true;
 #endif
     }
 
     JS::RootedObject obj(cx, &vp.toObject());
     if (!wrap(cx, &obj))
         return false;
     vp.setObject(*obj);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3670,17 +3670,17 @@ InCrossCompartmentMap(JSObject* src, JS:
     }
 
     /*
      * If the cross-compartment edge is caused by the debugger, then we don't
      * know the right hashtable key, so we have to iterate.
      */
     for (JSCompartment::WrapperEnum e(srccomp); !e.empty(); e.popFront()) {
         if (e.front().mutableKey().applyToWrapped(IsDestComparatorFunctor(dst)) &&
-            ToMarkable(e.front().value()) == src)
+            ToMarkable(e.front().value().unbarrieredGet()) == src)
         {
             return true;
         }
     }
 
     return false;
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3686,18 +3686,19 @@ JSObject::allocKindForTenure(const js::N
      */
     if (is<TypedArrayObject>() && !as<TypedArrayObject>().hasBuffer()) {
         size_t nbytes = as<TypedArrayObject>().byteLength();
         if (nbytes >= TypedArrayObject::INLINE_BUFFER_LIMIT)
             return GetGCObjectKind(getClass());
         return GetBackgroundAllocKind(TypedArrayObject::AllocKindForLazyBuffer(nbytes));
     }
 
-    // Proxies have finalizers and are not nursery allocated.
-    MOZ_ASSERT(!IsProxy(this));
+    // Proxies that are CrossCompartmentWrappers may be nursery allocated.
+    if (IsProxy(this))
+        return as<ProxyObject>().allocKindForTenure();
 
     // Unboxed plain objects are sized according to the data they store.
     if (is<UnboxedPlainObject>()) {
         size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
         return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
     }
 
     // Unboxed arrays use inline data if their size is small enough.
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1124,19 +1124,25 @@ extern const char js_lookupGetter_str[];
 extern const char js_lookupSetter_str[];
 #endif
 
 namespace js {
 
 inline gc::InitialHeap
 GetInitialHeap(NewObjectKind newKind, const Class* clasp)
 {
+    if (newKind == NurseryAllocatedProxy) {
+        MOZ_ASSERT(clasp->isProxy());
+        MOZ_ASSERT(clasp->hasFinalize());
+        MOZ_ASSERT(!CanNurseryAllocateFinalizedClass(clasp));
+        return gc::DefaultHeap;
+    }
     if (newKind != GenericObject)
         return gc::TenuredHeap;
-    if (clasp->hasFinalize() && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
+    if (clasp->hasFinalize() && !CanNurseryAllocateFinalizedClass(clasp))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
 
 bool
 NewObjectWithTaggedProtoIsCachable(ExclusiveContext* cxArg, Handle<TaggedProto> proto,
                                    NewObjectKind newKind, const Class* clasp);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -345,17 +345,18 @@ JSObject::create(js::ExclusiveContext* c
         MOZ_ASSERT(finalizeFlags == JSCLASS_FOREGROUND_FINALIZE ||
                    finalizeFlags == JSCLASS_BACKGROUND_FINALIZE);
         MOZ_ASSERT((finalizeFlags == JSCLASS_BACKGROUND_FINALIZE) == IsBackgroundFinalized(kind));
     } else {
         MOZ_ASSERT(finalizeFlags == 0);
     }
 
     MOZ_ASSERT_IF(clasp->hasFinalize(), heap == js::gc::TenuredHeap ||
-                                        (flags & JSCLASS_SKIP_NURSERY_FINALIZE));
+                                        CanNurseryAllocateFinalizedClass(clasp) ||
+                                        clasp->isProxy());
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(), heap == js::gc::TenuredHeap);
 #endif
 
     MOZ_ASSERT(!cx->compartment()->hasObjectPendingMetadata());
 
     // Non-native classes cannot have reserved slots or private data, and the
     // objects can't have any fixed slots, for compatibility with
     // GetReservedOrProxyPrivateSlot.
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -210,16 +210,19 @@ class JS_FRIEND_API(CrossCompartmentWrap
     virtual bool hasInstance(JSContext* cx, HandleObject wrapper, MutableHandleValue v,
                              bool* bp) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
 
+    // Allocate CrossCompartmentWrappers in the nursery.
+    virtual bool canNurseryAllocate() const override { return true; }
+
     static const CrossCompartmentWrapper singleton;
     static const CrossCompartmentWrapper singletonWithPrototype;
 };
 
 class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrapper
 {
   public:
     explicit constexpr OpaqueCrossCompartmentWrapper() : CrossCompartmentWrapper(0)
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -135,19 +135,16 @@ else
     fi
     if test -z "$HOST_AR_FLAGS"; then
         HOST_AR_FLAGS='$(AR_FLAGS)'
     fi
 fi
 
 MOZ_TOOL_VARIABLES
 
-AC_PROG_CPP
-AC_PROG_CXXCPP
-
 dnl Special win32 checks
 dnl ========================================================
 
 # Target the Windows 8.1 SDK by default
 WINVER=502
 
 case "$target" in
 *-mingw*)
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -468,16 +468,17 @@ js::IsCrossCompartmentWrapper(JSObject* 
 {
     return IsWrapper(obj) &&
            !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
 void
 js::NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper)
 {
+    MOZ_ASSERT(!IsInsideNursery(wrapper));
     MOZ_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
 
     NotifyGCNukeWrapper(wrapper);
 
     wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
 
     MOZ_ASSERT(IsDeadProxyObject(wrapper));
 }
@@ -494,16 +495,18 @@ JS_FRIEND_API(bool)
 js::NukeCrossCompartmentWrappers(JSContext* cx,
                                  const CompartmentFilter& sourceFilter,
                                  const CompartmentFilter& targetFilter,
                                  js::NukeReferencesToWindow nukeReferencesToWindow)
 {
     CHECK_REQUEST(cx);
     JSRuntime* rt = cx->runtime();
 
+    rt->gc.evictNursery(JS::gcreason::EVICT_NURSERY);
+
     // Iterate through scopes looking for system cross compartment wrappers
     // that point to an object that shares a global with obj.
 
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (!sourceFilter.match(c))
             continue;
 
         // Iterate the wrappers looking for anything interesting.
@@ -537,16 +540,19 @@ js::NukeCrossCompartmentWrappers(JSConte
 // Given a cross-compartment wrapper |wobj|, update it to point to
 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
 // useful even if wrapper already points to newTarget.
 // This operation crashes on failure rather than leaving the heap in an
 // inconsistent state.
 void
 js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
 {
+    MOZ_ASSERT(!IsInsideNursery(wobjArg));
+    MOZ_ASSERT(!IsInsideNursery(newTargetArg));
+
     RootedObject wobj(cx, wobjArg);
     RootedObject newTarget(cx, newTargetArg);
     MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
     MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
     JSObject* origTarget = Wrapper::wrappedObject(wobj);
     MOZ_ASSERT(origTarget);
     Value origv = ObjectValue(*origTarget);
     JSCompartment* wcompartment = wobj->compartment();
@@ -601,16 +607,19 @@ js::RemapWrapper(JSContext* cx, JSObject
 }
 
 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
 // |newTarget|. All wrappers are recomputed.
 JS_FRIEND_API(bool)
 js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
                               JSObject* newTargetArg)
 {
+    MOZ_ASSERT(!IsInsideNursery(oldTargetArg));
+    MOZ_ASSERT(!IsInsideNursery(newTargetArg));
+
     RootedValue origv(cx, ObjectValue(*oldTargetArg));
     RootedObject newTarget(cx, newTargetArg);
 
     AutoWrapperVector toTransplant(cx);
     if (!toTransplant.reserve(cx->runtime()->numCompartments))
         return false;
 
     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
@@ -625,18 +634,20 @@ js::RemapAllWrappersForObject(JSContext*
 
     return true;
 }
 
 JS_FRIEND_API(bool)
 js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
                       const CompartmentFilter& targetFilter)
 {
+    // Drop any nursery-allocated wrappers.
+    cx->runtime()->gc.evictNursery(JS::gcreason::EVICT_NURSERY);
+
     AutoWrapperVector toRecompute(cx);
-
     for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
         // Filter by source compartment.
         if (!sourceFilter.match(c))
             continue;
 
         // Iterate over the wrappers, filtering appropriately.
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             // Filter out non-objects.
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -772,16 +772,17 @@ js::NewProxyObject(JSContext* cx, const 
     }
 
     return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
 }
 
 void
 ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv)
 {
+    MOZ_ASSERT(!IsInsideNursery(this));
     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
     MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
     MOZ_ASSERT(!IsWindowProxy(this));
     MOZ_ASSERT(hasDynamicPrototype());
 
     setHandler(handler);
     setCrossCompartmentPrivate(priv);
     setExtra(0, UndefinedValue());
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/rest-parameter-names.js
@@ -0,0 +1,68 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288460;
+var summary =
+  "Rest parameters to functions can be named |yield| or |eval| or |let| in "
+  "non-strict code";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var f1 = (...yield) => yield + 42;
+assertEq(f1(), "42");
+assertEq(f1(1), "142");
+
+var f2 = (...eval) => eval + 42;
+assertEq(f2(), "42");
+assertEq(f2(1), "142");
+
+var f3 = (...let) => let + 42;
+assertEq(f3(), "42");
+assertEq(f3(1), "142");
+
+function g1(x, ...yield)
+{
+  return yield + x;
+}
+assertEq(g1(0, 42), "420");
+
+function g2(x, ...eval)
+{
+  return eval + x;
+}
+assertEq(g2(0, 42), "420");
+
+function g3(x, ...let)
+{
+  return let + x;
+}
+assertEq(g3(0, 42), "420");
+
+function h()
+{
+  "use strict";
+
+  var badNames = ["yield", "eval", "let"];
+
+  for (var badName of ["yield", "eval", "let"])
+  {
+    assertThrowsInstanceOf(() => eval(`var q = (...${badName}) => ${badName} + 42;`),
+                           SyntaxError);
+
+    assertThrowsInstanceOf(() => eval(`function r(x, ...${badName}) { return x + ${badName}; }`),
+                           SyntaxError);
+  }
+}
+h();
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/compile-lastIndex.js
@@ -0,0 +1,88 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var BUGNUMBER = 1253099;
+var summary =
+  "RegExp.prototype.compile must perform all its steps *except* setting " +
+  ".lastIndex, then throw, when provided a RegExp whose .lastIndex has been " +
+  "made non-writable";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var regex = /foo/i;
+
+// Aside from making .lastIndex non-writable, this has two incidental effects
+// ubiquitously tested through the remainder of this test:
+//
+//   * RegExp.prototype.compile will do everything it ordinarily does, BUT it
+//     will throw a TypeError when attempting to zero .lastIndex immediately
+//     before succeeding overall.
+//   * RegExp.prototype.test for a non-global and non-sticky regular expression,
+//     in case of a match, will return true (as normal).  BUT if no match is
+//     found, it will throw a TypeError when attempting to modify .lastIndex.
+//
+// Ain't it great?
+Object.defineProperty(regex, "lastIndex", { value: 42, writable: false });
+
+assertEq(regex.global, false);
+assertEq(regex.ignoreCase, true);
+assertEq(regex.multiline, false);
+assertEq(regex.unicode, false);
+assertEq(regex.sticky, false);
+assertEq(Object.getOwnPropertyDescriptor(regex, "lastIndex").writable, false);
+assertEq(regex.lastIndex, 42);
+
+assertEq(regex.test("foo"), true);
+assertEq(regex.test("FOO"), true);
+assertThrowsInstanceOf(() => regex.test("bar"), TypeError);
+assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
+
+assertThrowsInstanceOf(() => regex.compile("bar"), TypeError);
+
+assertEq(regex.global, false);
+assertEq(regex.ignoreCase, false);
+assertEq(regex.multiline, false);
+assertEq(regex.unicode, false);
+assertEq(regex.sticky, false);
+assertEq(Object.getOwnPropertyDescriptor(regex, "lastIndex").writable, false);
+assertEq(regex.lastIndex, 42);
+assertThrowsInstanceOf(() => regex.test("foo"), TypeError);
+assertThrowsInstanceOf(() => regex.test("FOO"), TypeError);
+assertEq(regex.test("bar"), true);
+assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
+
+assertThrowsInstanceOf(() => regex.compile("^baz", "m"), TypeError);
+
+assertEq(regex.global, false);
+assertEq(regex.ignoreCase, false);
+assertEq(regex.multiline, true);
+assertEq(regex.unicode, false);
+assertEq(regex.sticky, false);
+assertEq(Object.getOwnPropertyDescriptor(regex, "lastIndex").writable, false);
+assertEq(regex.lastIndex, 42);
+assertThrowsInstanceOf(() => regex.test("foo"), TypeError);
+assertThrowsInstanceOf(() => regex.test("FOO"), TypeError);
+assertThrowsInstanceOf(() => regex.test("bar"), TypeError);
+assertThrowsInstanceOf(() => regex.test("BAR"), TypeError);
+assertEq(regex.test("baz"), true);
+assertThrowsInstanceOf(() => regex.test("BAZ"), TypeError);
+assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901baz"),
+                       TypeError);
+assertEq(regex.test("012345678901234567890123456789012345678901\nbaz"), true);
+assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901BAZ"),
+                       TypeError);
+assertThrowsInstanceOf(() => regex.test("012345678901234567890123456789012345678901\nBAZ"),
+                       TypeError);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Syntax/escaped-let-static-identifier.js
@@ -0,0 +1,57 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1288460;
+var summary =
+  "|let| and |static| are forbidden as Identifier only in strict mode code, " +
+  "and it's permissible to use them as Identifier (with or without " +
+  "containing escapes) in non-strict code";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function t(code)
+{
+  var strictSemi = " 'use strict'; " + code;
+  var strictASI = " 'use strict' \n " + code;
+
+  var creationFunctions = [Function];
+  if (typeof evaluate === "function")
+    creationFunctions.push(evaluate);
+  if (typeof parseModule === "function")
+    creationFunctions.push(parseModule);
+
+  for (var func of creationFunctions)
+  {
+    if (typeof parseModule === "function" && func === parseModule)
+      assertThrowsInstanceOf(() => func(code), SyntaxError);
+    else
+      func(code);
+
+    assertThrowsInstanceOf(() => func(strictSemi), SyntaxError);
+    assertThrowsInstanceOf(() => func(strictASI), SyntaxError);
+  }
+}
+
+t("l\\u0065t: 42;");
+t("if (1) l\\u0065t: 42;");
+t("l\\u0065t = 42;");
+t("if (1) l\\u0065t = 42;");
+
+t("st\\u0061tic: 42;");
+t("if (1) st\\u0061tic: 42;");
+t("st\\u0061tic = 42;");
+t("if (1) st\\u0061tic = 42;");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_6/Syntax/let-as-label.js
+++ b/js/src/tests/ecma_6/Syntax/let-as-label.js
@@ -8,22 +8,28 @@ var BUGNUMBER = 1288459;
 var summary = "let can't be used as a label in strict mode code";
 
 print(BUGNUMBER + ": " + summary);
 
 /**************
  * BEGIN TEST *
  **************/
 
-Function("let: 42")
+Function("let: 42");
+Function("l\\u0065t: 42");
 assertThrowsInstanceOf(() => Function(" 'use strict'; let: 42"), SyntaxError);
 assertThrowsInstanceOf(() => Function(" 'use strict' \n let: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict'; l\\u0065t: 42"), SyntaxError);
+assertThrowsInstanceOf(() => Function(" 'use strict' \n l\\u0065t: 42"), SyntaxError);
 
-eval("let: 42")
+eval("let: 42");
+eval("l\\u0065t: 42");
 assertThrowsInstanceOf(() => eval(" 'use strict'; let: 42"), SyntaxError);
 assertThrowsInstanceOf(() => eval(" 'use strict' \n let: 42;"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict'; l\\u0065t: 42"), SyntaxError);
+assertThrowsInstanceOf(() => eval(" 'use strict' \n l\\u0065t: 42;"), SyntaxError);
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
 print("Tests complete");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/extensions/reentrant-RegExp-creation-and-gc-during-new-RegExp-pattern-ToString.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs gc (newGlobal/evaluate are shimmed)
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile =
+  "reentrant-RegExp-creation-and-gc-during-new-RegExp-pattern-ToString.js";
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1253099;
+var summary =
+  "Don't assert when, in |new RegExp(pat)|, stringifying |pat| creates " +
+  "another RegExp and then performs a GC";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+// The fresh global object is required to ensure that the outer |new RegExp|
+// is the first RegExp created in the global (other than RegExp.prototype).
+newGlobal().evaluate(`
+var createsRegExpAndCallsGCWhenStringified =
+  {
+    toString: function() {
+      new RegExp("a");
+      gc();
+      return "q";
+    }
+  };
+
+assertEq(new RegExp(createsRegExpAndCallsGCWhenStringified).source, "q");
+`);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -619,22 +619,22 @@ WasmArrayRawBuffer::Allocate(uint32_t nu
 /* static */ void
 WasmArrayRawBuffer::Release(void* mem)
 {
     WasmArrayRawBuffer* header = (WasmArrayRawBuffer*)((uint8_t*)mem - sizeof(WasmArrayRawBuffer));
     uint8_t* base = header->basePointer();
     MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize());
     size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize();
 
+    MemProfiler::RemoveNative(base);
 # ifdef XP_WIN
     VirtualFree(base, 0, MEM_RELEASE);
 # else  // XP_WIN
     munmap(base, mappedSizeWithHeader);
 # endif  // !XP_WIN
-    MemProfiler::RemoveNative(base);
 
 #  if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, mappedSizeWithHeader);
 #  endif
 }
 
 WasmArrayRawBuffer*
 ArrayBufferObject::BufferContents::wasmBuffer() const
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6180,17 +6180,17 @@ Debugger::observesFrame(AbstractFramePtr
 
 bool
 Debugger::observesFrame(const FrameIter& iter) const
 {
     // Skip frames not yet fully initialized during their prologue.
     if (iter.isInterp() && iter.isFunctionFrame()) {
         const Value& thisVal = iter.interpFrame()->thisArgument();
         if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING)
-            return false;;
+            return false;
     }
     if (iter.isWasm())
         return false;
     return observesScript(iter.script());
 }
 
 bool
 Debugger::observesScript(JSScript* script) const
@@ -9309,17 +9309,18 @@ DebuggerObject::initClass(JSContext* cx,
 
     return objectProto;
 }
 
 /* static */ DebuggerObject*
 DebuggerObject::create(JSContext* cx, HandleObject proto, HandleObject referent,
                        HandleNativeObject debugger)
 {
-  JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerObject::class_, proto, TenuredObject);
+  NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
+  JSObject* obj = NewObjectWithGivenProto(cx, &DebuggerObject::class_, proto, newKind);
   if (!obj)
     return nullptr;
 
   DebuggerObject& object = obj->as<DebuggerObject>();
   object.setPrivateGCThing(referent);
   object.setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*debugger));
 
   return &object;
@@ -10448,18 +10449,18 @@ DebuggerEnvironment::initClass(JSContext
     return InitClass(cx, dbgCtor, objProto, &DebuggerEnvironment::class_, construct, 0,
                      properties_, methods_, nullptr, nullptr);
 }
 
 /* static */ DebuggerEnvironment*
 DebuggerEnvironment::create(JSContext* cx, HandleObject proto, HandleObject referent,
                             HandleNativeObject debugger)
 {
-    RootedObject obj(cx, NewObjectWithGivenProto(cx, &DebuggerEnvironment::class_, proto,
-                                                 TenuredObject));
+    NewObjectKind newKind = IsInsideNursery(referent) ? GenericObject : TenuredObject;
+    RootedObject obj(cx, NewObjectWithGivenProto(cx, &DebuggerEnvironment::class_, proto, newKind));
     if (!obj)
         return nullptr;
 
     DebuggerEnvironment& environment = obj->as<DebuggerEnvironment>();
     environment.setPrivateGCThing(referent);
     environment.setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
 
     return &environment;
--- a/js/src/vm/Keywords.h
+++ b/js/src/vm/Keywords.h
@@ -50,18 +50,16 @@
     macro(enum, enum_, TOK_RESERVED) \
     /* Future reserved keywords, but only in strict mode. */ \
     macro(implements, implements, TOK_STRICT_RESERVED) \
     macro(interface, interface, TOK_STRICT_RESERVED) \
     macro(package, package, TOK_STRICT_RESERVED) \
     macro(private, private_, TOK_STRICT_RESERVED) \
     macro(protected, protected_, TOK_STRICT_RESERVED) \
     macro(public, public_, TOK_STRICT_RESERVED) \
-    macro(static, static_, TOK_STRICT_RESERVED) \
     /* \
      * Yield is a token inside function*.  Outside of a function*, it is a \
      * future reserved keyword in strict mode, but a keyword in JS1.7 even \
      * when strict.  Punt logic to parser. \
      */ \
-    macro(yield, yield, TOK_YIELD) \
-    macro(let, let, TOK_LET)
+    macro(yield, yield, TOK_YIELD)
 
 #endif /* vm_Keywords_h */
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -42,16 +42,22 @@ enum NewObjectKind {
     /*
      * Singleton objects are treated specially by the type system. This flag
      * ensures that the new object is automatically set up correctly as a
      * singleton and is allocated in the tenured heap.
      */
     SingletonObject,
 
     /*
+     * CrossCompartmentWrappers use the common Proxy class, but are allowed
+     * to have nursery lifetime.
+     */
+    NurseryAllocatedProxy,
+
+    /*
      * Objects which will not benefit from being allocated in the nursery
      * (e.g. because they are known to have a long lifetime) may be allocated
      * with this kind to place them immediately into the tenured generation.
      */
     TenuredObject
 };
 
 /*
--- a/js/src/vm/ProxyObject.cpp
+++ b/js/src/vm/ProxyObject.cpp
@@ -17,33 +17,45 @@ ProxyObject::New(JSContext* cx, const Ba
 {
     Rooted<TaggedProto> proto(cx, proto_);
 
     const Class* clasp = options.clasp();
 
     MOZ_ASSERT(isValidProxyClass(clasp));
     MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
     MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
+    MOZ_ASSERT(clasp->hasFinalize());
 
     /*
      * Eagerly mark properties unknown for proxies, so we don't try to track
      * their properties and so that we don't need to walk the compartment if
      * their prototype changes later.  But don't do this for DOM proxies,
      * because we want to be able to keep track of them in typesets in useful
      * ways.
      */
     if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
         RootedObject protoObj(cx, proto.toObject());
         if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
             return nullptr;
     }
 
-    NewObjectKind newKind = options.singleton() ? SingletonObject : GenericObject;
+    // Ensure that the wrapper has the same lifetime assumptions as the
+    // wrappee. Prefer to allocate in the nursery, when possible.
+    NewObjectKind newKind = NurseryAllocatedProxy;
+    if (options.singleton()) {
+        MOZ_ASSERT(priv.isGCThing() && priv.toGCThing()->isTenured());
+        newKind = SingletonObject;
+    } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
+               !handler->canNurseryAllocate() ||
+               !handler->finalizeInBackground(priv))
+    {
+        newKind = TenuredObject;
+    }
+
     gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
-
     if (handler->finalizeInBackground(priv))
         allocKind = GetBackgroundAllocKind(allocKind);
 
     AutoSetNewObjectMetadata metadata(cx);
     // Note: this will initialize the object's |data| to strange values, but we
     // will immediately overwrite those below.
     RootedObject obj(cx, NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind,
                                                        newKind));
@@ -57,16 +69,40 @@ ProxyObject::New(JSContext* cx, const Ba
 
     /* Don't track types of properties of non-DOM and non-singleton proxies. */
     if (newKind != SingletonObject && !clasp->isDOMClass())
         MarkObjectGroupUnknownProperties(cx, proxy->group());
 
     return proxy;
 }
 
+gc::AllocKind
+ProxyObject::allocKindForTenure() const
+{
+    gc::AllocKind allocKind = gc::GetGCObjectKind(group()->clasp());
+    if (data.handler->finalizeInBackground(const_cast<ProxyObject*>(this)->private_()))
+        allocKind = GetBackgroundAllocKind(allocKind);
+    return allocKind;
+}
+
+/* static */ size_t
+ProxyObject::objectMovedDuringMinorGC(TenuringTracer* trc, JSObject* dst, JSObject* src)
+{
+    ProxyObject& psrc = src->as<ProxyObject>();
+    ProxyObject& pdst = dst->as<ProxyObject>();
+
+    // We're about to sweep the nursery heap, so migrate the inline
+    // ProxyValueArray to the malloc heap if they were nursery allocated.
+    if (trc->runtime()->gc.nursery.isInside(psrc.data.values))
+        pdst.data.values = js_new<detail::ProxyValueArray>(*psrc.data.values);
+    else
+        trc->runtime()->gc.nursery.removeMallocedBuffer(psrc.data.values);
+    return sizeof(detail::ProxyValueArray);
+}
+
 void
 ProxyObject::setCrossCompartmentPrivate(const Value& priv)
 {
     *slotOfPrivate() = priv;
 }
 
 void
 ProxyObject::setSameCompartmentPrivate(const Value& priv)
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -72,16 +72,19 @@ class ProxyObject : public ShapedObject
     const Value& extra(size_t n) const {
         return GetProxyExtra(const_cast<ProxyObject*>(this), n);
     }
 
     void setExtra(size_t n, const Value& extra) {
         SetProxyExtra(this, n, extra);
     }
 
+    gc::AllocKind allocKindForTenure() const;
+    static size_t objectMovedDuringMinorGC(TenuringTracer* trc, JSObject* dst, JSObject* src);
+
   private:
     GCPtrValue* slotOfExtra(size_t n) {
         MOZ_ASSERT(n < detail::PROXY_EXTRA_SLOTS);
         return reinterpret_cast<GCPtrValue*>(&detail::GetProxyDataLayout(this)->values->extraSlots[n]);
     }
 
     static bool isValidProxyClass(const Class* clasp) {
         // Since we can take classes from the outside, make sure that they
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -480,16 +480,19 @@ class RegExpObject : public NativeObject
         MOZ_ASSERT(!maybeShared());
         NativeObject::setPrivate(&shared);
     }
 
     static void trace(JSTracer* trc, JSObject* obj);
 
     void initIgnoringLastIndex(HandleAtom source, RegExpFlag flags);
 
+    // NOTE: This method is *only* safe to call on RegExps that haven't been
+    //       exposed to script, because it requires that the "lastIndex"
+    //       property be writable.
     void initAndZeroLastIndex(HandleAtom source, RegExpFlag flags, ExclusiveContext* cx);
 
   private:
     /*
      * Precondition: the syntax for |source| has already been validated.
      * Side effect: sets the private field.
      */
     bool createShared(JSContext* cx, RegExpGuard* g);
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -842,16 +842,30 @@ TypeSet::Type::singletonNoBarrier() cons
 }
 
 inline ObjectGroup*
 TypeSet::Type::groupNoBarrier() const
 {
     return objectKey()->groupNoBarrier();
 }
 
+inline void
+TypeSet::Type::trace(JSTracer* trc)
+{
+    if (isSingletonUnchecked()) {
+        JSObject* obj = singletonNoBarrier();
+        TraceManuallyBarrieredEdge(trc, &obj, "TypeSet::Object");
+        *this = TypeSet::ObjectType(obj);
+    } else if (isGroupUnchecked()) {
+        ObjectGroup* group = groupNoBarrier();
+        TraceManuallyBarrieredEdge(trc, &group, "TypeSet::Group");
+        *this = TypeSet::ObjectType(group);
+    }
+}
+
 inline bool
 TypeSet::hasType(Type type) const
 {
     if (unknown())
         return true;
 
     if (type.isUnknown()) {
         return false;
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -345,19 +345,17 @@ class TypeSet
         }
         bool isGroupUnchecked() const {
             return isObjectUnchecked() && !(data & 1);
         }
 
         inline ObjectGroup* group() const;
         inline ObjectGroup* groupNoBarrier() const;
 
-        void trace(JSTracer* trc) {
-            MarkTypeUnbarriered(trc, this, "TypeSet::Type");
-        }
+        inline void trace(JSTracer* trc);
 
         bool operator == (Type o) const { return data == o.data; }
         bool operator != (Type o) const { return data != o.data; }
     } JS_HAZ_GC_POINTER;
 
     static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
     static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
     static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
@@ -525,18 +523,16 @@ class TypeSet
 
     static inline bool IsUntrackedValue(const Value& val);
 
     // Get the type of a possibly optimized out or uninitialized let value.
     // This generally only happens on unconditional type monitors on bailing
     // out of Ion, such as for argument and local types.
     static inline Type GetMaybeUntrackedValueType(const Value& val);
 
-    static void MarkTypeRoot(JSTracer* trc, Type* v, const char* name);
-    static void MarkTypeUnbarriered(JSTracer* trc, Type* v, const char* name);
     static bool IsTypeMarked(Type* v);
     static bool IsTypeAllocatedDuringIncremental(Type v);
     static bool IsTypeAboutToBeFinalized(Type* v);
 } JS_HAZ_GC_POINTER;
 
 /*
  * A constraint which listens to additions to a type set and propagates those
  * changes to other type sets.
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -425,16 +425,19 @@ AddAnimationForProperty(nsIFrame* aFrame
   animation->duration() = computedTiming.mDuration;
   animation->iterations() = computedTiming.mIterations;
   animation->iterationStart() = computedTiming.mIterationStart;
   animation->direction() = static_cast<uint8_t>(timing.mDirection);
   animation->property() = aProperty.mProperty;
   animation->playbackRate() = aAnimation->PlaybackRate();
   animation->data() = aData;
   animation->easingFunction() = ToTimingFunction(timing.mFunction);
+  animation->iterationComposite() =
+    static_cast<uint8_t>(aAnimation->GetEffect()->
+                         AsKeyframeEffect()->IterationComposite());
 
   for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
     const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
 
     AnimationSegment* animSegment = animation->segments().AppendElement();
     if (aProperty.mProperty == eCSSProperty_transform) {
       animSegment->startState() = InfallibleTArray<TransformFunction>();
       animSegment->endState() = InfallibleTArray<TransformFunction>();
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1869,16 +1869,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
     }
   }
 
   for (nsIPresShell* shell : mPresShellsToInvalidateIfHidden) {
     shell->InvalidatePresShellIfHidden();
   }
   mPresShellsToInvalidateIfHidden.Clear();
 
+  bool notifyGC = false;
   if (mViewManagerFlushIsPending) {
     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
 
     nsTArray<nsDocShell*> profilingDocShells;
     GetProfileTimelineSubDocShells(GetDocShell(mPresContext), profilingDocShells);
     for (nsDocShell* docShell : profilingDocShells) {
       // For the sake of the profile timeline's simplicity, this is flagged as
       // paint even if it includes creating display lists
@@ -1904,20 +1905,17 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
 #endif
 
     for (nsDocShell* docShell : profilingDocShells) {
       MOZ_ASSERT(timelines);
       MOZ_ASSERT(timelines->HasConsumer(docShell));
       timelines->AddMarkerForDocShell(docShell, "Paint",  MarkerTracingType::END);
     }
 
-    if (nsContentUtils::XPConnect()) {
-      nsContentUtils::XPConnect()->NotifyDidPaint();
-      nsJSContext::NotifyDidPaint();
-    }
+    notifyGC = true;
   }
 
 #ifndef ANDROID  /* bug 1142079 */
   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::REFRESH_DRIVER_TICK, mTickStart);
 #endif
 
   nsTObserverArray<nsAPostRefreshObserver*>::ForwardIterator iter(mPostRefreshObservers);
   while (iter.HasMore()) {
@@ -1927,16 +1925,21 @@ nsRefreshDriver::Tick(int64_t aNowEpoch,
 
   ConfigureHighPrecision();
 
   NS_ASSERTION(mInRefresh, "Still in refresh");
 
   if (mPresContext->IsRoot() && XRE_IsContentProcess() && gfxPrefs::AlwaysPaint()) {
     ScheduleViewManagerFlush();
   }
+
+  if (notifyGC && nsContentUtils::XPConnect()) {
+    nsContentUtils::XPConnect()->NotifyDidPaint();
+    nsJSContext::NotifyDidPaint();
+  }
 }
 
 void
 nsRefreshDriver::BeginRefreshingImages(RequestTable& aEntries,
                                        mozilla::TimeStamp aDesired)
 {
   for (auto iter = aEntries.Iter(); !iter.Done(); iter.Next()) {
     auto req = static_cast<imgIRequest*>(iter.Get()->GetKey());
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1190,28 +1190,28 @@ struct nsGridContainerFrame::Tracks
                                  nscoord                     aPercentageBasis,
                                  IntrinsicISizeType          aConstraint,
                                  const LineRange&            aRange,
                                  const GridItemInfo&         aGridItem);
   /**
    * Collect the tracks which are growable (matching aSelector) into
    * aGrowableTracks, and return the amount of space that can be used
    * to grow those tracks.  Specifically, we return aAvailableSpace minus
-   * the sum of mBase's in aPlan (clamped to 0) for the tracks in aRange,
-   * or zero when there are no growable tracks.
+   * the sum of mBase's (and corresponding grid gaps) in aPlan (clamped to 0)
+   * for the tracks in aRange, or zero when there are no growable tracks.
    * @note aPlan[*].mBase represents a planned new base or limit.
    */
-  static nscoord CollectGrowable(nscoord                    aAvailableSpace,
-                                 const nsTArray<TrackSize>& aPlan,
-                                 const LineRange&           aRange,
-                                 TrackSize::StateBits       aSelector,
-                                 nsTArray<uint32_t>&        aGrowableTracks)
+  nscoord CollectGrowable(nscoord                    aAvailableSpace,
+                          const nsTArray<TrackSize>& aPlan,
+                          const LineRange&           aRange,
+                          TrackSize::StateBits       aSelector,
+                          nsTArray<uint32_t>&        aGrowableTracks) const
   {
     MOZ_ASSERT(aAvailableSpace > 0, "why call me?");
-    nscoord space = aAvailableSpace;
+    nscoord space = aAvailableSpace - mGridGap * (aRange.Extent() - 1);
     const uint32_t start = aRange.mStart;
     const uint32_t end = aRange.mEnd;
     for (uint32_t i = start; i < end; ++i) {
       const TrackSize& sz = aPlan[i];
       space -= sz.mBase;
       if (space <= 0) {
         return 0;
       }
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-track-fit-content-sizing-002-ref.html
@@ -0,0 +1,148 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: fit-content() track sizing</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1299133">
+  <style type="text/css">
+body,html { color:black; background:white; font-size:16px; padding:0; margin:0; }
+
+.grid {
+  display: grid;
+  grid-auto-rows: 5px;
+  justify-content: start;
+  position: relative;
+  border: 1px solid;
+}
+
+.c1 { grid-template-columns: fit-content(40%) 25px minmax(100px, 1fr); }
+.c2 { grid-template-columns: fit-content(40%) 25px 0 25px minmax(100px, 1fr); }
+.c3 { grid-template-columns: fit-content(40%) 25px fit-content(40%) 25px minmax(0px, 1fr); }
+.c4 { grid-template-columns: fit-content(40%) 25px 100px 25px minmax(100px, 1fr); }
+.c5 { grid-template-columns: fit-content(40%) 25px minmax(0, 1fr) 25px minmax(100px, 1fr); }
+.c6 { grid-template-columns: fit-content(calc(1px - 99%)) 25px minmax(0, 1fr) 25px minmax(100px, 1fr); }
+.c7 { grid-template-columns: fit-content(40%) 25px fit-content(40%); }
+
+span {
+  grid-column: 1 / span 3;
+  grid-row: 2;
+  height: 2px;
+  background: lime;
+  min-width: 50px;
+}
+.c1 span { grid-column:1; }
+
+a {
+  display: inline-block;
+  width: 50px;
+  height: 1px;
+}
+
+y {
+    position: absolute;
+    left: 0; right: 0; top: 0;
+    height: 2px;
+    background: purple;
+    grid-column: 1 / 2;
+}
+y:nth-of-type(2n) { background: orange; grid-column: 3 / 4; }
+
+  </style>
+</head>
+<body>
+
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+
+<div style="width:502px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:442px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:382px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:322px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:262px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:202px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c7"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:142px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:82px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:22px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:2px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-track-fit-content-sizing-002.html
@@ -0,0 +1,151 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: fit-content() track sizing</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1299133">
+  <link rel="help" href="https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content">
+  <link rel="match" href="grid-track-fit-content-sizing-002-ref.html">
+  <style type="text/css">
+body,html { color:black; background:white; font-size:16px; padding:0; margin:0; }
+
+.grid {
+  display: grid;
+  grid-auto-rows: 5px;
+  grid-column-gap: 25px;
+  justify-content: start;
+  position: relative;
+  border: 1px solid;
+}
+
+.c1 { grid-template-columns: fit-content(40%) minmax(100px, 1fr); }
+.c2 { grid-template-columns: fit-content(40%) 0 minmax(100px, 1fr); }
+.c3 { grid-template-columns: fit-content(40%) fit-content(40%) minmax(0px, 1fr); }
+.c4 { grid-template-columns: fit-content(40%) 100px minmax(100px, 1fr); }
+.c5 { grid-template-columns: fit-content(40%) minmax(0, 1fr) minmax(100px, 1fr); }
+.c6 { grid-template-columns: fit-content(calc(1px - 99%)) minmax(0, 1fr) minmax(100px, 1fr); }
+.c7 { grid-template-columns: none; grid-auto-columns: fit-content(40%); }
+
+span {
+  grid-column: 1 / span 2;
+  grid-row: 2;
+  height: 2px;
+  background: lime;
+  min-width: 50px;
+}
+.c1 span { grid-column:1; }
+
+a {
+  display: inline-block;
+  width: 50px;
+  height: 1px;
+}
+
+y {
+    position: absolute;
+    left: 0; right: 0; top: 0;
+    height: 2px;
+    background: purple;
+    grid-column: 1 / 2;
+}
+y:nth-of-type(2n) { background: orange; grid-column: 2 / 3; }
+
+  </style>
+</head>
+<body>
+
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+
+<div style="width:502px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:442px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:382px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:322px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:262px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:202px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c7"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:142px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:82px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:22px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c6"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+<div style="width:2px">
+<div class="grid c1"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c2"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c3"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c4"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+<div class="grid c5"><span><a></a><a></a><a></a><a></a></span><y></y><y></y></div>
+</div>
+
+</body>
+</html>
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -58,16 +58,17 @@ fuzzy-if(winWidget,1,36) == grid-auto-mi
 == grid-auto-min-sizing-intrinsic-004.html grid-auto-min-sizing-intrinsic-004-ref.html
 == grid-auto-min-sizing-percent-001.html grid-auto-min-sizing-percent-001-ref.html
 == grid-track-intrinsic-sizing-001.html grid-track-intrinsic-sizing-001-ref.html
 == grid-track-intrinsic-sizing-002.html grid-track-intrinsic-sizing-002-ref.html
 == grid-track-intrinsic-sizing-003.html grid-track-intrinsic-sizing-003-ref.html
 == grid-track-intrinsic-sizing-004.html grid-track-intrinsic-sizing-004-ref.html
 == grid-track-percent-sizing-001.html grid-track-percent-sizing-001-ref.html
 == grid-track-fit-content-sizing-001.html grid-track-fit-content-sizing-001-ref.html
+== grid-track-fit-content-sizing-002.html grid-track-fit-content-sizing-002-ref.html
 == grid-max-sizing-flex-001.html grid-max-sizing-flex-001-ref.html
 == grid-max-sizing-flex-002.html grid-max-sizing-flex-002-ref.html
 == grid-max-sizing-flex-003.html grid-max-sizing-flex-003-ref.html
 == grid-max-sizing-flex-004.html grid-max-sizing-flex-004-ref.html
 == grid-max-sizing-flex-005.html grid-max-sizing-flex-005-ref.html
 == grid-max-sizing-flex-006.html grid-max-sizing-flex-006-ref.html
 == grid-flex-min-sizing-001.html grid-flex-min-sizing-001-ref.html
 == grid-flex-min-sizing-002.html grid-flex-min-sizing-002-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/animation-utils.js
@@ -0,0 +1,13 @@
+function waitForIterationChange(animation) {
+  var initialIteration = animation.effect.getComputedTiming().currentIteration;
+  return new Promise(resolve => {
+    window.requestAnimationFrame(handleFrame = () => {
+      if (animation.effect.getComputedTiming().currentIteration !=
+            initialIteration) {
+        resolve();
+      } else {
+        window.requestAnimationFrame(handleFrame);
+      }
+    });
+  });
+}
--- a/layout/reftests/web-animations/reftest.list
+++ b/layout/reftests/web-animations/reftest.list
@@ -4,8 +4,11 @@ test-pref(dom.animations-api.core.enable
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-none-animation-before-appending-element.html stacking-context-animation-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-opacity-changing-keyframe.html stacking-context-animation-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-opacity-changing-target.html stacking-context-animation-changing-target-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-opacity-changing-effect.html stacking-context-animation-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-changing-keyframe.html stacking-context-animation-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-changing-target.html stacking-context-animation-changing-target-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-changing-effect.html stacking-context-animation-ref.html
 test-pref(dom.animations-api.core.enabled,true) == stacking-context-transform-changing-display-property.html stacking-context-animation-ref.html
+test-pref(dom.animations-api.core.enabled,true) == style-updates-on-iteration-composition-changed-from-accumulate-to-replace.html style-updates-for-iteration-composite-ref.html
+test-pref(dom.animations-api.core.enabled,true) == style-updates-on-iteration-composition-changed-from-replace-to-accumulate.html style-updates-for-iteration-composite-ref.html
+test-pref(dom.animations-api.core.enabled,true) == style-updates-on-current-iteration-changed.html style-updates-for-iteration-composite-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/style-updates-for-iteration-composite-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<title>Reference of testcases for bug 1216843</title>
+<style>
+#test {
+  width: 100px; height: 100px;
+  margin-left: 200px;
+  background: blue;
+}
+</style>
+<div id="test"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/style-updates-on-current-iteration-changed.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Update styles when current iteration changed</title>
+<script src="animation-utils.js"></script>
+<style>
+#test {
+  width: 100px; height: 100px;
+  background: blue;
+}
+</style>
+<div id="test"></div>
+<script>
+  var anim = document.getElementById("test")
+    .animate({ marginLeft: [ "0px", "100px" ] },
+             { duration: 100000,
+               delay: -99999, // For starting right before second iteration.
+               easing: "steps(1, start)",
+               iterations: 2,
+               iterationComposite: "accumulate" });
+
+  waitForIterationChange(anim).then(() => {
+    // Wait for painting the result of the second iteration.
+    requestAnimationFrame(() => {
+      document.documentElement.classList.remove("reftest-wait");
+    });
+  });
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/style-updates-on-iteration-composition-changed-from-accumulate-to-replace.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Update styles when iteration composition changed from accumulate to
+replace</title>
+<script src="animation-utils.js"></script>
+<style>
+#test {
+  width: 100px; height: 100px;
+  background: blue;
+}
+</style>
+<div id="test"></div>
+<script>
+  var anim = document.getElementById("test")
+    .animate({ marginLeft: [ "0px", "200px" ] },
+             { duration: 100000,
+               delay: -99999, // For starting right before second iteration.
+               easing: "steps(1, start)",
+               iterations: 2,
+               iterationComposite: "accumulate" });
+
+  waitForIterationChange(anim).then(() => {
+    // Changing iterationComposite updates the element style.
+    anim.effect.iterationComposite = "replace";
+    requestAnimationFrame(() => {
+      // Now margin-left of the element should be 200px.
+      document.documentElement.classList.remove("reftest-wait");
+    });
+  });
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/web-animations/style-updates-on-iteration-composition-changed-from-replace-to-accumulate.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Update styles when iteration composition changed from replace to
+accumulate</title>
+<script src="animation-utils.js"></script>
+<style>
+#test {
+  width: 100px; height: 100px;
+  background: blue;
+}
+</style>
+<div id="test"></div>
+<script>
+  var anim = document.getElementById("test")
+    .animate({ marginLeft: [ "0px", "100px" ] },
+             { duration: 100000,
+               delay: -99999, // For starting right before second iteration.
+               easing: "steps(1, start)",
+               iterations: 2,
+               iterationComposite: "replace" });
+
+  waitForIterationChange(anim).then(() => {
+    // Changing iterationComposite updates the element style.
+    anim.effect.iterationComposite = "accumulate";
+    requestAnimationFrame(() => {
+      // Now margin-left of the element should be 200px.
+      document.documentElement.classList.remove("reftest-wait");
+    });
+  });
+</script>
--- a/layout/style/GenerateCSSPropsGenerated.py
+++ b/layout/style/GenerateCSSPropsGenerated.py
@@ -5,17 +5,17 @@
 import sys
 import string
 import argparse
 import subprocess
 import buildconfig
 from mozbuild import shellutil
 
 def get_properties(preprocessorHeader):
-    cpp = shellutil.split(buildconfig.substs['CPP'])
+    cpp = list(buildconfig.substs['CPP'])
     cpp += shellutil.split(buildconfig.substs['ACDEFINES'])
     cpp.append(preprocessorHeader)
     preprocessed = subprocess.check_output(cpp)
     properties = [{"name":p[0], "prop":p[1], "id":p[2],
                    "flags":p[3], "pref":p[4], "proptype":p[5]}
                   for (i, p) in enumerate(eval(preprocessed))]
 
     # Sort the list so that longhand and logical properties are intermingled
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Utilities for animation of computed style values */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/RuleNodeCacheConditions.h"
 #include "mozilla/StyleAnimationValue.h"
+#include "mozilla/Tuple.h"
 #include "mozilla/UniquePtr.h"
 #include "nsStyleTransformMatrix.h"
 #include "nsAutoPtr.h"
 #include "nsCOMArray.h"
 #include "nsIStyleRule.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsString.h"
 #include "nsStyleContext.h"
@@ -561,18 +562,18 @@ StyleAnimationValue::ComputeDistance(nsC
       // that we should use Euclidean RGB cube distance.  However, we
       // have to extend that to RGBA.  For now, we'll just use the
       // Euclidean distance in the (part of the) 4-cube of premultiplied
       // colors.
       // FIXME (spec): The CSS transitions spec doesn't say whether
       // colors are premultiplied, but things work better when they are,
       // so use premultiplication.  Spec issue is still open per
       // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
-      nscolor startColor = aStartValue.GetColorValue();
-      nscolor endColor = aEndValue.GetColorValue();
+      nscolor startColor = aStartValue.GetCSSValueValue()->GetColorValue();
+      nscolor endColor = aEndValue.GetCSSValueValue()->GetColorValue();
 
       // Get a color component on a 0-1 scale, which is much easier to
       // deal with when working with alpha.
       #define GET_COMPONENT(component_, color_) \
         (NS_GET_##component_(color_) * (1.0 / 255.0))
 
       double startA = GET_COMPONENT(A, startColor);
       double startR = GET_COMPONENT(R, startColor) * startA;
@@ -1157,82 +1158,151 @@ AddCSSValuePercentNumber(const uint32_t 
   // aCoeff2 is 0, then we'll return the value halfway between 1 and
   // aValue1, rather than the value halfway between 0 and aValue1.
   // Note that we do something similar in AddTransformScale().
   float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
   aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
                         eCSSUnit_Number);
 }
 
-static nscolor
-AddWeightedColors(double aCoeff1, nscolor aColor1,
-                  double aCoeff2, nscolor aColor2)
+// Returns Tuple(Red, Green, Blue, Alpha).
+// Red, Green, and Blue are scaled to the [0, 255] range, and Alpha is scaled
+// to the [0, 1] range (though values are allowed to fall outside of these
+// ranges).
+static Tuple<double, double, double, double>
+GetPremultipliedColorComponents(const nsCSSValue& aValue)
 {
+  // PercentageRGBColor and PercentageRGBAColor component value might be
+  // greater than 1.0 in case when the color value is accumulated, so we
+  // can't use nsCSSValue::GetColorValue() here because that function
+  // clamps its values.
+  if (aValue.GetUnit() == eCSSUnit_PercentageRGBColor ||
+      aValue.GetUnit() == eCSSUnit_PercentageRGBAColor) {
+    nsCSSValueFloatColor* floatColor = aValue.GetFloatColorValue();
+    double alpha = floatColor->Alpha();
+    return MakeTuple(floatColor->Comp1() * 255.0 * alpha,
+                     floatColor->Comp2() * 255.0 * alpha,
+                     floatColor->Comp3() * 255.0 * alpha,
+                     alpha);
+  }
+
+  nscolor color = aValue.GetColorValue();
+  double alpha = NS_GET_A(color) * (1.0 / 255.0);
+  return MakeTuple(NS_GET_R(color) * alpha,
+                   NS_GET_G(color) * alpha,
+                   NS_GET_B(color) * alpha,
+                   alpha);
+}
+
+enum class ColorAdditionType {
+  Clamped, // Clamp each color channel after adding.
+  Unclamped // Do not clamp color channels after adding.
+};
+
+// |aAdditionType| should be Clamped in case of interpolation or SMIL
+// animation (e.g. 'by' attribute). For now, Unclamped is only for
+// accumulation.
+static void
+AddWeightedColors(double aCoeff1, const nsCSSValue& aValue1,
+                  double aCoeff2, const nsCSSValue& aValue2,
+                  ColorAdditionType aAdditionType,
+                  nsCSSValue& aResult)
+{
+  MOZ_ASSERT(aValue1.IsNumericColorUnit() && aValue2.IsNumericColorUnit(),
+             "The unit should be color");
   // FIXME (spec): The CSS transitions spec doesn't say whether
   // colors are premultiplied, but things work better when they are,
   // so use premultiplication.  Spec issue is still open per
   // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
 
   // To save some math, scale the alpha down to a 0-1 scale, but
   // leave the color components on a 0-255 scale.
-  double A1 = NS_GET_A(aColor1) * (1.0 / 255.0);
-  double R1 = NS_GET_R(aColor1) * A1;
-  double G1 = NS_GET_G(aColor1) * A1;
-  double B1 = NS_GET_B(aColor1) * A1;
-  double A2 = NS_GET_A(aColor2) * (1.0 / 255.0);
-  double R2 = NS_GET_R(aColor2) * A2;
-  double G2 = NS_GET_G(aColor2) * A2;
-  double B2 = NS_GET_B(aColor2) * A2;
+
+  double R1, G1, B1, A1;
+  Tie(R1, G1, B1, A1) = GetPremultipliedColorComponents(aValue1);
+  double R2, G2, B2, A2;
+  Tie(R2, G2, B2, A2) = GetPremultipliedColorComponents(aValue2);
   double Aresf = (A1 * aCoeff1 + A2 * aCoeff2);
   if (Aresf <= 0.0) {
-    return NS_RGBA(0, 0, 0, 0);
+    aResult.SetColorValue(NS_RGBA(0, 0, 0, 0));
+    return;
   }
 
   if (Aresf > 1.0) {
     Aresf = 1.0;
   }
 
   double factor = 1.0 / Aresf;
-  uint8_t Ares = NSToIntRound(Aresf * 255.0);
-  uint8_t Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor);
-  uint8_t Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor);
-  uint8_t Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor);
-  return NS_RGBA(Rres, Gres, Bres, Ares);
+  double Rres = (R1 * aCoeff1 + R2 * aCoeff2) * factor;
+  double Gres = (G1 * aCoeff1 + G2 * aCoeff2) * factor;
+  double Bres = (B1 * aCoeff1 + B2 * aCoeff2) * factor;
+
+  if (aAdditionType == ColorAdditionType::Clamped) {
+    aResult.SetColorValue(
+      NS_RGBA(ClampColor(Rres), ClampColor(Gres), ClampColor(Bres),
+              NSToIntRound(Aresf * 255.0)));
+    return;
+  }
+
+  Rres = Rres * (1.0 / 255.0);
+  Gres = Gres * (1.0 / 255.0);
+  Bres = Bres * (1.0 / 255.0);
+
+  aResult.SetFloatColorValue(Rres, Gres, Bres, Aresf,
+                             eCSSUnit_PercentageRGBAColor);
 }
 
-// Multiplies |aColor| by |aDilutionRatio| with premultiplication.
+// Multiplies |aValue| color by |aDilutionRation| with premultiplication.
+// The result is stored in |aResult|.
 // (The logic here should pretty closely match AddWeightedColors()' logic.)
-static nscolor
-DiluteColor(nscolor aColor, double aDilutionRatio)
+static void
+DiluteColor(const nsCSSValue& aValue, double aDilutionRatio,
+            nsCSSValue& aResult)
 {
+  MOZ_ASSERT(aValue.IsNumericColorUnit(), "The unit should be color");
   MOZ_ASSERT(aDilutionRatio >= 0.0 && aDilutionRatio <= 1.0,
              "Dilution ratio should be in [0, 1]");
 
-  double A = NS_GET_A(aColor) * (1.0 / 255.0);
+  // Premultiplication
+  double R, G, B, A;
+  Tie(R, G, B, A) = GetPremultipliedColorComponents(aValue);
   double Aresf = A * aDilutionRatio;
   if (Aresf <= 0.0) {
-    return NS_RGBA(0, 0, 0, 0);
+    aResult.SetColorValue(NS_RGBA(0, 0, 0, 0));
+    return;
   }
 
-  // Premultiplication
-  double R = NS_GET_R(aColor) * A;
-  double G = NS_GET_G(aColor) * A;
-  double B = NS_GET_B(aColor) * A;
-
   double factor = 1.0 / Aresf;
-  return NS_RGBA(ClampColor(R * aDilutionRatio * factor),
-                 ClampColor(G * aDilutionRatio * factor),
-                 ClampColor(B * aDilutionRatio * factor),
-                 NSToIntRound(Aresf * 255.0));
+  aResult.SetColorValue(
+    NS_RGBA(ClampColor(R * aDilutionRatio * factor),
+            ClampColor(G * aDilutionRatio * factor),
+            ClampColor(B * aDilutionRatio * factor),
+            NSToIntRound(Aresf * 255.0)));
 }
 
-static bool
-AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
-               double aCoeff2, const nsCSSValue &aValue2,
-               nsCSSValueList **&aResultTail)
+void
+AppendToCSSValueList(UniquePtr<nsCSSValueList>& aHead,
+                     UniquePtr<nsCSSValueList>&& aValueToAppend,
+                     nsCSSValueList** aTail)
+{
+  MOZ_ASSERT(!aHead == !*aTail,
+             "Can't have head w/o tail, & vice versa");
+
+  if (!aHead) {
+    aHead = Move(aValueToAppend);
+    *aTail = aHead.get();
+  } else {
+    (*aTail) = (*aTail)->mNext = aValueToAppend.release();
+  }
+}
+
+static UniquePtr<nsCSSValueList>
+AddWeightedShadowItems(double aCoeff1, const nsCSSValue &aValue1,
+                       double aCoeff2, const nsCSSValue &aValue2,
+                       ColorAdditionType aColorAdditionType)
 {
   // X, Y, Radius, Spread, Color, Inset
   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Array,
              "wrong unit");
   MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Array,
              "wrong unit");
   nsCSSValue::Array *array1 = aValue1.GetArrayValue();
   nsCSSValue::Array *array2 = aValue2.GetArrayValue();
@@ -1244,46 +1314,42 @@ AddShadowItems(double aCoeff1, const nsC
                      // blur radius must be nonnegative
                      (i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
   }
 
   const nsCSSValue& color1 = array1->Item(4);
   const nsCSSValue& color2 = array2->Item(4);
   const nsCSSValue& inset1 = array1->Item(5);
   const nsCSSValue& inset2 = array2->Item(5);
-  if (color1.GetUnit() != color2.GetUnit() ||
+  if ((color1.GetUnit() != color2.GetUnit() &&
+       (!color1.IsNumericColorUnit() || !color2.IsNumericColorUnit())) ||
       inset1.GetUnit() != inset2.GetUnit()) {
     // We don't know how to animate between color and no-color, or
     // between inset and not-inset.
-    return false;
+    // NOTE: In case when both colors' units are eCSSUnit_Null, that means
+    // neither color value was specified, so we can interpolate.
+    return nullptr;
   }
 
   if (color1.GetUnit() != eCSSUnit_Null) {
-    StyleAnimationValue color1Value
-      (color1.GetColorValue(), StyleAnimationValue::ColorConstructor);
-    StyleAnimationValue color2Value
-      (color2.GetColorValue(), StyleAnimationValue::ColorConstructor);
-    StyleAnimationValue resultColorValue;
-    DebugOnly<bool> ok =
-      StyleAnimationValue::AddWeighted(eCSSProperty_color,
-                                       aCoeff1, color1Value,
-                                       aCoeff2, color2Value,
-                                       resultColorValue);
-    MOZ_ASSERT(ok, "should not fail");
-    resultArray->Item(4).SetColorValue(resultColorValue.GetColorValue());
+    if (aCoeff2 == 0.0 && aCoeff1 != 1.0) {
+      DiluteColor(color1, aCoeff1, resultArray->Item(4));
+    } else {
+      AddWeightedColors(aCoeff1, color1, aCoeff2, color2,
+                        aColorAdditionType,
+                        resultArray->Item(4));
+    }
   }
 
   MOZ_ASSERT(inset1 == inset2, "should match");
   resultArray->Item(5) = inset1;
 
-  nsCSSValueList *resultItem = new nsCSSValueList;
+  auto resultItem = MakeUnique<nsCSSValueList>();
   resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
-  *aResultTail = resultItem;
-  aResultTail = &resultItem->mNext;
-  return true;
+  return resultItem;
 }
 
 static void
 AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1,
                       double aCoeff2, const nsCSSValue &aValue2,
                       nsCSSValue &aResult)
 {
   MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent ||
@@ -1733,38 +1799,39 @@ AddDifferentTransformLists(double aCoeff
 }
 
 static bool
 TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
 {
   return ToPrimitive(func1) == ToPrimitive(func2);
 }
 
-static bool
-AddFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
-                      double aCoeff2, const nsCSSValueList* aList2,
-                      nsCSSValueList**& aResultTail)
+static UniquePtr<nsCSSValueList>
+AddWeightedFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
+                              double aCoeff2, const nsCSSValueList* aList2,
+                              ColorAdditionType aColorAdditionType)
 {
-  // AddFilterFunction should be our only caller, and it should ensure that both
-  // args are non-null.
+  // AddWeightedFilterFunction should be our only caller, and it should ensure
+  // that both args are non-null.
   MOZ_ASSERT(aList1, "expected filter list");
   MOZ_ASSERT(aList2, "expected filter list");
   MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
              "expected function");
   MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
              "expected function");
   RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
                               a2 = aList2->mValue.GetArrayValue();
   nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
-  if (filterFunction != a2->Item(0).GetKeywordValue())
-    return false; // Can't add two filters of different types.
-
-  nsAutoPtr<nsCSSValueList> resultListEntry(new nsCSSValueList);
+  if (filterFunction != a2->Item(0).GetKeywordValue()) {
+    return nullptr; // Can't add two filters of different types.
+  }
+
+  auto resultList = MakeUnique<nsCSSValueList>();
   nsCSSValue::Array* result =
-    resultListEntry->mValue.InitFunction(filterFunction, 1);
+    resultList->mValue.InitFunction(filterFunction, 1);
 
   // "hue-rotate" is the only filter-function that accepts negative values, and
   // we don't use this "restrictions" variable in its clause below.
   const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
   const nsCSSValue& funcArg1 = a1->Item(1);
   const nsCSSValue& funcArg2 = a2->Item(1);
   nsCSSValue& resultArg = result->Item(1);
   float initialVal = 1.0f;
@@ -1777,17 +1844,17 @@ AddFilterFunctionImpl(double aCoeff1, co
         // If units differ, we'll just combine them with calc().
         unit = eCSSUnit_Calc;
       }
       if (!AddCSSValuePixelPercentCalc(restrictions,
                                        unit,
                                        aCoeff1, funcArg1,
                                        aCoeff2, funcArg2,
                                        resultArg)) {
-        return false;
+        return nullptr;
       }
       break;
     }
     case eCSSKeyword_grayscale:
     case eCSSKeyword_invert:
     case eCSSKeyword_sepia:
       initialVal = 0.0f;
       MOZ_FALLTHROUGH;
@@ -1802,61 +1869,62 @@ AddFilterFunctionImpl(double aCoeff1, co
                                initialVal);
       break;
     case eCSSKeyword_hue_rotate:
       AddCSSValueAngle(aCoeff1, funcArg1,
                        aCoeff2, funcArg2,
                        resultArg);
       break;
     case eCSSKeyword_drop_shadow: {
-      nsCSSValueList* resultShadow = resultArg.SetListValue();
-      nsAutoPtr<nsCSSValueList> shadowValue;
-      nsCSSValueList **shadowTail = getter_Transfers(shadowValue);
       MOZ_ASSERT(!funcArg1.GetListValue()->mNext &&
                  !funcArg2.GetListValue()->mNext,
                  "drop-shadow filter func doesn't support lists");
-      if (!AddShadowItems(aCoeff1, funcArg1.GetListValue()->mValue,
-                          aCoeff2, funcArg2.GetListValue()->mValue,
-                          shadowTail)) {
-        return false;
+      UniquePtr<nsCSSValueList> shadowValue =
+        AddWeightedShadowItems(aCoeff1,
+                               funcArg1.GetListValue()->mValue,
+                               aCoeff2,
+                               funcArg2.GetListValue()->mValue,
+                               aColorAdditionType);
+      if (!shadowValue) {
+        return nullptr;
       }
-      *resultShadow = *shadowValue;
+      resultArg.AdoptListValue(Move(shadowValue));
       break;
     }
     default:
       MOZ_ASSERT(false, "unknown filter function");
-      return false;
+      return nullptr;
   }
 
-  *aResultTail = resultListEntry.forget();
-  aResultTail = &(*aResultTail)->mNext;
-
-  return true;
+  return resultList;
 }
 
-static bool
-AddFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
-                  double aCoeff2, const nsCSSValueList* aList2,
-                  nsCSSValueList**& aResultTail)
+static UniquePtr<nsCSSValueList>
+AddWeightedFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
+                          double aCoeff2, const nsCSSValueList* aList2,
+                          ColorAdditionType aColorAdditionType)
 {
   MOZ_ASSERT(aList1 || aList2,
              "one function list item must not be null");
   // Note that one of our arguments could be null, indicating that
   // it's the initial value. Rather than adding special null-handling
   // logic, we just check for null values and replace them with
-  // 0 * the other value. That way, AddFilterFunctionImpl can assume
+  // 0 * the other value. That way, AddWeightedFilterFunctionImpl can assume
   // its args are non-null.
   if (!aList1) {
-    return AddFilterFunctionImpl(aCoeff2, aList2, 0, aList2, aResultTail);
+    return AddWeightedFilterFunctionImpl(aCoeff2, aList2, 0, aList2,
+                                         aColorAdditionType);
   }
   if (!aList2) {
-    return AddFilterFunctionImpl(aCoeff1, aList1, 0, aList1, aResultTail);
+    return AddWeightedFilterFunctionImpl(aCoeff1, aList1, 0, aList1,
+                                         aColorAdditionType);
   }
 
-  return AddFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2, aResultTail);
+  return AddWeightedFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2,
+                                       aColorAdditionType);
 }
 
 static inline uint32_t
 ShapeArgumentCount(nsCSSKeyword aShapeFunction)
 {
   switch (aShapeFunction) {
     case eCSSKeyword_circle:
       return 2; // radius and center point
@@ -2304,16 +2372,108 @@ AddPositionCoords(double aCoeff1, const 
    */
   const nsCSSValue& v1 = posArray1->Item(1);
   const nsCSSValue& v2 = posArray2->Item(1);
   nsCSSValue& vr = resultPosArray->Item(1);
   AddCSSValueCanonicalCalc(aCoeff1, v1,
                            aCoeff2, v2, vr);
 }
 
+static UniquePtr<nsCSSValueList>
+AddWeightedShadowList(double aCoeff1,
+                      const nsCSSValueList* aShadow1,
+                      double aCoeff2,
+                      const nsCSSValueList* aShadow2,
+                      ColorAdditionType aColorAdditionType)
+{
+  // This is implemented according to:
+  // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
+  // and the third item in the summary of:
+  // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
+  UniquePtr<nsCSSValueList> result;
+  nsCSSValueList* tail = nullptr;
+  while (aShadow1 && aShadow2) {
+    UniquePtr<nsCSSValueList> shadowValue =
+      AddWeightedShadowItems(aCoeff1, aShadow1->mValue,
+                             aCoeff2, aShadow2->mValue,
+                             aColorAdditionType);
+    if (!shadowValue) {
+      return nullptr;
+    }
+    aShadow1 = aShadow1->mNext;
+    aShadow2 = aShadow2->mNext;
+    AppendToCSSValueList(result, Move(shadowValue), &tail);
+  }
+  if (aShadow1 || aShadow2) {
+    const nsCSSValueList *longShadow;
+    double longCoeff;
+    if (aShadow1) {
+      longShadow = aShadow1;
+      longCoeff = aCoeff1;
+    } else {
+      longShadow = aShadow2;
+      longCoeff = aCoeff2;
+    }
+
+    while (longShadow) {
+      // Passing coefficients that add to less than 1 produces the
+      // desired result of interpolating "0 0 0 transparent" with
+      // the current shadow.
+      UniquePtr<nsCSSValueList> shadowValue =
+        AddWeightedShadowItems(longCoeff, longShadow->mValue,
+                               0.0, longShadow->mValue,
+                               aColorAdditionType);
+      if (!shadowValue) {
+        return nullptr;
+      }
+
+      longShadow = longShadow->mNext;
+      AppendToCSSValueList(result, Move(shadowValue), &tail);
+    }
+  }
+  return result;
+}
+
+static UniquePtr<nsCSSValueList>
+AddWeightedFilterList(double aCoeff1, const nsCSSValueList* aList1,
+                      double aCoeff2, const nsCSSValueList* aList2,
+                      ColorAdditionType aColorAdditionType)
+{
+  UniquePtr<nsCSSValueList> result;
+  nsCSSValueList* tail = nullptr;
+  while (aList1 || aList2) {
+    if ((aList1 && aList1->mValue.GetUnit() != eCSSUnit_Function) ||
+        (aList2 && aList2->mValue.GetUnit() != eCSSUnit_Function)) {
+      // If we don't have filter-functions, we must have filter-URLs, which
+      // we can't add or interpolate.
+      return nullptr;
+    }
+
+    UniquePtr<nsCSSValueList> resultFunction =
+      AddWeightedFilterFunction(aCoeff1, aList1, aCoeff2, aList2,
+                                aColorAdditionType);
+    if (!resultFunction) {
+      // filter function mismatch
+      return nullptr;
+    }
+
+    AppendToCSSValueList(result, Move(resultFunction), &tail);
+
+    // move to next aList items
+    if (aList1) {
+      aList1 = aList1->mNext;
+    }
+    if (aList2) {
+      aList2 = aList2->mNext;
+    }
+  }
+
+  return result;
+}
+
 bool
 StyleAnimationValue::AddWeighted(nsCSSPropertyID aProperty,
                                  double aCoeff1,
                                  const StyleAnimationValue& aValue1,
                                  double aCoeff2,
                                  const StyleAnimationValue& aValue2,
                                  StyleAnimationValue& aResultValue)
 {
@@ -2403,31 +2563,34 @@ StyleAnimationValue::AddWeighted(nsCSSPr
     }
     case eUnit_Float: {
       aResultValue.SetFloatValue(RestrictValue(aProperty,
         aCoeff1 * aValue1.GetFloatValue() +
         aCoeff2 * aValue2.GetFloatValue()));
       return true;
     }
     case eUnit_Color: {
-      nscolor color1 = aValue1.GetColorValue();
-      nscolor color2 = aValue2.GetColorValue();
-      nscolor resultColor;
+      const nsCSSValue* value1 = aValue1.GetCSSValueValue();
+      const nsCSSValue* value2 = aValue2.GetCSSValueValue();
+      MOZ_ASSERT(value1 && value2, "Both of CSS value should be valid");
+      auto resultColor = MakeUnique<nsCSSValue>();
 
       // We are using AddWeighted() with a zero aCoeff2 for colors to
       // pretend AddWeighted() against transparent color, i.e. rgba(0, 0, 0, 0).
       // But unpremultiplication in AddWeightedColors() does not work well
       // for such cases, so we use another function named DiluteColor() which
       // has a similar logic to AddWeightedColors().
       if (aCoeff2 == 0.0) {
-        resultColor = DiluteColor(color1, aCoeff1);
+        DiluteColor(*value1, aCoeff1, *resultColor);
       } else {
-        resultColor = AddWeightedColors(aCoeff1, color1, aCoeff2, color2);
+        AddWeightedColors(aCoeff1, *value1, aCoeff2, *value2,
+                          ColorAdditionType::Clamped,
+                          *resultColor);
       }
-      aResultValue.SetColorValue(resultColor);
+      aResultValue.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
       return true;
     }
     case eUnit_Calc: {
       PixelCalcValue v1 = ExtractCalcValue(aValue1);
       PixelCalcValue v2 = ExtractCalcValue(aValue2);
       double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
       double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
       bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
@@ -2599,104 +2762,49 @@ StyleAnimationValue::AddWeighted(nsCSSPr
         }
       }
 
       aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
                                                 eUnit_Dasharray);
       return true;
     }
     case eUnit_Shadow: {
-      // This is implemented according to:
-      // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
-      // and the third item in the summary of:
-      // http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
-      const nsCSSValueList *shadow1 = aValue1.GetCSSValueListValue();
-      const nsCSSValueList *shadow2 = aValue2.GetCSSValueListValue();
-      nsAutoPtr<nsCSSValueList> result;
-      nsCSSValueList **resultTail = getter_Transfers(result);
-      while (shadow1 && shadow2) {
-        if (!AddShadowItems(aCoeff1, shadow1->mValue,
-                            aCoeff2, shadow2->mValue,
-                            resultTail)) {
-          return false;
-        }
-        shadow1 = shadow1->mNext;
-        shadow2 = shadow2->mNext;
+      UniquePtr<nsCSSValueList> result =
+        AddWeightedShadowList(aCoeff1,
+                              aValue1.GetCSSValueListValue(),
+                              aCoeff2,
+                              aValue2.GetCSSValueListValue(),
+                              ColorAdditionType::Clamped);
+      if (!result) {
+        return false;
       }
-      if (shadow1 || shadow2) {
-        const nsCSSValueList *longShadow;
-        double longCoeff;
-        if (shadow1) {
-          longShadow = shadow1;
-          longCoeff = aCoeff1;
-        } else {
-          longShadow = shadow2;
-          longCoeff = aCoeff2;
-        }
-
-        while (longShadow) {
-          // Passing coefficients that add to less than 1 produces the
-          // desired result of interpolating "0 0 0 transparent" with
-          // the current shadow.
-          if (!AddShadowItems(longCoeff, longShadow->mValue,
-                              0.0, longShadow->mValue,
-                              resultTail)) {
-            return false;
-          }
-
-          longShadow = longShadow->mNext;
-        }
-      }
-      aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
+      aResultValue.SetAndAdoptCSSValueListValue(result.release(), eUnit_Shadow);
       return true;
     }
     case eUnit_Shape: {
       RefPtr<nsCSSValue::Array> result =
         AddShapeFunction(aProperty,
                          aCoeff1, aValue1.GetCSSValueArrayValue(),
                          aCoeff2, aValue2.GetCSSValueArrayValue());
       if (!result) {
         return false;
       }
       aResultValue.SetCSSValueArrayValue(result, eUnit_Shape);
       return true;
     }
     case eUnit_Filter: {
-      const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
-      const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
-
-      nsAutoPtr<nsCSSValueList> result;
-      nsCSSValueList **resultTail = getter_Transfers(result);
-      while (list1 || list2) {
-        MOZ_ASSERT(!*resultTail,
-          "resultTail isn't pointing to the tail (may leak)");
-        if ((list1 && list1->mValue.GetUnit() != eCSSUnit_Function) ||
-            (list2 && list2->mValue.GetUnit() != eCSSUnit_Function)) {
-          // If we don't have filter-functions, we must have filter-URLs, which
-          // we can't add or interpolate.
-          return false;
-        }
-
-        if (!AddFilterFunction(aCoeff1, list1, aCoeff2, list2, resultTail)) {
-          // filter function mismatch
-          return false;
-        }
-
-        // move to next list items
-        if (list1) {
-          list1 = list1->mNext;
-        }
-        if (list2) {
-          list2 = list2->mNext;
-        }
+      UniquePtr<nsCSSValueList> result =
+        AddWeightedFilterList(aCoeff1, aValue1.GetCSSValueListValue(),
+                              aCoeff2, aValue2.GetCSSValueListValue(),
+                              ColorAdditionType::Clamped);
+      if (!result) {
+        return false;
       }
-      MOZ_ASSERT(!*resultTail,
-                 "resultTail isn't pointing to the tail (may leak)");
-
-      aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
+
+      aResultValue.SetAndAdoptCSSValueListValue(result.release(),
                                                 eUnit_Filter);
       return true;
     }
 
     case eUnit_Transform: {
       const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
       const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
 
@@ -2796,16 +2904,67 @@ StyleAnimationValue::AddWeighted(nsCSSPr
       return true;
     }
   }
 
   MOZ_ASSERT(false, "Can't interpolate using the given common unit");
   return false;
 }
 
+bool
+StyleAnimationValue::Accumulate(nsCSSPropertyID aProperty,
+                                StyleAnimationValue& aDest,
+                                const StyleAnimationValue& aValueToAccumulate,
+                                uint64_t aCount)
+{
+  Unit commonUnit =
+    GetCommonUnit(aProperty, aDest.GetUnit(), aValueToAccumulate.GetUnit());
+  switch (commonUnit) {
+    case eUnit_Filter: {
+      UniquePtr<nsCSSValueList> result =
+        AddWeightedFilterList(1.0, aDest.GetCSSValueListValue(),
+                              aCount, aValueToAccumulate.GetCSSValueListValue(),
+                              ColorAdditionType::Unclamped);
+      if (!result) {
+        return false;
+      }
+
+      aDest.SetAndAdoptCSSValueListValue(result.release(), eUnit_Filter);
+      return true;
+    }
+    case eUnit_Shadow: {
+      UniquePtr<nsCSSValueList> result =
+        AddWeightedShadowList(1.0, aDest.GetCSSValueListValue(),
+                              aCount, aValueToAccumulate.GetCSSValueListValue(),
+                              ColorAdditionType::Unclamped);
+      if (!result) {
+        return false;
+      }
+      aDest.SetAndAdoptCSSValueListValue(result.release(), eUnit_Shadow);
+      return true;
+    }
+    case eUnit_Color: {
+      auto resultColor = MakeUnique<nsCSSValue>();
+      AddWeightedColors(1.0,
+                        *aDest.GetCSSValueValue(),
+                        aCount,
+                        *aValueToAccumulate.GetCSSValueValue(),
+                        ColorAdditionType::Unclamped,
+                        *resultColor);
+
+      aDest.SetAndAdoptCSSValueValue(resultColor.release(), eUnit_Color);
+      return true;
+    }
+    default:
+      return Add(aProperty, aDest, aValueToAccumulate, aCount);
+  }
+  MOZ_ASSERT_UNREACHABLE("Can't accumulate using the given common unit");
+  return false;
+}
+
 already_AddRefed<css::StyleRule>
 BuildStyleRule(nsCSSPropertyID aProperty,
                dom::Element* aTargetElement,
                const nsAString& aSpecifiedValue,
                bool aUseSVGMode)
 {
   // Set up an empty CSS Declaration
   RefPtr<css::Declaration> declaration(new css::Declaration());
@@ -3093,31 +3252,30 @@ StyleAnimationValue::UncomputeValue(nsCS
       break;
     case eUnit_Percent:
       aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
       break;
     case eUnit_Float:
       aSpecifiedValue.
         SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
       break;
-    case eUnit_Color:
-      // colors can be alone, or part of a paint server
-      aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue());
-      break;
     case eUnit_CurrentColor:
       aSpecifiedValue.SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
       break;
     case eUnit_Calc:
+    case eUnit_Color:
     case eUnit_ObjectPosition:
     case eUnit_URL:
     case eUnit_DiscreteCSSValue: {
       nsCSSValue* val = aComputedValue.GetCSSValueValue();
       // Sanity-check that the underlying unit in the nsCSSValue is what we
       // expect for our StyleAnimationValue::Unit:
       MOZ_ASSERT((unit == eUnit_Calc && val->GetUnit() == eCSSUnit_Calc) ||
+                 (unit == eUnit_Color &&
+                  nsCSSValue::IsNumericColorUnit(val->GetUnit())) ||
                  (unit == eUnit_ObjectPosition &&
                   val->GetUnit() == eCSSUnit_Array) ||
                  (unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL) ||
                  unit == eUnit_DiscreteCSSValue,
                  "unexpected unit");
       aSpecifiedValue = *val;
       break;
     }
@@ -4331,17 +4489,18 @@ StyleAnimationValue::StyleAnimationValue
   mUnit = eUnit_Float;
   mValue.mFloat = aFloat;
   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 }
 
 StyleAnimationValue::StyleAnimationValue(nscolor aColor, ColorConstructorType)
 {
   mUnit = eUnit_Color;
-  mValue.mColor = aColor;
+  mValue.mCSSValue = new nsCSSValue();
+  mValue.mCSSValue->SetColorValue(aColor);
 }
 
 StyleAnimationValue&
 StyleAnimationValue::operator=(const StyleAnimationValue& aOther)
 {
   if (this == &aOther) {
     return *this;
   }
@@ -4364,20 +4523,18 @@ StyleAnimationValue::operator=(const Sty
     case eUnit_Coord:
       mValue.mCoord = aOther.mValue.mCoord;
       break;
     case eUnit_Percent:
     case eUnit_Float:
       mValue.mFloat = aOther.mValue.mFloat;
       MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
       break;
+    case eUnit_Calc:
     case eUnit_Color:
-      mValue.mColor = aOther.mValue.mColor;
-      break;
-    case eUnit_Calc:
     case eUnit_ObjectPosition:
     case eUnit_URL:
     case eUnit_DiscreteCSSValue:
       MOZ_ASSERT(IsCSSValueUnit(mUnit),
                  "This clause is for handling nsCSSValue-backed units");
       MOZ_ASSERT(aOther.mValue.mCSSValue, "values may not be null");
       mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
       break;
@@ -4489,17 +4646,18 @@ StyleAnimationValue::SetFloatValue(float
   MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
 }
 
 void
 StyleAnimationValue::SetColorValue(nscolor aColor)
 {
   FreeValue();
   mUnit = eUnit_Color;
-  mValue.mColor = aColor;
+  mValue.mCSSValue = new nsCSSValue();
+  mValue.mCSSValue->SetColorValue(aColor);
 }
 
 void
 StyleAnimationValue::SetCurrentColorValue()
 {
   FreeValue();
   mUnit = eUnit_CurrentColor;
 }
@@ -4643,19 +4801,18 @@ StyleAnimationValue::operator==(const St
     case eUnit_Visibility:
     case eUnit_Integer:
       return mValue.mInt == aOther.mValue.mInt;
     case eUnit_Coord:
       return mValue.mCoord == aOther.mValue.mCoord;
     case eUnit_Percent:
     case eUnit_Float:
       return mValue.mFloat == aOther.mValue.mFloat;
+    case eUnit_Calc:
     case eUnit_Color:
-      return mValue.mColor == aOther.mValue.mColor;
-    case eUnit_Calc:
     case eUnit_ObjectPosition:
     case eUnit_URL:
     case eUnit_DiscreteCSSValue:
       MOZ_ASSERT(IsCSSValueUnit(mUnit),
                  "This clause is for handling nsCSSValue-backed units");
       return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
     case eUnit_CSSValuePair:
       return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -124,16 +124,37 @@ public:
    * positive.
    */
   static MOZ_MUST_USE bool
   AddWeighted(nsCSSPropertyID aProperty,
               double aCoeff1, const StyleAnimationValue& aValue1,
               double aCoeff2, const StyleAnimationValue& aValue2,
               StyleAnimationValue& aResultValue);
 
+  /**
+   * Accumulates |aValueToAccumulate| onto |aDest| |aCount| times.
+   * The result is stored in |aDest| on success.
+   *
+   * @param aDest              The base value to be accumulated.
+   * @param aValueToAccumulate The value to accumulate.
+   * @param aCount             The number of times to accumulate
+   *                           aValueToAccumulate.
+   * @return true on success, false on failure.
+   *
+   * NOTE: This function will work as a wrapper of StyleAnimationValue::Add()
+   * if |aProperty| isn't color or shadow or filter.  For these properties,
+   * this function may return a color value that at least one of its components
+   * has a value which is outside the range [0, 1] so that we can calculate
+   * plausible values as interpolation with the return value.
+   */
+  static MOZ_MUST_USE bool
+  Accumulate(nsCSSPropertyID aProperty, StyleAnimationValue& aDest,
+             const StyleAnimationValue& aValueToAccumulate,
+             uint64_t aCount);
+
   // Type-conversion methods
   // -----------------------
   /**
    * Creates a computed value for the given specified value
    * (property ID + string).  A style context is needed in case the
    * specified value depends on inherited style or on the values of other
    * properties.
    *
@@ -279,17 +300,18 @@ public:
     eUnit_None,
     eUnit_Enumerated,
     eUnit_Visibility, // special case for transitions (which converts
                       // Enumerated to Visibility as needed)
     eUnit_Integer,
     eUnit_Coord,
     eUnit_Percent,
     eUnit_Float,
-    eUnit_Color,
+    eUnit_Color, // nsCSSValue* (never null), always with an nscolor or
+                 // an nsCSSValueFloatColor
     eUnit_CurrentColor,
     eUnit_Calc, // nsCSSValue* (never null), always with a single
                 // calc() expression that's either length or length+percent
     eUnit_ObjectPosition, // nsCSSValue* (never null), always with a
                           // 4-entry nsCSSValue::Array
     eUnit_URL, // nsCSSValue* (never null), always with a css::URLValue
     eUnit_DiscreteCSSValue, // nsCSSValue* (never null)
     eUnit_CSSValuePair, // nsCSSValuePair* (never null)
@@ -306,17 +328,16 @@ public:
   };
 
 private:
   Unit mUnit;
   union {
     int32_t mInt;
     nscoord mCoord;
     float mFloat;
-    nscolor mColor;
     nsCSSValue* mCSSValue;
     nsCSSValuePair* mCSSValuePair;
     nsCSSValueTriplet* mCSSValueTriplet;
     nsCSSRect* mCSSRect;
     nsCSSValue::Array* mCSSValueArray;
     nsCSSValueList* mCSSValueList;
     nsCSSValueSharedList* mCSSValueSharedList;
     nsCSSValuePairList* mCSSValuePairList;
@@ -346,20 +367,16 @@ public:
   float GetPercentValue() const {
     NS_ASSERTION(mUnit == eUnit_Percent, "unit mismatch");
     return mValue.mFloat;
   }
   float GetFloatValue() const {
     NS_ASSERTION(mUnit == eUnit_Float, "unit mismatch");
     return mValue.mFloat;
   }
-  nscolor GetColorValue() const {
-    NS_ASSERTION(mUnit == eUnit_Color, "unit mismatch");
-    return mValue.mColor;
-  }
   nsCSSValue* GetCSSValueValue() const {
     NS_ASSERTION(IsCSSValueUnit(mUnit), "unit mismatch");
     return mValue.mCSSValue;
   }
   nsCSSValuePair* GetCSSValuePairValue() const {
     NS_ASSERTION(IsCSSValuePairUnit(mUnit), "unit mismatch");
     return mValue.mCSSValuePair;
   }
@@ -485,17 +502,18 @@ private:
     return static_cast<char16_t*>(aBuffer->Data());
   }
 
   static bool IsIntUnit(Unit aUnit) {
     return aUnit == eUnit_Enumerated || aUnit == eUnit_Visibility ||
            aUnit == eUnit_Integer;
   }
   static bool IsCSSValueUnit(Unit aUnit) {
-    return aUnit == eUnit_Calc ||
+    return aUnit == eUnit_Color ||
+           aUnit == eUnit_Calc ||
            aUnit == eUnit_ObjectPosition ||
            aUnit == eUnit_URL ||
            aUnit == eUnit_DiscreteCSSValue;
   }
   static bool IsCSSValuePairUnit(Unit aUnit) {
     return aUnit == eUnit_CSSValuePair;
   }
   static bool IsCSSValueTripletUnit(Unit aUnit) {
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -777,17 +777,17 @@ CSSAnimationBuilder::BuildAnimationFrame
       }
     }
 
     // If we have a keyframe at the same offset with the same timing
     // function we should merge our (unique) values into it.
     // Otherwise, we should update the existing keyframe with only the
     // unique properties.
     //
-    // Bug 1216843: We should also match composite modes here.
+    // Bug 1293490: We should also match composite modes here.
     Keyframe* existingKeyframe = nullptr;
     // Don't bother searching for an existing keyframe if we don't
     // have anything to contribute to it.
     if (!uniquePropertyValues.IsEmpty()) {
       for (size_t i = keyframeIdx; i < keyframes.Length(); i++) {
         Keyframe& kf = keyframes[i];
         if (kf.mOffset.value() != currentOffset) {
           break;
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -1479,27 +1479,28 @@ ExtractAnimationValue(nsCSSPropertyID aP
 
 static nscolor
 ExtractColor(nsCSSPropertyID aProperty,
              nsStyleContext *aStyleContext)
 {
   StyleAnimationValue val;
   ExtractAnimationValue(aProperty, aStyleContext, val);
   return val.GetUnit() == StyleAnimationValue::eUnit_CurrentColor
-    ? aStyleContext->StyleColor()->mColor : val.GetColorValue();
+    ? aStyleContext->StyleColor()->mColor
+    : val.GetCSSValueValue()->GetColorValue();
 }
 
 static nscolor
 ExtractColorLenient(nsCSSPropertyID aProperty,
                     nsStyleContext *aStyleContext)
 {
   StyleAnimationValue val;
   ExtractAnimationValue(aProperty, aStyleContext, val);
   if (val.GetUnit() == StyleAnimationValue::eUnit_Color) {
-    return val.GetColorValue();
+    return val.GetCSSValueValue()->GetColorValue();
   } else if (val.GetUnit() == StyleAnimationValue::eUnit_CurrentColor) {
     return aStyleContext->StyleColor()->mColor;
   }
   return NS_RGBA(0, 0, 0, 0);
 }
 
 struct ColorIndexSet {
   uint8_t colorIndex, alphaIndex;
--- a/old-configure.in
+++ b/old-configure.in
@@ -236,19 +236,16 @@ else
 fi
 
 if test -n "$MOZ_WINCONSOLE"; then
     AC_DEFINE(MOZ_WINCONSOLE)
 fi
 
 MOZ_TOOL_VARIABLES
 
-AC_PROG_CPP
-AC_PROG_CXXCPP
-
 dnl ========================================================
 dnl Special win32 checks
 dnl ========================================================
 
 WINVER=502
 
 case "$target" in
 *-mingw*)
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -11405,20 +11405,16 @@
         "path": "IndexedDB/idbcursor-advance.htm",
         "url": "/IndexedDB/idbcursor-advance.htm"
       },
       {
         "path": "IndexedDB/idbcursor-continue.htm",
         "url": "/IndexedDB/idbcursor-continue.htm"
       },
       {
-        "path": "IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm",
-        "url": "/IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm"
-      },
-      {
         "path": "IndexedDB/idbcursor-continuePrimaryKey.htm",
         "url": "/IndexedDB/idbcursor-continuePrimaryKey.htm"
       },
       {
         "path": "IndexedDB/idbcursor-direction-index-keyrange.htm",
         "url": "/IndexedDB/idbcursor-direction-index-keyrange.htm"
       },
       {
@@ -35717,16 +35713,21 @@
         "path": "workers/semantics/xhr/005.html",
         "url": "/workers/semantics/xhr/005.html"
       },
       {
         "path": "workers/semantics/xhr/006.html",
         "url": "/workers/semantics/xhr/006.html"
       },
       {
+        "path": "IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm",
+        "timeout": "long",
+        "url": "/IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm"
+      },
+      {
         "path": "IndexedDB/idbdatabase_createObjectStore10-1000ends.htm",
         "timeout": "long",
         "url": "/IndexedDB/idbdatabase_createObjectStore10-1000ends.htm"
       },
       {
         "path": "IndexedDB/idbdatabase_createObjectStore8-parameters.htm",
         "timeout": "long",
         "url": "/IndexedDB/idbdatabase_createObjectStore8-parameters.htm"
@@ -37879,16 +37880,22 @@
           }
         ],
         "web-animations/interfaces/Animation/effect.html": [
           {
             "path": "web-animations/interfaces/Animation/effect.html",
             "url": "/web-animations/interfaces/Animation/effect.html"
           }
         ],
+        "web-animations/interfaces/KeyframeEffect/iterationComposite.html": [
+          {
+            "path": "web-animations/interfaces/KeyframeEffect/iterationComposite.html",
+            "url": "/web-animations/interfaces/KeyframeEffect/iterationComposite.html"
+          }
+        ],
         "web-animations/interfaces/KeyframeEffect/spacing.html": [
           {
             "path": "web-animations/interfaces/KeyframeEffect/spacing.html",
             "url": "/web-animations/interfaces/KeyframeEffect/spacing.html"
           }
         ],
         "web-animations/timing-model/animation-effects/phases-and-states.html": [
           {
--- a/testing/web-platform/meta/web-animations/interfaces/Animatable/animate.html.ini
+++ b/testing/web-platform/meta/web-animations/interfaces/Animatable/animate.html.ini
@@ -11,28 +11,28 @@
   [Element.animate() accepts a one property two value property-indexed keyframes specification where the first value is invalid]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [Element.animate() accepts a one property two value property-indexed keyframes specification where the second value is invalid]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
-  [Element.animate() accepts a keyframe sequence with different composite values, but the same composite value for a given offset]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
-
   [Element.animate() accepts a one property one keyframe sequence]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [Element.animate() accepts a single keyframe sequence with omitted offsets]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
+  [Element.animate() accepts a keyframe sequence with different composite values, but the same composite value for a given offset]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1291468
+
   [Element.animate() accepts a two property keyframe sequence where one property is missing from the first keyframe]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [Element.animate() accepts a two property keyframe sequence where one property is missing from the last keyframe]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
--- a/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/constructor.html.ini
+++ b/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/constructor.html.ini
@@ -1,25 +1,21 @@
 [constructor.html]
   type: testharness
   [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in property-indexed keyframes]
     expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1291468
 
   [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in regular keyframes]
     expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1291468
 
   [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in KeyframeTimingOptions]
     expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
-
-  [a KeyframeEffectReadOnly can be constructed with a keyframe sequence with different composite values, but the same composite value for a given offset]
-    expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [a KeyframeEffectReadOnly can be constructed with a one property one value property-indexed keyframes specification]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [a KeyframeEffectReadOnly constructed with a one property one value property-indexed keyframes specification roundtrips]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
@@ -59,16 +55,20 @@
   [a KeyframeEffectReadOnly can be constructed with a single keyframe sequence with omitted offsets]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [a KeyframeEffectReadOnly constructed with a single keyframe sequence with omitted offsets roundtrips]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
+  [a KeyframeEffectReadOnly can be constructed with a keyframe sequence with different composite values, but the same composite value for a given offset]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1291468
+
   [a KeyframeEffectReadOnly can be constructed with a two property keyframe sequence where one property is missing from the first keyframe]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [a KeyframeEffectReadOnly constructed with a two property keyframe sequence where one property is missing from the first keyframe roundtrips]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
--- a/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/setKeyframes.html.ini
+++ b/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/setKeyframes.html.ini
@@ -11,29 +11,29 @@
   [Keyframes can be replaced with a one property two value property-indexed keyframes specification where the first value is invalid]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [Keyframes can be replaced with a one property two value property-indexed keyframes specification where the second value is invalid]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
+  [Keyframes can be replaced with a one property one keyframe sequence]
+    expected: FAIL
+
+  [Keyframes can be replaced with a single keyframe sequence with omitted offsets]
+    expected: FAIL
+
   [Keyframes can be replaced with a keyframe sequence with different composite values, but the same composite value for a given offset]
     expected: FAIL
-    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1291468
 
   [Keyframes can be replaced with a two property keyframe sequence where one property is missing from the first keyframe]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
   [Keyframes can be replaced with a two property keyframe sequence where one property is missing from the last keyframe]
     expected: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
 
-  [Keyframes can be replaced with a one property one keyframe sequence]
-    expected: FAIL
-
-  [Keyframes can be replaced with a single keyframe sequence with omitted offsets]
-    expected: FAIL
-
   [Keyframes can be replaced with a single keyframe sequence with string offset]
     expected: FAIL
 
--- a/testing/web-platform/tests/IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm
+++ b/testing/web-platform/tests/IndexedDB/idbcursor-continuePrimaryKey-exception-order.htm
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
+<meta name="timeout" content="long">
 <title>IDBCursor.continuePrimaryKey() - Exception Orders </title>
 <link rel="author" title="Mozilla" href="https://www.mozilla.org">
 <link rel="help" href="http://w3c.github.io/IndexedDB/#dom-idbcursor-continueprimarykey">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="support.js"></script>
 
 <script>
--- a/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm
+++ b/testing/web-platform/tests/XMLHttpRequest/responsexml-document-properties.htm
@@ -5,17 +5,17 @@
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
     <link rel="help" href="https://xhr.spec.whatwg.org/#the-responsexml-attribute" data-tested-assertations="following::ol[1]/li[4]" />
     <link rel="help" href="https://xhr.spec.whatwg.org/#document-response-entity-body" data-tested-assertations="following::ol[1]/li[6] following::ol[1]/li[7] following::ol[1]/li[8] following::ol[1]/li[10]" />
   </head>
   <body>
     <div id="log"></div>
     <script>
-      var timePreXHR = Math.floor(new Date().getTime() / 1000);
+      var timePreXHR = Math.floor(new Date().getTime(new Date().getTime() - 3000) / 1000); // three seconds ago, in case there's clock drift
       var client = new XMLHttpRequest()
       client.open("GET", "resources/well-formed.xml", false)
       client.send(null)
       var expected = {
         domain:undefined,
         URL:location.href.replace(/[^/]*$/, 'resources/well-formed.xml'),
         documentURI:location.href.replace(/[^/]*$/, 'resources/well-formed.xml'),
         referrer:'',
@@ -40,17 +40,17 @@
       function runTest(name, value){
         test(function(){
           assert_equals(client.responseXML[name], value)
         }, name)
       }
 
       test(function() {
         var lastModified = Math.floor(new Date(client.responseXML.lastModified).getTime() / 1000);
-        var now = Math.floor(new Date().getTime(new Date().getTime() + 2000) / 1000); // two seconds from now, in case there's clock drift
+        var now = Math.floor(new Date().getTime(new Date().getTime() + 3000) / 1000); // three seconds from now, in case there's clock drift
         assert_greater_than_equal(lastModified, timePreXHR);
         assert_less_than_equal(lastModified, now);
       }, 'lastModified set to time of response if no HTTP header provided')
 
       test(function() {
         var client2 = new XMLHttpRequest()
         client2.open("GET", "resources/last-modified.py", false)
         client2.send(null)
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/iterationComposite.html
@@ -0,0 +1,693 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>KeyframeEffect.iterationComposite tests</title>
+<link rel="help" href="https://w3c.github.io/web-animations/#effect-accumulation-section">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="../../testcommon.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim =
+    div.animate({ marginLeft: ['0px', '10px'] },
+                { duration: 100 * MS_PER_SEC,
+                  easing: 'linear',
+                  iterations: 10,
+                  iterationComposite: 'accumulate' });
+  anim.pause();
+
+  anim.currentTime = anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).marginLeft, '5px',
+    'Animated margin-left style at 50s of the first iteration');
+  anim.currentTime = anim.effect.timing.duration * 2;
+  assert_equals(getComputedStyle(div).marginLeft, '20px',
+    'Animated margin-left style at 0s of the third iteration');
+  anim.currentTime += anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).marginLeft, '25px',
+    'Animated margin-left style at 50s of the third iteration');
+}, 'iterationComposite of <length> type animation');
+
+test(function(t) {
+  var parent = createDiv(t);
+  parent.style.width = '100px';
+  var div = createDiv(t);
+  parent.appendChild(div);
+
+  var anim =
+    div.animate({ width: ['0%', '50%'] },
+                { duration: 100 * MS_PER_SEC,
+                  easing: 'linear',
+                  iterations: 10,
+                  iterationComposite: 'accumulate' });
+  anim.pause();
+
+  anim.currentTime = anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).width, '25px',
+    'Animated width style at 50s of the first iteration');
+  anim.currentTime = anim.effect.timing.duration * 2;
+  assert_equals(getComputedStyle(div).width, '100px',
+    'Animated width style at 0s of the third iteration');
+  anim.currentTime += anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).width, '125px',
+    'Animated width style at 50s of the third iteration');
+}, 'iterationComposite of <percentage> type animation');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim =
+    div.animate({ color: ['rgb(0, 0, 0)', 'rgb(120, 120, 120)'] },
+                { duration: 100 * MS_PER_SEC,
+                  easing: 'linear',
+                  iterations: 10,
+                  iterationComposite: 'accumulate' });
+  anim.pause();
+
+  anim.currentTime = anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).color, 'rgb(60, 60, 60)',
+    'Animated color style at 50s of the first iteration');
+  anim.currentTime = anim.effect.timing.duration * 2;
+  assert_equals(getComputedStyle(div).color, 'rgb(240, 240, 240)',
+    'Animated color style at 0s of the third iteration');
+  anim.currentTime += anim.effect.timing.duration / 2;
+  assert_equals(getComputedStyle(div).color, 'rgb(255, 255, 255)',
+    'Animated color style at 50s of the third iteration');
+}, 'iterationComposite of <color> type animation');
+
+test(function(t) {
+  var div = createDiv(t);
+  var anim =
+    div.animate({ color: ['rgb(0, 120, 0)', 'rgb(60, 60, 60)'] },
+                { duration: 100 * MS_PER_SEC,
+                  easing: 'linear',
+                  iterations: 10,
+                  iterationComposite: 'accumulate' });
+  anim.pause();