merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 01 Nov 2017 22:55:12 +0100
changeset 442940 e81094853e1d7035db30a54deaf60a0108924b85
parent 442868 b022ff43f4bb93dd4198a0ddc6a7cd9b5765ad6e (current diff)
parent 442939 bc99deed64c5750a5df5d28fc4a5c55f0abce6c8 (diff)
child 442941 3ab165aac87dade7b058b4e91e739766dc11849c
child 442971 71a9ff2acdd8a9e5af4871e8ff2826f83de79bbb
child 443033 33221afe200c3caa8a35b41f104637644b6becf3
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
e81094853e1d / 58.0a1 / 20171101220120 / files
nightly linux64
e81094853e1d / 58.0a1 / 20171101220120 / files
nightly mac
e81094853e1d / 58.0a1 / 20171101220120 / files
nightly win32
e81094853e1d / 58.0a1 / 20171101220120 / files
nightly win64
e81094853e1d / 58.0a1 / 20171101220120 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: GSDYk9wLOAM
browser/modules/test/browser/formValidation/browser_form_validation.js
browser/modules/test/browser/formValidation/browser_validation_iframe.js
browser/modules/test/browser/formValidation/browser_validation_invisible.js
image/imgIOnloadBlocker.idl
js/src/jit-test/tests/auto-regress/bug521694.js
js/src/jit-test/tests/auto-regress/bug622167.js
js/src/jit-test/tests/auto-regress/bug678090.js
js/src/jit-test/tests/auto-regress/bug726799.js
js/src/jit-test/tests/basic/bug528644.js
js/src/jit-test/tests/basic/testBrandedVsGeneric.js
js/src/jit-test/tests/basic/testBug603193.js
js/src/jit-test/tests/basic/testBug704351.js
js/src/jit-test/tests/basic/testBug705879.js
js/src/jit-test/tests/debug/onExceptionUnwind-11.js
js/src/jit-test/tests/generators/bug1151326.js
js/src/jit-test/tests/jaeger/bug552644.js
js/src/jit-test/tests/jaeger/recompile/bug673812.js
js/src/jit-test/tests/jaeger/testBug550743.js
js/src/jit-test/tests/parser/bug-1263355-25.js
js/src/jit-test/tests/parser/legacy-generator-warn.js
js/src/tests/js1_7/GC/regress-341675.js
js/src/tests/js1_7/extensions/regress-352885-01.js
js/src/tests/js1_7/extensions/regress-352885-02.js
js/src/tests/js1_7/extensions/regress-355512.js
js/src/tests/js1_7/extensions/regress-372364.js
js/src/tests/js1_7/extensions/regress-387955-01.js
js/src/tests/js1_7/extensions/regress-387955-02.js
js/src/tests/js1_7/extensions/regress-392308.js
js/src/tests/js1_7/geniter/close-returns-undefined.js
js/src/tests/js1_7/geniter/message-value-passing.js
js/src/tests/js1_7/geniter/multiple-close.js
js/src/tests/js1_7/geniter/regress-345879-02.js
js/src/tests/js1_7/geniter/regress-349012-02.js
js/src/tests/js1_7/geniter/regress-349012-03.js
js/src/tests/js1_7/geniter/regress-349012-04.js
js/src/tests/js1_7/geniter/regress-349012-05.js
js/src/tests/js1_7/geniter/regress-349023-01.js
js/src/tests/js1_7/geniter/regress-349023-02.js
js/src/tests/js1_7/geniter/regress-349023-03.js
js/src/tests/js1_7/geniter/regress-350621.js
js/src/tests/js1_7/geniter/regress-352197.js
js/src/tests/js1_7/geniter/regress-352876.js
js/src/tests/js1_7/geniter/regress-355834.js
js/src/tests/js1_7/geniter/regress-382335.js
js/src/tests/js1_7/geniter/regress-387871.js
js/src/tests/js1_7/geniter/regress-390918.js
js/src/tests/js1_7/geniter/regress-392310.js
js/src/tests/js1_7/geniter/send-no-rhs.js
js/src/tests/js1_7/geniter/sequential-yields.js
js/src/tests/js1_7/geniter/throw-after-close.js
js/src/tests/js1_7/geniter/throw-forever.js
js/src/tests/js1_7/geniter/unreachable-yield.js
js/src/tests/js1_7/geniter/yield-new.js
js/src/tests/js1_7/geniter/yield-undefined.js
js/src/tests/js1_7/iterable/regress-341499.js
js/src/tests/js1_7/iterable/regress-341510.js
js/src/tests/js1_7/regress/regress-372331.js
js/src/tests/js1_7/regress/regress-385133-02.js
js/src/tests/js1_7/regress/regress-416601.js
js/src/tests/js1_7/regress/regress-419803.js
js/src/tests/js1_7/regress/regress-453411.js
js/src/tests/js1_8/genexps/regress-347739.js
js/src/tests/js1_8/genexps/regress-349012-01.js
js/src/tests/js1_8/genexps/regress-349326.js
js/src/tests/js1_8/genexps/regress-665286.js
js/src/tests/js1_8/genexps/regress-683738.js
js/src/tests/js1_8/regress/regress-366941.js
js/src/tests/js1_8/regress/regress-384412.js
js/src/tests/js1_8/regress/regress-461930.js
js/src/tests/js1_8/regress/regress-461932.js
js/src/tests/js1_8/regress/regress-463334-01.js
js/src/tests/js1_8/regress/regress-463334-02.js
js/src/tests/js1_8/regress/regress-465460-09.js
js/src/tests/js1_8/regress/regress-471373.js
js/src/tests/js1_8/regress/regress-477581.js
js/src/tests/js1_8_1/regress/regress-524264.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java
mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
mobile/android/modules/JNI.jsm
mobile/android/tests/browser/chrome/test_jni.html
old-configure.in
taskcluster/taskgraph/transforms/tests.py
toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
widget/android/AndroidJNI.cpp
widget/android/AndroidJNIWrapper.cpp
widget/android/AndroidJNIWrapper.h
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
--- a/accessible/base/TextAttrs.h
+++ b/accessible/base/TextAttrs.h
@@ -394,17 +394,19 @@ protected:
    * TextDecorTextAttr class is used for the work with
    * "text-line-through-style", "text-line-through-color",
    * "text-underline-style" and "text-underline-color" text attributes.
    */
 
   class TextDecorValue
   {
   public:
-    TextDecorValue() { }
+    TextDecorValue() :
+      mColor{0}, mLine{NS_STYLE_TEXT_DECORATION_LINE_NONE},
+      mStyle{NS_STYLE_TEXT_DECORATION_STYLE_NONE} { }
     explicit TextDecorValue(nsIFrame* aFrame);
 
     nscolor Color() const { return mColor; }
     uint8_t Style() const { return mStyle; }
 
     bool IsDefined() const
       { return IsUnderline() || IsLineThrough(); }
     bool IsUnderline() const
--- a/accessible/base/TextRange.h
+++ b/accessible/base/TextRange.h
@@ -42,17 +42,17 @@ struct TextPoint final
  * Represents a text range within the text control or document.
  */
 class TextRange final
 {
 public:
   TextRange(HyperTextAccessible* aRoot,
             HyperTextAccessible* aStartContainer, int32_t aStartOffset,
             HyperTextAccessible* aEndContainer, int32_t aEndOffset);
-  TextRange() {}
+  TextRange() : mStartOffset{0}, mEndOffset{0} {}
   TextRange(TextRange&& aRange) :
     mRoot(mozilla::Move(aRange.mRoot)),
     mStartContainer(mozilla::Move(aRange.mStartContainer)),
     mEndContainer(mozilla::Move(aRange.mEndContainer)),
     mStartOffset(aRange.mStartOffset), mEndOffset(aRange.mEndOffset) {}
 
   TextRange& operator= (TextRange&& aRange)
   {
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -15,18 +15,19 @@ using namespace mozilla::a11y;
 
 
 /**
  * An object that stores a given traversal rule during the pivot movement.
  */
 class RuleCache
 {
 public:
-  explicit RuleCache(nsIAccessibleTraversalRule* aRule) : mRule(aRule),
-                                                          mAcceptRoles(nullptr) { }
+  explicit RuleCache(nsIAccessibleTraversalRule* aRule) :
+    mRule(aRule), mAcceptRoles(nullptr),
+    mAcceptRolesLength{0}, mPreFilter{0} { }
   ~RuleCache () {
     if (mAcceptRoles)
       free(mAcceptRoles);
   }
 
   nsresult ApplyFilter(Accessible* aAccessible, uint16_t* aResult);
 
 private:
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -89,17 +89,17 @@ DocAccessible::
     // confusing leak checking machinary.
   HyperTextAccessibleWrap(nullptr, nullptr),
   // XXX aaronl should we use an algorithm for the initial cache size?
   mAccessibleCache(kDefaultCacheLength),
   mNodeToAccessibleMap(kDefaultCacheLength),
   mDocumentNode(aDocument),
   mScrollPositionChangedTicks(0),
   mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
-  mVirtualCursor(nullptr),
+  mARIAAttrOldValue{nullptr}, mVirtualCursor(nullptr),
   mPresShell(aPresShell), mIPCDoc(nullptr)
 {
   mGenericTypes |= eDocument;
   mStateFlags |= eNotNodeMapEntry;
   mDoc = this;
 
   MOZ_ASSERT(mPresShell, "should have been given a pres shell");
   mPresShell->SetDocAccessible(this);
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -458,19 +458,19 @@ Section "-Application" APP_IDX
   ; exists in the shortcuts_log.ini and the SMPROGRAMS. The installer's shortcut
   ; creation code will create the shortcut in the root of the Start Menu
   ; Programs directory.
   ${RemoveStartMenuDir}
 
   ; Always add the application's shortcuts to the shortcuts log ini file. The
   ; DeleteShortcuts macro will do the right thing on uninstall if the
   ; shortcuts don't exist.
-  ${LogStartMenuShortcut} "${BrandFullName}.lnk"
-  ${LogQuickLaunchShortcut} "${BrandFullName}.lnk"
-  ${LogDesktopShortcut} "${BrandFullName}.lnk"
+  ${LogStartMenuShortcut} "${BrandShortName}.lnk"
+  ${LogQuickLaunchShortcut} "${BrandShortName}.lnk"
+  ${LogDesktopShortcut} "${BrandShortName}.lnk"
 
   ; Best effort to update the Win7 taskbar and start menu shortcut app model
   ; id's. The possible contexts are current user / system and the user that
   ; elevated the installer.
   Call FixShortcutAppModelIDs
   ; If the current context is all also perform Win7 taskbar and start menu link
   ; maintenance for the current user context.
   ${If} $TmpVal == "HKLM"
@@ -490,27 +490,27 @@ Section "-Application" APP_IDX
     UAC::ExecCodeSegment $0
   ${EndUnless}
 
   ; UAC only allows elevating to an Admin account so there is no need to add
   ; the Start Menu or Desktop shortcuts from the original unelevated process
   ; since this will either add it for the user if unelevated or All Users if
   ; elevated.
   ${If} $AddStartMenuSC == 1
-    CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-    ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-      ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
+    CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+    ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+      ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
                                            "$INSTDIR"
       ${If} ${AtLeastWin7}
       ${AndIf} "$AppUserModelID" != ""
-        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID" "true"
+        ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true"
       ${EndIf}
-      ${LogMsg} "Added Shortcut: $SMPROGRAMS\${BrandFullName}.lnk"
+      ${LogMsg} "Added Shortcut: $SMPROGRAMS\${BrandShortName}.lnk"
     ${Else}
-      ${LogMsg} "** ERROR Adding Shortcut: $SMPROGRAMS\${BrandFullName}.lnk"
+      ${LogMsg} "** ERROR Adding Shortcut: $SMPROGRAMS\${BrandShortName}.lnk"
     ${EndIf}
   ${EndIf}
 
   ; Update lastwritetime of the Start Menu shortcut to clear the tile cache.
   ; Do this for both shell contexts in case the user has shortcuts in multiple
   ; locations, then restore the previous context at the end.
   ${If} ${AtLeastWin8}
     SetShellVarContext all
@@ -520,45 +520,45 @@ Section "-Application" APP_IDX
     ${If} $TmpVal == "HKLM"
       SetShellVarContext all
     ${ElseIf} $TmpVal == "HKCU"
       SetShellVarContext current
     ${EndIf}
   ${EndIf}
 
   ${If} $AddDesktopSC == 1
-    CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-    ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-      ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" \
+    CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+    ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+      ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" \
                                              "$INSTDIR"
       ${If} ${AtLeastWin7}
       ${AndIf} "$AppUserModelID" != ""
-        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID"  "true"
+        ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" "$AppUserModelID"  "true"
       ${EndIf}
-      ${LogMsg} "Added Shortcut: $DESKTOP\${BrandFullName}.lnk"
+      ${LogMsg} "Added Shortcut: $DESKTOP\${BrandShortName}.lnk"
     ${Else}
-      ${LogMsg} "** ERROR Adding Shortcut: $DESKTOP\${BrandFullName}.lnk"
+      ${LogMsg} "** ERROR Adding Shortcut: $DESKTOP\${BrandShortName}.lnk"
     ${EndIf}
   ${EndIf}
 
   ; If elevated the Quick Launch shortcut must be added from the unelevated
   ; original process.
   ${If} $AddQuickLaunchSC == 1
     ${Unless} ${AtLeastWin7}
       ClearErrors
       ${GetParameters} $0
       ${GetOptions} "$0" "/UAC:" $0
       ${If} ${Errors}
         Call AddQuickLaunchShortcut
-        ${LogMsg} "Added Shortcut: $QUICKLAUNCH\${BrandFullName}.lnk"
+        ${LogMsg} "Added Shortcut: $QUICKLAUNCH\${BrandShortName}.lnk"
       ${Else}
         ; It is not possible to add a log entry from the unelevated process so
         ; add the log entry without the path since there is no simple way to
         ; know the correct full path.
-        ${LogMsg} "Added Quick Launch Shortcut: ${BrandFullName}.lnk"
+        ${LogMsg} "Added Quick Launch Shortcut: ${BrandShortName}.lnk"
         GetFunctionAddress $0 AddQuickLaunchShortcut
         UAC::ExecCodeSegment $0
       ${EndIf}
     ${EndUnless}
   ${EndIf}
 
 !ifdef MOZ_MAINTENANCE_SERVICE
   ${If} $TmpVal == "HKLM"
@@ -739,19 +739,19 @@ FunctionEnd
 Function AbortSurveySummary
   ExecShell "open" "${AbortSurveyURL}step5"
 FunctionEnd
 
 ################################################################################
 # Helper Functions
 
 Function AddQuickLaunchShortcut
-  CreateShortCut "$QUICKLAUNCH\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-  ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
-    ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandFullName}.lnk" \
+  CreateShortCut "$QUICKLAUNCH\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+  ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk"
+    ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandShortName}.lnk" \
                                            "$INSTDIR"
   ${EndIf}
 FunctionEnd
 
 Function CheckExistingInstall
   ; If there is a pending file copy from a previous upgrade don't allow
   ; installing until after the system has rebooted.
   IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4
--- a/browser/installer/windows/nsis/shared.nsh
+++ b/browser/installer/windows/nsis/shared.nsh
@@ -32,68 +32,67 @@
   SetShellVarContext current  ; Set SHCTX to the current user (e.g. HKCU)
   ${RegCleanMain} "Software\Mozilla"
   ${RegCleanUninstall}
   ${UpdateProtocolHandlers}
 
   ; setup the application model id registration value
   ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
 
-  ; Win7 taskbar and start menu link maintenance
-  Call FixShortcutAppModelIDs
-
   ClearErrors
   WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
   ${If} ${Errors}
     StrCpy $TmpVal "HKCU"
   ${Else}
     SetShellVarContext all    ; Set SHCTX to all users (e.g. HKLM)
     DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
     StrCpy $TmpVal "HKLM"
     ${RegCleanMain} "Software\Mozilla"
     ${RegCleanUninstall}
     ${UpdateProtocolHandlers}
     ${FixShellIconHandler} "HKLM"
     ${SetAppLSPCategories} ${LSP_CATEGORIES}
 
-    ; Win7 taskbar and start menu link maintenance
-    Call FixShortcutAppModelIDs
-
     ; Add the Firewall entries after an update
     Call AddFirewallEntries
 
     ReadRegStr $0 HKLM "Software\mozilla.org\Mozilla" "CurrentVersion"
     ${If} "$0" != "${GREVersion}"
       WriteRegStr HKLM "Software\mozilla.org\Mozilla" "CurrentVersion" "${GREVersion}"
     ${EndIf}
   ${EndIf}
 
   ; Migrate the application's Start Menu directory to a single shortcut in the
   ; root of the Start Menu Programs directory.
   ${MigrateStartMenuShortcut}
 
-  ; Update lastwritetime of the Start Menu shortcut to clear the tile cache.
-  ; Do this for both shell contexts in case the user has shortcuts in multiple
-  ; locations, then restore the previous context at the end.
-  ${If} ${AtLeastWin8}
-    SetShellVarContext all
-    ${TouchStartMenuShortcut}
-    SetShellVarContext current
-    ${TouchStartMenuShortcut}
-    ${If} $TmpVal == "HKLM"
-      SetShellVarContext all
-    ${ElseIf} $TmpVal == "HKCU"
-      SetShellVarContext current
-    ${EndIf}
-  ${EndIf}
-
   ; Adds a pinned Task Bar shortcut (see MigrateTaskBarShortcut for details).
   ${MigrateTaskBarShortcut}
 
+  ; Update the name/icon/AppModelID of our shortcuts as needed, then update the
+  ; lastwritetime of the Start Menu shortcut to clear the tile icon cache.
+  ; Do this for both shell contexts in case the user has shortcuts in multiple
+  ; locations, then restore the previous context at the end.
+  SetShellVarContext all
   ${UpdateShortcutBranding}
+  ${If} ${AtLeastWin8}
+    ${TouchStartMenuShortcut}
+  ${EndIf}
+  Call FixShortcutAppModelIDs
+  SetShellVarContext current
+  ${UpdateShortcutBranding}
+  ${If} ${AtLeastWin8}
+    ${TouchStartMenuShortcut}
+  ${EndIf}
+  Call FixShortcutAppModelIDs
+  ${If} $TmpVal == "HKLM"
+    SetShellVarContext all
+  ${ElseIf} $TmpVal == "HKCU"
+    SetShellVarContext current
+  ${EndIf}
 
   ${RemoveDeprecatedKeys}
   ${Set32to64DidMigrateReg}
 
   ${SetAppKeys}
   ${FixClassKeys}
   ${SetUninstallKeys}
   ${If} $TmpVal == "HKLM"
@@ -160,20 +159,21 @@
       nsExec::Exec "$\"$INSTDIR\maintenanceservice_installer.exe$\""
     ${EndIf}
   ${EndIf}
 !endif
 !macroend
 !define PostUpdate "!insertmacro PostUpdate"
 
 ; Update the last modified time on the Start Menu shortcut, so that its icon
-; gets refreshed. Should be called on Win8+ after MigrateStartMenuShortcut.
+; gets refreshed. Should be called on Win8+ after MigrateStartMenuShortcut
+; and UpdateShortcutBranding.
 !macro TouchStartMenuShortcut
-  ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-    FileOpen $0 "$SMPROGRAMS\${BrandFullName}.lnk" a
+  ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+    FileOpen $0 "$SMPROGRAMS\${BrandShortName}.lnk" a
     ${IfNot} ${Errors}
       System::Call '*(i, i) p .r1'
       System::Call 'kernel32::GetSystemTimeAsFileTime(p r1)'
       System::Call 'kernel32::SetFileTime(p r0, i 0, i 0, p r1) i .r2'
       System::Free $1
       FileClose $0
     ${EndIf}
   ${EndIf}
@@ -203,61 +203,61 @@
     StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
   ${EndIf}
   WriteRegDWORD HKLM "$R1" "IconsVisible" 0
   ${If} ${AtLeastWin8}
     WriteRegDWORD HKCU "$R1" "IconsVisible" 0
   ${EndIf}
 
   SetShellVarContext all  ; Set $DESKTOP to All Users
-  ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
+  ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
     SetShellVarContext current  ; Set $DESKTOP to the current user's desktop
   ${EndUnless}
 
-  ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-    ShellLink::GetShortCutArgs "$DESKTOP\${BrandFullName}.lnk"
+  ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+    ShellLink::GetShortCutArgs "$DESKTOP\${BrandShortName}.lnk"
     Pop $0
     ${If} "$0" == ""
-      ShellLink::GetShortCutTarget "$DESKTOP\${BrandFullName}.lnk"
+      ShellLink::GetShortCutTarget "$DESKTOP\${BrandShortName}.lnk"
       Pop $0
       ${GetLongPath} "$0" $0
       ${If} "$0" == "$INSTDIR\${FileMainEXE}"
-        Delete "$DESKTOP\${BrandFullName}.lnk"
+        Delete "$DESKTOP\${BrandShortName}.lnk"
       ${EndIf}
     ${EndIf}
   ${EndIf}
 
   SetShellVarContext all  ; Set $SMPROGRAMS to All Users
-  ${Unless} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
+  ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
     SetShellVarContext current  ; Set $SMPROGRAMS to the current user's Start
                                 ; Menu Programs directory
   ${EndUnless}
 
-  ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-    ShellLink::GetShortCutArgs "$SMPROGRAMS\${BrandFullName}.lnk"
+  ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+    ShellLink::GetShortCutArgs "$SMPROGRAMS\${BrandShortName}.lnk"
     Pop $0
     ${If} "$0" == ""
-      ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandFullName}.lnk"
+      ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandShortName}.lnk"
       Pop $0
       ${GetLongPath} "$0" $0
       ${If} "$0" == "$INSTDIR\${FileMainEXE}"
-        Delete "$SMPROGRAMS\${BrandFullName}.lnk"
+        Delete "$SMPROGRAMS\${BrandShortName}.lnk"
       ${EndIf}
     ${EndIf}
   ${EndIf}
 
-  ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
-    ShellLink::GetShortCutArgs "$QUICKLAUNCH\${BrandFullName}.lnk"
+  ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk"
+    ShellLink::GetShortCutArgs "$QUICKLAUNCH\${BrandShortName}.lnk"
     Pop $0
     ${If} "$0" == ""
-      ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandFullName}.lnk"
+      ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandShortName}.lnk"
       Pop $0
       ${GetLongPath} "$0" $0
       ${If} "$0" == "$INSTDIR\${FileMainEXE}"
-        Delete "$QUICKLAUNCH\${BrandFullName}.lnk"
+        Delete "$QUICKLAUNCH\${BrandShortName}.lnk"
       ${EndIf}
     ${EndIf}
   ${EndIf}
 !macroend
 !define HideShortcuts "!insertmacro HideShortcuts"
 
 ; Adds shortcuts for this installation. This should also add the application
 ; to Open With for the file types the application handles (bug 370480).
@@ -270,84 +270,86 @@
     StrCpy $R1 "Software\Clients\StartMenuInternet\$0\InstallInfo"
   ${EndIf}
   WriteRegDWORD HKLM "$R1" "IconsVisible" 1
   ${If} ${AtLeastWin8}
     WriteRegDWORD HKCU "$R1" "IconsVisible" 1
   ${EndIf}
 
   SetShellVarContext all  ; Set $DESKTOP to All Users
-  ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-    CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-    ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-      ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR"
+  ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+    CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+    ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+      ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR"
       ${If} ${AtLeastWin7}
       ${AndIf} "$AppUserModelID" != ""
-        ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID" "true"
+        ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" "$AppUserModelID" "true"
       ${EndIf}
     ${Else}
       SetShellVarContext current  ; Set $DESKTOP to the current user's desktop
-      ${Unless} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-        CreateShortCut "$DESKTOP\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-        ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
-          ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandFullName}.lnk" \
+      ${Unless} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+        CreateShortCut "$DESKTOP\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+        ${If} ${FileExists} "$DESKTOP\${BrandShortName}.lnk"
+          ShellLink::SetShortCutWorkingDirectory "$DESKTOP\${BrandShortName}.lnk" \
                                                  "$INSTDIR"
           ${If} ${AtLeastWin7}
           ${AndIf} "$AppUserModelID" != ""
-            ApplicationID::Set "$DESKTOP\${BrandFullName}.lnk" "$AppUserModelID" "true"
+            ApplicationID::Set "$DESKTOP\${BrandShortName}.lnk" "$AppUserModelID" "true"
           ${EndIf}
         ${EndIf}
       ${EndUnless}
     ${EndIf}
   ${EndUnless}
 
   SetShellVarContext all  ; Set $SMPROGRAMS to All Users
-  ${Unless} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-    CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-    ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-      ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
+  ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+    CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+    ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+      ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
                                              "$INSTDIR"
       ${If} ${AtLeastWin7}
       ${AndIf} "$AppUserModelID" != ""
-        ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID" "true"
+        ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true"
       ${EndIf}
     ${Else}
       SetShellVarContext current  ; Set $SMPROGRAMS to the current user's Start
                                   ; Menu Programs directory
-      ${Unless} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-        CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" "$INSTDIR\${FileMainEXE}"
-        ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-          ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
+      ${Unless} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+        CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" "$INSTDIR\${FileMainEXE}"
+        ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+          ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
                                                  "$INSTDIR"
           ${If} ${AtLeastWin7}
           ${AndIf} "$AppUserModelID" != ""
-            ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" "$AppUserModelID" "true"
+            ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" "$AppUserModelID" "true"
           ${EndIf}
         ${EndIf}
       ${EndUnless}
     ${EndIf}
   ${EndUnless}
 
   ; Windows 7 doesn't use the QuickLaunch directory
   ${Unless} ${AtLeastWin7}
-  ${AndUnless} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
-    CreateShortCut "$QUICKLAUNCH\${BrandFullName}.lnk" \
+  ${AndUnless} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk"
+    CreateShortCut "$QUICKLAUNCH\${BrandShortName}.lnk" \
                    "$INSTDIR\${FileMainEXE}"
-    ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
-      ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandFullName}.lnk" \
+    ${If} ${FileExists} "$QUICKLAUNCH\${BrandShortName}.lnk"
+      ShellLink::SetShortCutWorkingDirectory "$QUICKLAUNCH\${BrandShortName}.lnk" \
                                              "$INSTDIR"
     ${EndIf}
   ${EndUnless}
 !macroend
 !define ShowShortcuts "!insertmacro ShowShortcuts"
 
 ; Update the branding information on all shortcuts our installer created,
-; in case the branding has changed between updates.
+; to convert from BrandFullName (which is what we used to name shortcuts)
+; to BrandShortName (which is what we now name shortcuts). Also update the
+; icon if it's been changed.
 ; This should only be called sometime after both MigrateStartMenuShortcut
-; and MigrateTaskBarShurtcut
+; and MigrateTaskBarShurtcut, and it assumes SHCTX is set correctly.
 !macro UpdateShortcutBranding
   ${GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R9
   ${If} ${FileExists} "$R9"
     ClearErrors
     ; The entries in the shortcut log are numbered, but we never actually
     ; create more than one shortcut (or log entry) in each location.
     ReadINIStr $R8 "$R9" "STARTMENU" "Shortcut0"
     ${IfNot} ${Errors}
@@ -365,26 +367,26 @@
           ${ElseIf} $R6 == "$INSTDIR\firefox.ico"
           ${AndIfNot} ${FileExists} "$INSTDIR\firefox.ico"
             StrCpy $R5 "1"
           ${Else}
             StrCpy $R5 "0"
           ${EndIf}
 
           ${If} $R5 == "1"
-          ${OrIf} $R8 != "${BrandFullName}.lnk"
+          ${OrIf} $R8 != "${BrandShortName}.lnk"
             Delete "$SMPROGRAMS\$R8"
             ${If} ${FileExists} "$INSTDIR\firefox.ico"
-              CreateShortcut "$SMPROGRAMS\${BrandFullName}.lnk" \
+              CreateShortcut "$SMPROGRAMS\${BrandShortName}.lnk" \
                              "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\firefox.ico"
             ${Else}
-              CreateShortcut "$SMPROGRAMS\${BrandFullName}.lnk" \
+              CreateShortcut "$SMPROGRAMS\${BrandShortName}.lnk" \
                              "$INSTDIR\${FileMainEXE}"
             ${EndIf}
-            WriteINIStr "$R9" "STARTMENU" "Shortcut0" "${BrandFullName}.lnk"
+            WriteINIStr "$R9" "STARTMENU" "Shortcut0" "${BrandShortName}.lnk"
           ${EndIf}
         ${EndIf}
       ${EndIf}
     ${EndIf}
 
     ClearErrors
     ReadINIStr $R8 "$R9" "DESKTOP" "Shortcut0"
     ${IfNot} ${Errors}
@@ -402,26 +404,26 @@
           ${ElseIf} $R6 == "$INSTDIR\firefox.ico"
           ${AndIfNot} ${FileExists} "$INSTDIR\firefox.ico"
             StrCpy $R5 "1"
           ${Else}
             StrCpy $R5 "0"
           ${EndIf}
 
           ${If} $R5 == "1"
-          ${OrIf} $R8 != "${BrandFullName}.lnk"
+          ${OrIf} $R8 != "${BrandShortName}.lnk"
             Delete "$DESKTOP\$R8"
             ${If} ${FileExists} "$INSTDIR\firefox.ico"
-              CreateShortcut "$DESKTOP\${BrandFullName}.lnk" \
+              CreateShortcut "$DESKTOP\${BrandShortName}.lnk" \
                              "$INSTDIR\${FileMainEXE}" "" "$INSTDIR\firefox.ico"
             ${Else}
-              CreateShortcut "$DESKTOP\${BrandFullName}.lnk" \
+              CreateShortcut "$DESKTOP\${BrandShortName}.lnk" \
                              "$INSTDIR\${FileMainEXE}"
             ${EndIf}
-            WriteINIStr "$R9" "DESKTOP" "Shortcut0" "${BrandFullName}.lnk"
+            WriteINIStr "$R9" "DESKTOP" "Shortcut0" "${BrandShortName}.lnk"
           ${EndIf}
         ${EndIf}
       ${EndIf}
     ${EndIf}
 
     ClearErrors
     ReadINIStr $R8 "$R9" "QUICKLAUNCH" "Shortcut0"
     ${IfNot} ${Errors}
@@ -1286,35 +1288,35 @@
   ${If} ${FileExists} "$0"
     ClearErrors
     ReadINIStr $5 "$0" "SMPROGRAMS" "RelativePathToDir"
     ${Unless} ${Errors}
       ClearErrors
       ReadINIStr $1 "$0" "STARTMENU" "Shortcut0"
       ${If} ${Errors}
         ; The STARTMENU ini section doesn't exist.
-        ${LogStartMenuShortcut} "${BrandFullName}.lnk"
+        ${LogStartMenuShortcut} "${BrandShortName}.lnk"
         ${GetLongPath} "$SMPROGRAMS" $2
         ${GetLongPath} "$2\$5" $1
         ${If} "$1" != ""
           ClearErrors
           ReadINIStr $3 "$0" "SMPROGRAMS" "Shortcut0"
           ${Unless} ${Errors}
             ${If} ${FileExists} "$1\$3"
               ShellLink::GetShortCutTarget "$1\$3"
               Pop $4
               ${If} "$INSTDIR\${FileMainEXE}" == "$4"
-                CreateShortCut "$SMPROGRAMS\${BrandFullName}.lnk" \
+                CreateShortCut "$SMPROGRAMS\${BrandShortName}.lnk" \
                                "$INSTDIR\${FileMainEXE}"
-                ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
-                  ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandFullName}.lnk" \
+                ${If} ${FileExists} "$SMPROGRAMS\${BrandShortName}.lnk"
+                  ShellLink::SetShortCutWorkingDirectory "$SMPROGRAMS\${BrandShortName}.lnk" \
                                                          "$INSTDIR"
                   ${If} ${AtLeastWin7}
                   ${AndIf} "$AppUserModelID" != ""
-                    ApplicationID::Set "$SMPROGRAMS\${BrandFullName}.lnk" \
+                    ApplicationID::Set "$SMPROGRAMS\${BrandShortName}.lnk" \
                                        "$AppUserModelID" "true"
                   ${EndIf}
                 ${EndIf}
               ${EndIf}
             ${EndIf}
           ${EndUnless}
         ${EndIf}
       ${EndIf}
@@ -1382,19 +1384,19 @@
 !macroend
 !define RemoveStartMenuDir "!insertmacro RemoveStartMenuDir"
 
 ; Creates the shortcuts log ini file with the appropriate entries if it doesn't
 ; already exist.
 !macro CreateShortcutsLog
   ${GetShortcutsLogPath} $0
   ${Unless} ${FileExists} "$0"
-    ${LogStartMenuShortcut} "${BrandFullName}.lnk"
-    ${LogQuickLaunchShortcut} "${BrandFullName}.lnk"
-    ${LogDesktopShortcut} "${BrandFullName}.lnk"
+    ${LogStartMenuShortcut} "${BrandShortName}.lnk"
+    ${LogQuickLaunchShortcut} "${BrandShortName}.lnk"
+    ${LogDesktopShortcut} "${BrandShortName}.lnk"
   ${EndUnless}
 !macroend
 !define CreateShortcutsLog "!insertmacro CreateShortcutsLog"
 
 ; The files to check if they are in use during (un)install so the restart is
 ; required message is displayed. All files must be located in the $INSTDIR
 ; directory.
 !macro PushFilesToCheck
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -182,18 +182,18 @@ set_config('XCODE_PATH', xcode_path)
 # ==============================================================
 # Normally, we'd use js_option and automatically have those variables
 # propagated to js/src, but things are complicated by possible additional
 # wrappers in CC/CXX, and by other subconfigures that do not handle those
 # options and do need CC/CXX altered.
 option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
        help='Enable compiling with wrappers such as distcc and ccache')
 
-option('--with-ccache', env='CCACHE', nargs='?',
-       help='Enable compiling with ccache')
+js_option('--with-ccache', env='CCACHE', nargs='?',
+          help='Enable compiling with ccache')
 
 
 @depends_if('--with-ccache')
 def ccache(value):
     if len(value):
         return value
     # If --with-ccache was given without an explicit value, we default to
     # 'ccache'.
@@ -1207,16 +1207,29 @@ def wrap_system_includes(target, visibil
 set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
            depends(visibility_flags)(lambda v: bool(v) or None))
 set_define('HAVE_VISIBILITY_ATTRIBUTE',
            depends(visibility_flags)(lambda v: bool(v) or None))
 set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
 set_config('VISIBILITY_FLAGS', visibility_flags)
 
 
+@depends(c_compiler, using_sccache)
+def depend_cflags(info, using_sccache):
+    if info.type not in ('clang-cl', 'msvc'):
+        return '-MD -MP -MF $(MDDEPDIR)/$(@F).pp'
+    elif using_sccache:
+        # sccache supports a special flag to create depfiles
+        # by parsing MSVC's -showIncludes output.
+        return '-deps$(MDDEPDIR)/$(@F).pp'
+
+
+set_config('_DEPEND_CFLAGS', depend_cflags)
+
+
 @depends(c_compiler)
 @imports('multiprocessing')
 @imports(_from='__builtin__', _import='min')
 def pgo_flags(compiler):
     if compiler.type in ('gcc', 'clang'):
         return namespace(
             gen_cflags=['-fprofile-generate'],
             gen_ldflags=['-fprofile-generate'],
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -442,8 +442,33 @@ def alter_path(sdk_bin_path):
     path = os.pathsep.join(sdk_bin_path)
     os.environ['PATH'] = path
     return path
 
 
 set_config('PATH', alter_path)
 
 check_prog('MAKECAB', ('makecab.exe',))
+
+
+@depends(c_compiler, using_sccache)
+def need_showincludes_prefix(info, using_sccache):
+    # sccache does its own -showIncludes prefix checking.
+    if info.type in ('clang-cl', 'msvc') and not using_sccache:
+        return True
+
+
+@depends(c_compiler, when=need_showincludes_prefix)
+@imports(_from='re', _import='compile', _as='re_compile')
+def msvc_showincludes_prefix(c_compiler):
+    pattern = re_compile(br'^([^:]*:.*[ :] )(.*\\stdio.h)$')
+    output = try_invoke_compiler([c_compiler.compiler], 'C', '#include <stdio.h>\n',
+                                 ['-nologo', '-c', '-Fonul', '-showIncludes'])
+    for line in output.splitlines():
+        if line.endswith(b'\\stdio.h'):
+            m = pattern.match(line)
+            if m:
+                return m.group(1)
+    # We should have found the prefix and returned earlier
+    die('Cannot find cl -showIncludes prefix.')
+
+
+set_config('CL_INCLUDES_PREFIX', msvc_showincludes_prefix)
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -119,22 +119,16 @@ else
     esac
     export CCACHE="$topsrcdir/sccache2/sccache${suffix}"
     export SCCACHE_VERBOSE_STATS=1
     mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
     mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
     mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz"
     case "$platform" in
     win*)
-        # sccache supports a special flag to create depfiles.
-        #TODO: bug 1318370 - move this all into toolchain.configure
-        export _DEPEND_CFLAGS='-deps$(MDDEPDIR)/$(@F).pp'
-        # Windows builds have a default wrapper that needs to be overridden
-        mk_add_options "export CC_WRAPPER="
-        mk_add_options "export CXX_WRAPPER="
         # For now, sccache doesn't support separate PDBs so force debug info to be
         # in object files.
         mk_add_options "export COMPILE_PDB_FLAG="
         mk_add_options "export HOST_PDB_FLAG="
         mk_add_options "export MOZ_DEBUG_FLAGS=-Z7"
         ;;
     esac
 fi
--- a/config/config.mk
+++ b/config/config.mk
@@ -115,18 +115,20 @@ else
   win_srcdir := $(srcdir)
   BUILD_TOOLS = $(MOZILLA_DIR)/build/unix
 endif
 
 CONFIG_TOOLS	= $(MOZ_BUILD_ROOT)/config
 AUTOCONF_TOOLS	= $(MOZILLA_DIR)/build/autoconf
 
 ifdef _MSC_VER
+ifndef MOZ_USING_SCCACHE
 CC_WRAPPER ?= $(call py_action,cl)
 CXX_WRAPPER ?= $(call py_action,cl)
+endif
 endif # _MSC_VER
 
 CC := $(CC_WRAPPER) $(CC)
 CXX := $(CXX_WRAPPER) $(CXX)
 MKDIR ?= mkdir
 SLEEP ?= sleep
 TOUCH ?= touch
 
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -24,21 +24,21 @@ function consoleOpened(HUD) {
   let dbg = new tmp.Debugger();
 
   let jsterm = HUD.jsterm;
   let win = content.wrappedJSObject;
   let dbgWindow = dbg.addDebuggee(content);
   let container = win._container;
 
   // Make sure autocomplete does not walk through generators.
-  let result = container.gen1.next();
+  let result = container.gen1.next().value;
   let completion = JSPropertyProvider(dbgWindow, null, "_container.gen1.");
   isnot(completion.matches.length, 0, "Got matches for gen1");
 
-  is(result + 1, container.gen1.next(), "gen1.next() did not execute");
+  is(result + 1, container.gen1.next().value, "gen1.next() did not execute");
 
   result = container.gen2.next().value;
 
   completion = JSPropertyProvider(dbgWindow, null, "_container.gen2.");
   isnot(completion.matches.length, 0, "Got matches for gen2");
 
   is((result / 2 + 1) * 2, container.gen2.next().value,
      "gen2.next() did not execute");
--- a/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
+++ b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
@@ -2,17 +2,17 @@
 <html lang="en">
   <head>
     <meta charset="utf-8">
     <title>Web Console test for bug 632347 - generators</title>
     <!-- Any copyright is dedicated to the Public Domain.
          http://creativecommons.org/publicdomain/zero/1.0/ -->
 <script type="application/javascript;version=1.8">
 (function(){
-function genFunc() {
+function* genFunc() {
   var a = 5;
   while (a < 10) {
     yield a++;
   }
 }
 
 window._container = {};
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -941,20 +941,25 @@ nsDocShell::DestroyChildren()
     if (shell) {
       shell->SetTreeOwner(nullptr);
     }
   }
 
   nsDocLoader::DestroyChildren();
 }
 
+NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDocShell,
+                                   nsDocLoader,
+                                   mSessionStorageManager,
+                                   mScriptGlobal)
+
 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
 
-NS_INTERFACE_MAP_BEGIN(nsDocShell)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
   NS_INTERFACE_MAP_ENTRY(nsIScrollable)
   NS_INTERFACE_MAP_ENTRY(nsITextScroll)
   NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -168,16 +168,17 @@ class nsDocShell final
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsDocShell)
 
   nsDocShell();
 
   virtual nsresult Init() override;
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDocShell, nsDocLoader)
 
   NS_DECL_NSIDOCSHELL
   NS_DECL_NSIDOCSHELLTREEITEM
   NS_DECL_NSIWEBNAVIGATION
   NS_DECL_NSIBASEWINDOW
   NS_DECL_NSISCROLLABLE
   NS_DECL_NSITEXTSCROLL
   NS_DECL_NSIDOCCHARSET
--- a/docshell/test/chrome/test_bug909218.html
+++ b/docshell/test/chrome/test_bug909218.html
@@ -93,17 +93,20 @@ RequestWatcher = {
     // We are checking requests - if there isn't one, ignore it.
     if (!req) {
       return;
     }
     // We will usually see requests for 'about:document-onload-blocker' not
     // have the flag, so we just ignore them.
     // We also see, eg, resource://gre-resources/loading-image.png, so
     // skip resource:// URLs too.
-    if (req.name.startsWith("about:") || req.name.startsWith("resource:")) {
+    // We may also see, eg, chrome://global/skin/icons/resizer.png, so
+    // skip chrome:// URLs too.
+    if (req.name.startsWith("about:") || req.name.startsWith("resource:") ||
+      req.name.startsWith("chrome:")) {
       return;
     }
     is(req.loadFlags & TEST_FLAGS, TEST_FLAGS, "request " + req.name + " has the expected flags");
     this.requestCounts[req.name] += 1;
     var stopFlags = Ci.nsIWebProgressListener.STATE_STOP |
                     Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
     if (req.name == TEST_URL && (flags & stopFlags) == stopFlags) {
       this.finalize();
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1722,30 +1722,16 @@ Navigator::HasUserMediaSupport(JSContext
                                JSObject* /* unused */)
 {
   // Make enabling peerconnection enable getUserMedia() as well
   return Preferences::GetBool("media.navigator.enabled", false) ||
          Preferences::GetBool("media.peerconnection.enabled", false);
 }
 
 /* static */
-bool
-Navigator::IsE10sEnabled(JSContext* aCx, JSObject* aGlobal)
-{
-  return XRE_IsContentProcess();
-}
-
-bool
-Navigator::MozE10sEnabled()
-{
-  // This will only be called if IsE10sEnabled() is true.
-  return true;
-}
-
-/* static */
 already_AddRefed<nsPIDOMWindowInner>
 Navigator::GetWindowFromGlobal(JSObject* aGlobal)
 {
   nsCOMPtr<nsPIDOMWindowInner> win =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
   MOZ_ASSERT(!win || win->IsInnerWindow());
   return win.forget();
 }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -222,31 +222,27 @@ public:
                               ErrorResult& aRv);
 
   already_AddRefed<ServiceWorkerContainer> ServiceWorker();
 
   mozilla::dom::CredentialsContainer* Credentials();
 
   void GetLanguages(nsTArray<nsString>& aLanguages);
 
-  bool MozE10sEnabled();
-
   StorageManager* Storage();
 
   static void GetAcceptLanguages(nsTArray<nsString>& aLanguages);
 
   // WebIDL helper methods
   static bool HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */);
   static bool HasWifiManagerSupport(JSContext* /* unused */,
                                   JSObject* aGlobal);
   static bool HasUserMediaSupport(JSContext* /* unused */,
                                   JSObject* /* unused */);
 
-  static bool IsE10sEnabled(JSContext* aCx, JSObject* aGlobal);
-
   nsPIDOMWindowInner* GetParentObject() const
   {
     return GetWindow();
   }
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
   // GetWindowFromGlobal returns the inner window for this global, if
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -503,17 +503,17 @@ mozilla::dom::TraceBlackJS(JSTracer* aTr
   }
 
   // Mark globals of active windows black.
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   if (windowsById) {
     for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) {
       nsGlobalWindow* window = iter.Data();
-      if (window->GetDocShell() && window->IsOuterWindow()) {
+      if (!window->IsCleanedUp() && window->IsOuterWindow()) {
         window->TraceGlobalJSObject(aTrc);
         EventListenerManager* elm = window->GetExistingListenerManager();
         if (elm) {
           elm->TraceListeners(aTrc);
         }
 
         if (window->IsRootOuterWindow()) {
           // In child process trace all the TabChildGlobals.
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -74,23 +74,25 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
   if (tmp->mDocument) {
     tmp->mDocument->RemoveObserver(tmp);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 nsContentSink::nsContentSink()
   : mBackoffCount(0)
--- a/dom/base/nsGenConImageContent.cpp
+++ b/dom/base/nsGenConImageContent.cpp
@@ -65,18 +65,17 @@ private:
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 };
 
 NS_IMPL_ISUPPORTS_INHERITED(nsGenConImageContent,
                             nsXMLElement,
                             nsIImageLoadingContent,
-                            imgINotificationObserver,
-                            imgIOnloadBlocker)
+                            imgINotificationObserver)
 
 nsresult
 NS_NewGenConImageContent(nsIContent** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                          imgRequestProxy* aImageRequest)
 {
   NS_PRECONDITION(aImageRequest, "Must have request!");
   nsGenConImageContent *it = new nsGenConImageContent(aNodeInfo);
   NS_ADDREF(*aResult = it);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2320,16 +2320,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioWorklet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMozSelfSupport)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
+
   tmp->TraverseHostObjectURIs(cb);
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mBrowserDOMWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mOpenerForInitialContentBrowser)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -2403,16 +2405,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
+
   tmp->UnlinkHostObjectURIs();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
   tmp->DisableIdleCallbackRequests();
 
   if (tmp->IsChromeWindow()) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
     if (tmp->mChromeFields.mMessageManager) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -665,16 +665,22 @@ public:
   bool IsClosedOrClosing() {
     return (mIsClosed ||
             mInClose ||
             mHavePendingClose ||
             mCleanedUp);
   }
 
   bool
+  IsCleanedUp() const
+  {
+    return mCleanedUp;
+  }
+
+  bool
   HadOriginalOpener() const
   {
     MOZ_ASSERT(IsOuterWindow());
     return mHadOriginalOpener;
   }
 
   bool
   IsTopLevelWindow()
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -838,62 +838,16 @@ nsImageLoadingContent::ForceReload(bool 
     notify.Construct() = aNotify;
   }
 
   ErrorResult result;
   ForceReload(notify, result);
   return result.StealNSResult();
 }
 
-NS_IMETHODIMP
-nsImageLoadingContent::BlockOnload(imgIRequest* aRequest)
-{
-  if (aRequest == mCurrentRequest) {
-    NS_ASSERTION(!(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD),
-                 "Double BlockOnload!?");
-    mCurrentRequestFlags |= REQUEST_BLOCKS_ONLOAD;
-  } else if (aRequest == mPendingRequest) {
-    NS_ASSERTION(!(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD),
-                 "Double BlockOnload!?");
-    mPendingRequestFlags |= REQUEST_BLOCKS_ONLOAD;
-  } else {
-    return NS_OK;
-  }
-
-  nsIDocument* doc = GetOurCurrentDoc();
-  if (doc) {
-    doc->BlockOnload();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsImageLoadingContent::UnblockOnload(imgIRequest* aRequest)
-{
-  if (aRequest == mCurrentRequest) {
-    NS_ASSERTION(mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD,
-                 "Double UnblockOnload!?");
-    mCurrentRequestFlags &= ~REQUEST_BLOCKS_ONLOAD;
-  } else if (aRequest == mPendingRequest) {
-    NS_ASSERTION(mPendingRequestFlags & REQUEST_BLOCKS_ONLOAD,
-                 "Double UnblockOnload!?");
-    mPendingRequestFlags &= ~REQUEST_BLOCKS_ONLOAD;
-  } else {
-    return NS_OK;
-  }
-
-  nsIDocument* doc = GetOurCurrentDoc();
-  if (doc) {
-    doc->UnblockOnload(false);
-  }
-
-  return NS_OK;
-}
-
 /*
  * Non-interface methods
  */
 
 nsresult
 nsImageLoadingContent::LoadImage(const nsAString& aNewURI,
                                  bool aForce,
                                  bool aNotify,
@@ -1602,34 +1556,28 @@ nsImageLoadingContent::BindToTree(nsIDoc
 {
   // We may be entering the document, so if our image should be tracked,
   // track it.
   if (!aDocument)
     return;
 
   TrackImage(mCurrentRequest);
   TrackImage(mPendingRequest);
-
-  if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
-    aDocument->BlockOnload();
 }
 
 void
 nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // We may be leaving the document, so if our image is tracked, untrack it.
   nsCOMPtr<nsIDocument> doc = GetOurCurrentDoc();
   if (!doc)
     return;
 
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
-
-  if (mCurrentRequestFlags & REQUEST_BLOCKS_ONLOAD)
-    doc->UnblockOnload(false);
 }
 
 void
 nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility,
                                           const Maybe<OnNonvisible>& aNonvisibleAction)
 {
   switch (aNewVisibility) {
     case Visibility::APPROXIMATELY_VISIBLE:
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -9,17 +9,16 @@
  * subclassed by various content nodes that want to provide image
  * loading functionality (eg <img>, <object>, etc).
  */
 
 #ifndef nsImageLoadingContent_h__
 #define nsImageLoadingContent_h__
 
 #include "imgINotificationObserver.h"
-#include "imgIOnloadBlocker.h"
 #include "mozilla/CORSMode.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/TimeStamp.h"
 #include "nsCOMPtr.h"
 #include "nsIImageLoadingContent.h"
 #include "nsIRequest.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIContentPolicy.h"
@@ -36,32 +35,30 @@ namespace mozilla {
 class AsyncEventDispatcher;
 } // namespace mozilla
 
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
-class nsImageLoadingContent : public nsIImageLoadingContent,
-                              public imgIOnloadBlocker
+class nsImageLoadingContent : public nsIImageLoadingContent
 {
   template <typename T> using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   using Visibility = mozilla::Visibility;
 
   /* METHODS */
 public:
   nsImageLoadingContent();
   virtual ~nsImageLoadingContent();
 
   NS_DECL_IMGINOTIFICATIONOBSERVER
   NS_DECL_NSIIMAGELOADINGCONTENT
-  NS_DECL_IMGIONLOADBLOCKER
 
   // Web IDL binding methods.
   // Note that the XPCOM SetLoadingEnabled, AddObserver, RemoveObserver,
   // ForceImageState methods are OK for Web IDL bindings to use as well,
   // since none of them throw when called via the Web IDL bindings.
 
   bool LoadingEnabled() const { return mLoadingEnabled; }
   int16_t ImageBlockingStatus() const
@@ -416,18 +413,16 @@ protected:
   RefPtr<imgRequestProxy> mCurrentRequest;
   RefPtr<imgRequestProxy> mPendingRequest;
   uint32_t mCurrentRequestFlags;
   uint32_t mPendingRequestFlags;
 
   enum {
     // Set if the request needs ResetAnimation called on it.
     REQUEST_NEEDS_ANIMATION_RESET = 0x00000001U,
-    // Set if the request is blocking onload.
-    REQUEST_BLOCKS_ONLOAD = 0x00000002U,
     // Set if the request is currently tracked with the document.
     REQUEST_IS_TRACKED = 0x00000004U,
     // Set if this is an imageset request, such as from <img srcset> or
     // <picture>
     REQUEST_IS_IMAGESET = 0x00000008U
   };
 
   // If the image was blocked or if there was an error loading, it's nice to
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -138,27 +138,29 @@ nsInProcessTabChildGlobal::Init()
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsInProcessTabChildGlobal)
 
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                   DOMEventTargetHelper)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
+   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
    tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                DOMEventTargetHelper)
   tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   tmp->nsMessageManagerScriptExecutor::Unlink();
   tmp->UnlinkHostObjectURIs();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsInProcessTabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -634,20 +634,19 @@ protected:
   // Cache the URI when mDoc is cleared.
   nsCOMPtr<nsIURI> mDocumentURI; // strong
   nsCOMPtr<nsIURI> mDocBaseURI; // strong
 
   nsCOMPtr<mozilla::dom::EventTarget> mParentTarget; // strong
 
   // These members are only used on outer windows.
   nsCOMPtr<mozilla::dom::Element> mFrameElement;
-  // This reference is used by the subclass nsGlobalWindow, and cleared in it's
-  // DetachFromDocShell() method. This method is called by nsDocShell::Destroy(),
-  // which is called before the nsDocShell is destroyed.
-  nsIDocShell* MOZ_NON_OWNING_REF mDocShell;  // Weak Reference
+
+  // This reference is used by nsGlobalWindow.
+  nsCOMPtr<nsIDocShell> mDocShell;
 
   // mPerformance is only used on inner windows.
   RefPtr<mozilla::dom::Performance> mPerformance;
   // mTimeoutManager is only useed on inner windows.
   mozilla::UniquePtr<mozilla::dom::TimeoutManager> mTimeoutManager;
 
   typedef nsRefPtrHashtable<nsStringHashKey,
                             mozilla::dom::ServiceWorkerRegistration>
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1014,16 +1014,17 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasR
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
   // Make sure we remove ourselves from the list of demotable contexts (raw pointers),
   // since we're logically destructed at this point.
   CanvasRenderingContext2D::RemoveDemotableContext(tmp);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
     ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
     auto filterChainObserver =
       static_cast<CanvasFilterChainObserver*>(tmp->mStyleStack[i].filterChainObserver.get());
     if (filterChainObserver) {
@@ -1037,16 +1038,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Ca
       ImplCycleCollectionUnlink(info.mElement);
     }
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
   for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::STROKE], "Stroke CanvasPattern");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].patternStyles[Style::FILL], "Fill CanvasPattern");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE], "Stroke CanvasGradient");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].gradientStyles[Style::FILL], "Fill CanvasGradient");
     ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].filterChainObserver, "Filter Chain Observer");
   }
   for (size_t x = 0 ; x < tmp->mHitRegionsOptions.Length(); x++) {
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleChild.cpp
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientHandleChild.h"
+
+#include "ClientHandleOpChild.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+void
+ClientHandleChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mHandle) {
+    mHandle->RevokeActor(this);
+
+    // Revoking the actor link should automatically cause the owner
+    // to call RevokeOwner() as well.
+    MOZ_DIAGNOSTIC_ASSERT(!mHandle);
+  }
+}
+
+PClientHandleOpChild*
+ClientHandleChild::AllocPClientHandleOpChild(const ClientOpConstructorArgs& aArgs)
+{
+  MOZ_ASSERT_UNREACHABLE("ClientHandleOpChild must be explicitly constructed.");
+  return nullptr;
+}
+
+bool
+ClientHandleChild::DeallocPClientHandleOpChild(PClientHandleOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+ClientHandleChild::ClientHandleChild()
+  : mHandle(nullptr)
+  , mTeardownStarted(false)
+{
+}
+
+void
+ClientHandleChild::SetOwner(ClientThing<ClientHandleChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mHandle);
+  mHandle = aThing;
+  MOZ_DIAGNOSTIC_ASSERT(mHandle);
+}
+
+void
+ClientHandleChild::RevokeOwner(ClientThing<ClientHandleChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mHandle);
+  MOZ_DIAGNOSTIC_ASSERT(mHandle == aThing);
+  mHandle = nullptr;
+}
+
+void
+ClientHandleChild::MaybeStartTeardown()
+{
+  if (mTeardownStarted) {
+    return;
+  }
+  mTeardownStarted = true;
+  Unused << SendTeardown();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientHandleChild_h
+#define _mozilla_dom_ClientHandleChild_h
+
+#include "ClientThing.h"
+#include "mozilla/dom/PClientHandleChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientHandle;
+class ClientInfo;
+
+template <typename ActorType> class ClientThing;
+
+class ClientHandleChild final : public PClientHandleChild
+{
+  ClientThing<ClientHandleChild>* mHandle;
+  bool mTeardownStarted;
+
+  // PClientHandleChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientHandleOpChild*
+  AllocPClientHandleOpChild(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientHandleOpChild(PClientHandleOpChild* aActor) override;
+
+public:
+  ClientHandleChild();
+
+  void
+  SetOwner(ClientThing<ClientHandleChild>* aThing);
+
+  void
+  RevokeOwner(ClientThing<ClientHandleChild>* aThing);
+
+  void
+  MaybeStartTeardown();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientHandleChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleOpChild.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientHandleOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientHandleOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mPromise) {
+    mPromise->Reject(NS_ERROR_ABORT, __func__);
+    mPromise = nullptr;
+  }
+}
+
+IPCResult
+ClientHandleOpChild::Recv__delete__(const ClientOpResult& aResult)
+{
+  if (aResult.type() == ClientOpResult::Tnsresult &&
+      NS_FAILED(aResult.get_nsresult())) {
+    mPromise->Reject(aResult.get_nsresult(), __func__);
+    mPromise = nullptr;
+    return IPC_OK();
+  }
+  mPromise->Resolve(aResult, __func__);
+  mPromise = nullptr;
+  return IPC_OK();
+}
+
+ClientHandleOpChild::ClientHandleOpChild(const ClientOpConstructorArgs& aArgs,
+                                         ClientOpPromise::Private* aPromise)
+  : mPromise(aPromise)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
+}
+
+ClientHandleOpChild::~ClientHandleOpChild()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleOpChild.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientHandleOpChild_h
+#define _mozilla_dom_ClientHandleOpChild_h
+
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/PClientHandleOpChild.h"
+#include "mozilla/MozPromise.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientHandleOpChild final : public PClientHandleOpChild
+{
+  RefPtr<ClientOpPromise::Private> mPromise;
+
+  // PClientHandleOpChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const ClientOpResult& aResult) override;
+
+public:
+  ClientHandleOpChild(const ClientOpConstructorArgs& aArgs,
+                      ClientOpPromise::Private* aPromise);
+
+  ~ClientHandleOpChild();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientHandleOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleOpParent.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientHandleOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientHandleOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+void
+ClientHandleOpParent::Init(const ClientOpConstructorArgs& aArgs)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleOpParent.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientHandleOpParent_h
+#define _mozilla_dom_ClientHandleOpParent_h
+
+#include "mozilla/dom/PClientHandleOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientHandleOpParent final : public PClientHandleOpParent
+{
+  // PClientHandleOpParent interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+public:
+  ClientHandleOpParent() = default;
+  ~ClientHandleOpParent() = default;
+
+  void
+  Init(const ClientOpConstructorArgs& aArgs);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientHandleOpParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleParent.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientHandleParent.h"
+
+#include "ClientHandleOpParent.h"
+#include "ClientSourceParent.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+IPCResult
+ClientHandleParent::RecvTeardown()
+{
+  Unused << Send__delete__(this);
+  return IPC_OK();
+}
+
+void
+ClientHandleParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+PClientHandleOpParent*
+ClientHandleParent::AllocPClientHandleOpParent(const ClientOpConstructorArgs& aArgs)
+{
+  return new ClientHandleOpParent();
+}
+
+bool
+ClientHandleParent::DeallocPClientHandleOpParent(PClientHandleOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+IPCResult
+ClientHandleParent::RecvPClientHandleOpConstructor(PClientHandleOpParent* aActor,
+                                                   const ClientOpConstructorArgs& aArgs)
+{
+  auto actor = static_cast<ClientHandleOpParent*>(aActor);
+  actor->Init(aArgs);
+  return IPC_OK();
+}
+
+ClientHandleParent::ClientHandleParent()
+{
+}
+
+ClientHandleParent::~ClientHandleParent()
+{
+}
+
+void
+ClientHandleParent::Init(const IPCClientInfo& aClientInfo)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientHandleParent.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientHandleParent_h
+#define _mozilla_dom_ClientHandleParent_h
+
+#include "mozilla/dom/PClientHandleParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientHandleParent final : public PClientHandleParent
+{
+  // PClientHandleParent interface
+  mozilla::ipc::IPCResult
+  RecvTeardown() override;
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientHandleOpParent*
+  AllocPClientHandleOpParent(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientHandleOpParent(PClientHandleOpParent* aActor) override;
+
+  mozilla::ipc::IPCResult
+  RecvPClientHandleOpConstructor(PClientHandleOpParent* aActor,
+                                 const ClientOpConstructorArgs& aArgs) override;
+
+public:
+  ClientHandleParent();
+  ~ClientHandleParent();
+
+  void
+  Init(const IPCClientInfo& aClientInfo);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientHandleParent_h
--- a/dom/clients/manager/ClientIPCTypes.ipdlh
+++ b/dom/clients/manager/ClientIPCTypes.ipdlh
@@ -1,23 +1,29 @@
 /* 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 protocol PClientSource;
 include DOMTypes;
 include PBackgroundSharedTypes;
+include IPCServiceWorkerDescriptor;
 include ProtocolTypes;
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using ClientType from "mozilla/dom/ClientIPCUtils.h";
 using FrameType from "mozilla/dom/ClientIPCUtils.h";
 using VisibilityState from "mozilla/dom/ClientIPCUtils.h";
 
 namespace mozilla {
 namespace dom {
 
+struct ClientSourceConstructorArgs
+{
+};
+
 struct IPCClientInfo
 {
   nsID id;
   ClientType type;
   PrincipalInfo principalInfo;
   TimeStamp creationTime;
   nsCString url;
   FrameType frameType;
@@ -35,10 +41,27 @@ struct IPCClientWorkerState
 };
 
 union IPCClientState
 {
   IPCClientWindowState;
   IPCClientWorkerState;
 };
 
+struct ClientOpenWindowArgs
+{
+};
+
+struct ClientOpConstructorArgs
+{
+};
+
+struct ClientNavigateOpConstructorArgs
+{
+};
+
+union ClientOpResult
+{
+  nsresult;
+};
+
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerActors.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientManagerChild.h"
+#include "ClientManagerParent.h"
+
+namespace mozilla {
+namespace dom {
+
+PClientManagerChild*
+AllocClientManagerChild()
+{
+  MOZ_ASSERT_UNREACHABLE("Default ClientManagerChild allocator should not be invoked");
+  return nullptr;
+}
+
+bool
+DeallocClientManagerChild(PClientManagerChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PClientManagerParent*
+AllocClientManagerParent()
+{
+  return new ClientManagerParent();
+}
+
+bool
+DeallocClientManagerParent(PClientManagerParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerActors.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientManagerActors_h
+#define _mozilla_dom_ClientManagerActors_h
+
+namespace mozilla {
+namespace dom {
+
+class PClientManagerChild;
+class PClientManagerParent;
+
+PClientManagerChild*
+AllocClientManagerChild();
+
+bool
+DeallocClientManagerChild(PClientManagerChild* aActor);
+
+PClientManagerParent*
+AllocClientManagerParent();
+
+bool
+DeallocClientManagerParent(PClientManagerParent* aActor);
+
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientManagerActors_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerChild.cpp
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientManagerChild.h"
+
+#include "ClientHandleChild.h"
+#include "ClientManagerOpChild.h"
+#include "ClientNavigateOpChild.h"
+#include "ClientSourceChild.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::dom::workers::WorkerHolderToken;
+using mozilla::dom::workers::WorkerPrivate;
+
+void
+ClientManagerChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mWorkerHolderToken) {
+    mWorkerHolderToken->RemoveListener(this);
+    mWorkerHolderToken = nullptr;
+
+  }
+
+  if (mManager) {
+    mManager->RevokeActor(this);
+
+    // Revoking the actor link should automatically cause the owner
+    // to call RevokeOwner() as well.
+    MOZ_DIAGNOSTIC_ASSERT(!mManager);
+  }
+}
+
+PClientHandleChild*
+ClientManagerChild::AllocPClientHandleChild(const IPCClientInfo& aClientInfo)
+{
+  return new ClientHandleChild();
+}
+
+bool
+ClientManagerChild::DeallocPClientHandleChild(PClientHandleChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PClientManagerOpChild*
+ClientManagerChild::AllocPClientManagerOpChild(const ClientOpConstructorArgs& aArgs)
+{
+  MOZ_ASSERT_UNREACHABLE("ClientManagerOpChild must be explicitly constructed.");
+  return nullptr;
+}
+
+bool
+ClientManagerChild::DeallocPClientManagerOpChild(PClientManagerOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PClientNavigateOpChild*
+ClientManagerChild::AllocPClientNavigateOpChild(const ClientNavigateOpConstructorArgs& aArgs)
+{
+  return new ClientNavigateOpChild();
+}
+
+bool
+ClientManagerChild::DeallocPClientNavigateOpChild(PClientNavigateOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+mozilla::ipc::IPCResult
+ClientManagerChild::RecvPClientNavigateOpConstructor(PClientNavigateOpChild* aActor,
+                                                     const ClientNavigateOpConstructorArgs& aArgs)
+{
+  auto actor = static_cast<ClientNavigateOpChild*>(aActor);
+  actor->Init(aArgs);
+  return IPC_OK();
+}
+
+PClientSourceChild*
+ClientManagerChild::AllocPClientSourceChild(const ClientSourceConstructorArgs& aArgs)
+{
+  return new ClientSourceChild(aArgs);
+}
+
+bool
+ClientManagerChild::DeallocPClientSourceChild(PClientSourceChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+void
+ClientManagerChild::WorkerShuttingDown()
+{
+  MaybeStartTeardown();
+}
+
+ClientManagerChild::ClientManagerChild(WorkerHolderToken* aWorkerHolderToken)
+  : mManager(nullptr)
+  , mWorkerHolderToken(aWorkerHolderToken)
+  , mTeardownStarted(false)
+{
+  MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolderToken);
+
+  if (mWorkerHolderToken) {
+    mWorkerHolderToken->AddListener(this);
+  }
+}
+
+void
+ClientManagerChild::SetOwner(ClientThing<ClientManagerChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aThing);
+  MOZ_DIAGNOSTIC_ASSERT(!mManager);
+  mManager = aThing;
+}
+
+void
+ClientManagerChild::RevokeOwner(ClientThing<ClientManagerChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager == aThing);
+  mManager = nullptr;
+}
+
+void
+ClientManagerChild::MaybeStartTeardown()
+{
+  if (mTeardownStarted) {
+    return;
+  }
+  mTeardownStarted = true;
+  SendTeardown();
+}
+
+WorkerPrivate*
+ClientManagerChild::GetWorkerPrivate() const
+{
+  if (!mWorkerHolderToken) {
+    return nullptr;
+  }
+  return mWorkerHolderToken->GetWorkerPrivate();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerChild.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientManagerChild_h
+#define _mozilla_dom_ClientManagerChild_h
+
+#include "ClientThing.h"
+#include "mozilla/dom/PClientManagerChild.h"
+#include "mozilla/dom/workers/bindings/WorkerHolderToken.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerPrivate;
+} // workers namespace
+
+class ClientManagerChild final : public PClientManagerChild
+                               , public mozilla::dom::workers::WorkerHolderToken::Listener
+{
+  ClientThing<ClientManagerChild>* mManager;
+
+  RefPtr<mozilla::dom::workers::WorkerHolderToken> mWorkerHolderToken;
+  bool mTeardownStarted;
+
+  // PClientManagerChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientHandleChild*
+  AllocPClientHandleChild(const IPCClientInfo& aClientInfo) override;
+
+  bool
+  DeallocPClientHandleChild(PClientHandleChild* aActor) override;
+
+  PClientManagerOpChild*
+  AllocPClientManagerOpChild(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientManagerOpChild(PClientManagerOpChild* aActor) override;
+
+  PClientNavigateOpChild*
+  AllocPClientNavigateOpChild(const ClientNavigateOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientNavigateOpChild(PClientNavigateOpChild* aActor) override;
+
+  mozilla::ipc::IPCResult
+  RecvPClientNavigateOpConstructor(PClientNavigateOpChild* aActor,
+                                   const ClientNavigateOpConstructorArgs& aArgs) override;
+
+  PClientSourceChild*
+  AllocPClientSourceChild(const ClientSourceConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientSourceChild(PClientSourceChild* aActor) override;
+
+  // WorkerHolderToken::Listener interface
+  void
+  WorkerShuttingDown() override;
+
+public:
+  explicit ClientManagerChild(workers::WorkerHolderToken* aWorkerHolderToken);
+
+  void
+  SetOwner(ClientThing<ClientManagerChild>* aThing);
+
+  void
+  RevokeOwner(ClientThing<ClientManagerChild>* aThing);
+
+  void
+  MaybeStartTeardown();
+
+  mozilla::dom::workers::WorkerPrivate*
+  GetWorkerPrivate() const;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientManagerChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerOpChild.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientManagerOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientManagerOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mPromise) {
+    mPromise->Reject(NS_ERROR_ABORT, __func__);
+    mPromise = nullptr;
+  }
+}
+
+IPCResult
+ClientManagerOpChild::Recv__delete__(const ClientOpResult& aResult)
+{
+  if (aResult.type() == ClientOpResult::Tnsresult &&
+      NS_FAILED(aResult.get_nsresult())) {
+    mPromise->Reject(aResult.get_nsresult(), __func__);
+    mPromise = nullptr;
+    return IPC_OK();
+  }
+  mPromise->Resolve(aResult, __func__);
+  mPromise = nullptr;
+  return IPC_OK();
+}
+
+ClientManagerOpChild::ClientManagerOpChild(const ClientOpConstructorArgs& aArgs,
+                                           ClientOpPromise::Private* aPromise)
+  : mPromise(aPromise)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
+}
+
+ClientManagerOpChild::~ClientManagerOpChild()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerOpChild.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientManagerOpChild_h
+#define _mozilla_dom_ClientManagerOpChild_h
+
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/PClientManagerOpChild.h"
+#include "mozilla/MozPromise.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientManagerOpChild final : public PClientManagerOpChild
+{
+  RefPtr<ClientOpPromise::Private> mPromise;
+
+  // PClientManagerOpChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const ClientOpResult& aResult) override;
+
+public:
+  ClientManagerOpChild(const ClientOpConstructorArgs& aArgs,
+                       ClientOpPromise::Private* aPromise);
+
+  ~ClientManagerOpChild();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientManagerOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerOpParent.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientManagerOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientManagerOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+void
+ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerOpParent.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientManagerOpParent_h
+#define _mozilla_dom_ClientManagerOpParent_h
+
+#include "mozilla/dom/PClientManagerOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientManagerService;
+
+class ClientManagerOpParent final : public PClientManagerOpParent
+{
+  // PClientManagerOpParent interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+public:
+  ClientManagerOpParent() = default;
+  ~ClientManagerOpParent() = default;
+
+  void
+  Init(const ClientOpConstructorArgs& aArgs);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientManagerOpParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerParent.cpp
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientManagerParent.h"
+
+#include "ClientHandleParent.h"
+#include "ClientManagerOpParent.h"
+#include "ClientSourceParent.h"
+#include "mozilla/dom/PClientNavigateOpParent.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+IPCResult
+ClientManagerParent::RecvTeardown()
+{
+  Unused << Send__delete__(this);
+  return IPC_OK();
+}
+
+void
+ClientManagerParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+PClientHandleParent*
+ClientManagerParent::AllocPClientHandleParent(const IPCClientInfo& aClientInfo)
+{
+  return new ClientHandleParent();
+}
+
+bool
+ClientManagerParent::DeallocPClientHandleParent(PClientHandleParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+IPCResult
+ClientManagerParent::RecvPClientHandleConstructor(PClientHandleParent* aActor,
+                                                  const IPCClientInfo& aClientInfo)
+{
+  ClientHandleParent* actor = static_cast<ClientHandleParent*>(aActor);
+  actor->Init(aClientInfo);
+  return IPC_OK();
+}
+
+PClientManagerOpParent*
+ClientManagerParent::AllocPClientManagerOpParent(const ClientOpConstructorArgs& aArgs)
+{
+  return new ClientManagerOpParent();
+}
+
+bool
+ClientManagerParent::DeallocPClientManagerOpParent(PClientManagerOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+IPCResult
+ClientManagerParent::RecvPClientManagerOpConstructor(PClientManagerOpParent* aActor,
+                                                     const ClientOpConstructorArgs& aArgs)
+{
+  ClientManagerOpParent* actor = static_cast<ClientManagerOpParent*>(aActor);
+  actor->Init(aArgs);
+  return IPC_OK();
+}
+
+PClientNavigateOpParent*
+ClientManagerParent::AllocPClientNavigateOpParent(const ClientNavigateOpConstructorArgs& aArgs)
+{
+  MOZ_ASSERT_UNREACHABLE("ClientNavigateOpParent should be explicitly constructed.");
+  return nullptr;
+}
+
+bool
+ClientManagerParent::DeallocPClientNavigateOpParent(PClientNavigateOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PClientSourceParent*
+ClientManagerParent::AllocPClientSourceParent(const ClientSourceConstructorArgs& aArgs)
+{
+  return new ClientSourceParent(aArgs);
+}
+
+bool
+ClientManagerParent::DeallocPClientSourceParent(PClientSourceParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+ClientManagerParent::ClientManagerParent()
+{
+}
+
+ClientManagerParent::~ClientManagerParent()
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientManagerParent.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientManagerParent_h
+#define _mozilla_dom_ClientManagerParent_h
+
+#include "mozilla/dom/PClientManagerParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientManagerParent final : public PClientManagerParent
+{
+  // PClientManagerParent interface
+  mozilla::ipc::IPCResult
+  RecvTeardown() override;
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientHandleParent*
+  AllocPClientHandleParent(const IPCClientInfo& aClientInfo) override;
+
+  bool
+  DeallocPClientHandleParent(PClientHandleParent* aActor) override;
+
+  mozilla::ipc::IPCResult
+  RecvPClientHandleConstructor(PClientHandleParent* aActor,
+                               const IPCClientInfo& aClientInfo) override;
+
+  PClientManagerOpParent*
+  AllocPClientManagerOpParent(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientManagerOpParent(PClientManagerOpParent* aActor) override;
+
+  mozilla::ipc::IPCResult
+  RecvPClientManagerOpConstructor(PClientManagerOpParent* aActor,
+                                 const ClientOpConstructorArgs& aArgs) override;
+
+  PClientNavigateOpParent*
+  AllocPClientNavigateOpParent(const ClientNavigateOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientNavigateOpParent(PClientNavigateOpParent* aActor) override;
+
+  PClientSourceParent*
+  AllocPClientSourceParent(const ClientSourceConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientSourceParent(PClientSourceParent* aActor) override;
+
+public:
+  ClientManagerParent();
+  ~ClientManagerParent();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientManagerParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientNavigateOpChild.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientNavigateOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientNavigateOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+void
+ClientNavigateOpChild::Init(const ClientNavigateOpConstructorArgs& aArgs)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientNavigateOpChild.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientNavigateOpChild_h
+#define _mozilla_dom_ClientNavigateOpChild_h
+
+#include "mozilla/dom/PClientNavigateOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientNavigateOpChild final : public PClientNavigateOpChild
+{
+  // PClientNavigateOpChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+public:
+  ClientNavigateOpChild() = default;
+  ~ClientNavigateOpChild() = default;
+
+  void
+  Init(const ClientNavigateOpConstructorArgs& aArgs);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientNavigateOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientNavigateOpParent.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientNavigateOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+void
+ClientNavigateOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+IPCResult
+ClientNavigateOpParent::Recv__delete__(const ClientOpResult& aResult)
+{
+  if (aResult.type() == ClientOpResult::Tnsresult &&
+      NS_FAILED(aResult.get_nsresult())) {
+    mPromise->Reject(aResult.get_nsresult(), __func__);
+    mPromise = nullptr;
+    return IPC_OK();
+  }
+  mPromise->Resolve(aResult, __func__);
+  mPromise = nullptr;
+  return IPC_OK();
+}
+
+ClientNavigateOpParent::ClientNavigateOpParent(const ClientNavigateOpConstructorArgs& aArgs,
+                                               ClientOpPromise::Private* aPromise)
+  : mPromise(aPromise)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
+}
+
+ClientNavigateOpParent::~ClientNavigateOpParent()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientNavigateOpParent.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientNavigateOpParent_h
+#define _mozilla_dom_ClientNavigateOpParent_h
+
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/PClientNavigateOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientNavigateOpParent final : public PClientNavigateOpParent
+{
+  RefPtr<ClientOpPromise::Private> mPromise;
+
+  // PClientNavigateOpParent interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const ClientOpResult& aResult) override;
+
+public:
+  ClientNavigateOpParent(const ClientNavigateOpConstructorArgs& aArgs,
+                         ClientOpPromise::Private* aPromise);
+
+  ~ClientNavigateOpParent();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientNavigateOpParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpPromise.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientOpPromise_h
+#define _mozilla_dom_ClientOpPromise_h
+
+#include "mozilla/MozPromise.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientOpResult;
+class ClientState;
+
+typedef MozPromise<ClientOpResult, nsresult, false> ClientOpPromise;
+
+typedef MozPromise<ClientState, nsresult, false> ClientStatePromise;
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientOpPromise_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpActors.cpp
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientOpenWindowOpActors.h"
+
+#include "ClientOpenWindowOpChild.h"
+#include "mozilla/dom/PClientOpenWindowOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+PClientOpenWindowOpChild*
+AllocClientOpenWindowOpChild()
+{
+  return new ClientOpenWindowOpChild();
+}
+
+void
+InitClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor,
+                            const ClientOpenWindowArgs& aArgs)
+{
+  auto actor = static_cast<ClientOpenWindowOpChild*>(aActor);
+  actor->Init(aArgs);
+}
+
+bool
+DeallocClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+PClientOpenWindowOpParent*
+AllocClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs)
+{
+  MOZ_CRASH("ClientOpenWindowOpParent must be explicitly allocated");
+  return nullptr;
+}
+
+bool
+DeallocClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpActors.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientOpenWindowOpActors_h
+#define _mozilla_dom_ClientOpenWindowOpActors_h
+
+namespace mozilla {
+namespace dom {
+
+class ClientOpenWindowArgs;
+class PClientOpenWindowOpChild;
+class PClientOpenWindowOpParent;
+
+PClientOpenWindowOpChild*
+AllocClientOpenWindowOpChild();
+
+void
+InitClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor,
+                            const ClientOpenWindowArgs& aArgs);
+
+bool
+DeallocClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor);
+
+PClientOpenWindowOpParent*
+AllocClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs);
+
+bool
+DeallocClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor);
+
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientOpenWindowOpActors_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpChild.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientOpenWindowOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientOpenWindowOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+void
+ClientOpenWindowOpChild::Init(const ClientOpenWindowArgs& aArgs)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpChild.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientOpenWindowOpChild_h
+#define _mozilla_dom_ClientOpenWindowOpChild_h
+
+#include "mozilla/dom/PClientOpenWindowOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientOpenWindowOpChild final : public PClientOpenWindowOpChild
+{
+  // PClientOpenWindowOpChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+public:
+  ClientOpenWindowOpChild() = default;
+  ~ClientOpenWindowOpChild() = default;
+
+  void
+  Init(const ClientOpenWindowArgs& aArgs);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientOpenWindowOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpParent.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientOpenWindowOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+void
+ClientOpenWindowOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mPromise) {
+    mPromise->Reject(NS_ERROR_ABORT, __func__);
+    mPromise = nullptr;
+  }
+}
+
+IPCResult
+ClientOpenWindowOpParent::Recv__delete__(const ClientOpResult& aResult)
+{
+  if (aResult.type() == ClientOpResult::Tnsresult &&
+      NS_FAILED(aResult.get_nsresult())) {
+    mPromise->Reject(aResult.get_nsresult(), __func__);
+    mPromise = nullptr;
+    return IPC_OK();
+  }
+  mPromise->Resolve(aResult, __func__);
+  mPromise = nullptr;
+  return IPC_OK();
+}
+
+ClientOpenWindowOpParent::ClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs,
+                                                   ClientOpPromise::Private* aPromise)
+  : mPromise(aPromise)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
+}
+
+ClientOpenWindowOpParent::~ClientOpenWindowOpParent()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientOpenWindowOpParent.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientOpenWindowOpParent_h
+#define _mozilla_dom_ClientOpenWindowOpParent_h
+
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/PClientOpenWindowOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientOpenWindowOpParent final : public PClientOpenWindowOpParent
+{
+  RefPtr<ClientOpPromise::Private> mPromise;
+
+  // PClientOpenWindowOpParent interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const ClientOpResult& aResult) override;
+
+public:
+  ClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs,
+                           ClientOpPromise::Private* aPromise);
+
+  ~ClientOpenWindowOpParent();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientOpenWindowOpParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceChild.cpp
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientSourceChild.h"
+
+#include "ClientSourceOpChild.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+void
+ClientSourceChild::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mSource) {
+    mSource->RevokeActor(this);
+
+    // Revoking the actor link should automatically cause the owner
+    // to call RevokeOwner() as well.
+    MOZ_DIAGNOSTIC_ASSERT(!mSource);
+  }
+}
+
+PClientSourceOpChild*
+ClientSourceChild::AllocPClientSourceOpChild(const ClientOpConstructorArgs& aArgs)
+{
+  return new ClientSourceOpChild();
+}
+
+bool
+ClientSourceChild::DeallocPClientSourceOpChild(PClientSourceOpChild* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+IPCResult
+ClientSourceChild::RecvPClientSourceOpConstructor(PClientSourceOpChild* aActor,
+                                                  const ClientOpConstructorArgs& aArgs)
+{
+  auto actor = static_cast<ClientSourceOpChild*>(aActor);
+  actor->Init(aArgs);
+  return IPC_OK();
+}
+
+ClientSourceChild::ClientSourceChild(const ClientSourceConstructorArgs& aArgs)
+  : mSource(nullptr)
+  , mTeardownStarted(false)
+{
+}
+
+void
+ClientSourceChild::SetOwner(ClientThing<ClientSourceChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aThing);
+  MOZ_DIAGNOSTIC_ASSERT(!mSource);
+  mSource = aThing;
+}
+
+void
+ClientSourceChild::RevokeOwner(ClientThing<ClientSourceChild>* aThing)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mSource);
+  MOZ_DIAGNOSTIC_ASSERT(mSource == aThing);
+  mSource = nullptr;
+}
+
+void
+ClientSourceChild::MaybeStartTeardown()
+{
+  if (mTeardownStarted) {
+    return;
+  }
+  mTeardownStarted = true;
+  Unused << SendTeardown();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceChild.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientSourceChild_h
+#define _mozilla_dom_ClientSourceChild_h
+
+#include "mozilla/dom/PClientSourceChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientSourceConstructorArgs;
+template <typename ActorType> class ClientThing;
+
+class ClientSourceChild final : public PClientSourceChild
+{
+  ClientThing<ClientSourceChild>* mSource;
+  bool mTeardownStarted;
+
+  // PClientSourceChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientSourceOpChild*
+  AllocPClientSourceOpChild(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientSourceOpChild(PClientSourceOpChild* aActor) override;
+
+  mozilla::ipc::IPCResult
+  RecvPClientSourceOpConstructor(PClientSourceOpChild* aActor,
+                                 const ClientOpConstructorArgs& aArgs) override;
+
+public:
+  explicit ClientSourceChild(const ClientSourceConstructorArgs& aArgs);
+
+  void
+  SetOwner(ClientThing<ClientSourceChild>* aThing);
+
+  void
+  RevokeOwner(ClientThing<ClientSourceChild>* aThing);
+
+  void
+  MaybeStartTeardown();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientSourceChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceOpChild.cpp
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientSourceOpChild.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+ClientSourceOpChild::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+void
+ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceOpChild.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientSourceOpChild_h
+#define _mozilla_dom_ClientSourceOpChild_h
+
+#include "mozilla/dom/PClientSourceOpChild.h"
+#include "ClientOpPromise.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientSourceOpChild final : public PClientSourceOpChild
+{
+  // PClientSourceOpChild interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+public:
+  ClientSourceOpChild() = default;
+  ~ClientSourceOpChild() = default;
+
+  void
+  Init(const ClientOpConstructorArgs& aArgs);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientSourceOpChild_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceOpParent.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientSourceOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+
+void
+ClientSourceOpParent::ActorDestroy(ActorDestroyReason aReason)
+{
+  if (mPromise) {
+    mPromise->Reject(NS_ERROR_ABORT, __func__);
+    mPromise = nullptr;
+  }
+}
+
+IPCResult
+ClientSourceOpParent::Recv__delete__(const ClientOpResult& aResult)
+{
+  if (aResult.type() == ClientOpResult::Tnsresult &&
+      NS_FAILED(aResult.get_nsresult())) {
+    mPromise->Reject(aResult.get_nsresult(), __func__);
+    mPromise = nullptr;
+    return IPC_OK();
+  }
+  mPromise->Resolve(aResult, __func__);
+  mPromise = nullptr;
+  return IPC_OK();
+}
+
+ClientSourceOpParent::ClientSourceOpParent(const ClientOpConstructorArgs& aArgs,
+                                           ClientOpPromise::Private* aPromise)
+  : mPromise(aPromise)
+{
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
+}
+
+ClientSourceOpParent::~ClientSourceOpParent()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceOpParent.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientSourceOpParent_h
+#define _mozilla_dom_ClientSourceOpParent_h
+
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/PClientSourceOpParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientSourceOpParent final : public PClientSourceOpParent
+{
+  RefPtr<ClientOpPromise::Private> mPromise;
+
+  // PClientSourceOpParent interface
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  mozilla::ipc::IPCResult
+  Recv__delete__(const ClientOpResult& aResult) override;
+
+public:
+  ClientSourceOpParent(const ClientOpConstructorArgs& aArgs,
+                       ClientOpPromise::Private* aPromise);
+
+  ~ClientSourceOpParent();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientSourceOpParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceParent.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ClientSourceParent.h"
+
+#include "ClientHandleParent.h"
+#include "ClientSourceOpParent.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+namespace dom {
+
+using mozilla::ipc::IPCResult;
+using mozilla::ipc::PrincipalInfo;
+
+IPCResult
+ClientSourceParent::RecvTeardown()
+{
+  Unused << Send__delete__(this);
+  return IPC_OK();
+}
+
+void
+ClientSourceParent::ActorDestroy(ActorDestroyReason aReason)
+{
+}
+
+PClientSourceOpParent*
+ClientSourceParent::AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs)
+{
+  MOZ_ASSERT_UNREACHABLE("ClientSourceOpParent should be explicitly constructed.");
+  return nullptr;
+}
+
+bool
+ClientSourceParent::DeallocPClientSourceOpParent(PClientSourceOpParent* aActor)
+{
+  delete aActor;
+  return true;
+}
+
+ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
+{
+}
+
+ClientSourceParent::~ClientSourceParent()
+{
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientSourceParent.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientSourceParent_h
+#define _mozilla_dom_ClientSourceParent_h
+
+#include "mozilla/dom/PClientSourceParent.h"
+
+namespace mozilla {
+namespace dom {
+
+class ClientSourceParent final : public PClientSourceParent
+{
+  // PClientSourceParent
+  IPCResult
+  RecvTeardown() override;
+
+  void
+  ActorDestroy(ActorDestroyReason aReason) override;
+
+  PClientSourceOpParent*
+  AllocPClientSourceOpParent(const ClientOpConstructorArgs& aArgs) override;
+
+  bool
+  DeallocPClientSourceOpParent(PClientSourceOpParent* aActor) override;
+
+public:
+  explicit ClientSourceParent(const ClientSourceConstructorArgs& aArgs);
+  ~ClientSourceParent();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientSourceParent_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/ClientThing.h
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _mozilla_dom_ClientThing_h
+#define _mozilla_dom_ClientThing_h
+
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+// Base class representing various Client "things" such as ClientHandle,
+// ClientSource, and ClientManager.  Currently it provides a common set
+// of code for handling activation and shutdown of IPC actors.
+template <typename ActorType>
+class ClientThing
+{
+  ActorType* mActor;
+  bool mShutdown;
+
+protected:
+  ClientThing()
+    : mActor(nullptr)
+    , mShutdown(false)
+  {
+  }
+
+  ~ClientThing()
+  {
+    ShutdownThing();
+  }
+
+  // Return the current actor.
+  ActorType*
+  GetActor() const
+  {
+    return mActor;
+  }
+
+  // Returns true if ShutdownThing() has been called.
+  bool
+  IsShutdown() const
+  {
+    return mShutdown;
+  }
+
+  // Conditionally execute the given callable based on the current state.
+  template<typename Callable>
+  void
+  MaybeExecute(const Callable& aCallable)
+  {
+    if (mShutdown) {
+      return;
+    }
+    MOZ_DIAGNOSTIC_ASSERT(mActor);
+    aCallable(mActor);
+  }
+
+  // Attach activate the thing by attaching its underlying IPC actor.  This
+  // will make the thing register as the actor's owner as well.  The actor
+  // must call RevokeActor() to clear this weak back reference before its
+  // destroyed.
+  void
+  ActivateThing(ActorType* aActor)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(aActor);
+    MOZ_DIAGNOSTIC_ASSERT(!mActor);
+    MOZ_DIAGNOSTIC_ASSERT(!mShutdown);
+    mActor = aActor;
+    mActor->SetOwner(this);
+  }
+
+  // Start destroying the underlying actor and disconnect the thing.
+  void
+  ShutdownThing()
+  {
+    if (mShutdown) {
+      return;
+    }
+    mShutdown = true;
+
+    // If we are shutdown before the actor, then clear the weak references
+    // between the actor and the thing.
+    if (mActor) {
+      mActor->RevokeOwner(this);
+      mActor->MaybeStartTeardown();
+      mActor = nullptr;
+    }
+  }
+
+public:
+  // Clear the weak references between the thing and its IPC actor.
+  void
+  RevokeActor(ActorType* aActor)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mActor);
+    MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
+    mActor->RevokeOwner(this);
+    mActor = nullptr;
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // _mozilla_dom_ClientThing_h
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientHandle.ipdl
@@ -0,0 +1,32 @@
+/* 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 protocol PClientManager;
+include protocol PClientHandleOp;
+include protocol PIPCBlobInputStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol PFileDescriptorSet;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientHandle
+{
+  manager PClientManager;
+
+  manages PClientHandleOp;
+
+parent:
+  async Teardown();
+
+  async PClientHandleOp(ClientOpConstructorArgs aArgs);
+
+child:
+  async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientHandleOp.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PClientHandle;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientHandleOp
+{
+  manager PClientHandle;
+
+child:
+  async __delete__(ClientOpResult aResult);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientManager.ipdl
@@ -0,0 +1,42 @@
+/* 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 protocol PBackground;
+include protocol PClientHandle;
+include protocol PClientManagerOp;
+include protocol PClientNavigateOp;
+include protocol PClientSource;
+include protocol PIPCBlobInputStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol PFileDescriptorSet;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PClientManager
+{
+  manager PBackground;
+
+  manages PClientHandle;
+  manages PClientManagerOp;
+  manages PClientNavigateOp;
+  manages PClientSource;
+
+parent:
+  async Teardown();
+
+  async PClientHandle(IPCClientInfo aClientInfo);
+  async PClientManagerOp(ClientOpConstructorArgs aArgs);
+  async PClientSource(ClientSourceConstructorArgs aArgs);
+
+child:
+  async PClientNavigateOp(ClientNavigateOpConstructorArgs aArgs);
+
+  async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientManagerOp.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PClientManager;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientManagerOp
+{
+  manager PClientManager;
+
+child:
+  async __delete__(ClientOpResult aResult);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientNavigateOp.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PClientManager;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientNavigateOp
+{
+  manager PClientManager;
+
+parent:
+  async __delete__(ClientOpResult aResult);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientOpenWindowOp.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PContent;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientOpenWindowOp
+{
+  manager PContent;
+
+parent:
+  async __delete__(ClientOpResult aResult);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientSource.ipdl
@@ -0,0 +1,32 @@
+/* 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 protocol PClientManager;
+include protocol PClientSourceOp;
+include protocol PIPCBlobInputStream;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol PFileDescriptorSet;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+sync protocol PClientSource
+{
+  manager PClientManager;
+
+  manages PClientSourceOp;
+
+parent:
+  async Teardown();
+
+child:
+  async PClientSourceOp(ClientOpConstructorArgs aArgs);
+
+  async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/clients/manager/PClientSourceOp.ipdl
@@ -0,0 +1,20 @@
+/* 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 protocol PClientSource;
+include ClientIPCTypes;
+
+namespace mozilla {
+namespace dom {
+
+protocol PClientSourceOp
+{
+  manager PClientSource;
+
+parent:
+  async __delete__(ClientOpResult aResult);
+};
+
+} // namespace dom
+} // namespace mozilla
--- a/dom/clients/manager/moz.build
+++ b/dom/clients/manager/moz.build
@@ -2,26 +2,56 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
   'ClientInfo.h',
   'ClientIPCUtils.h',
+  'ClientManagerActors.h',
+  'ClientOpenWindowOpActors.h',
+  'ClientOpPromise.h',
   'ClientState.h',
+  'ClientThing.h',
 ]
 
 UNIFIED_SOURCES += [
+  'ClientHandleChild.cpp',
+  'ClientHandleOpChild.cpp',
+  'ClientHandleOpParent.cpp',
+  'ClientHandleParent.cpp',
   'ClientInfo.cpp',
+  'ClientManagerActors.cpp',
+  'ClientManagerChild.cpp',
+  'ClientManagerOpChild.cpp',
+  'ClientManagerOpParent.cpp',
+  'ClientManagerParent.cpp',
+  'ClientNavigateOpChild.cpp',
+  'ClientNavigateOpParent.cpp',
+  'ClientOpenWindowOpActors.cpp',
+  'ClientOpenWindowOpChild.cpp',
+  'ClientOpenWindowOpParent.cpp',
+  'ClientSourceChild.cpp',
+  'ClientSourceOpChild.cpp',
+  'ClientSourceOpParent.cpp',
+  'ClientSourceParent.cpp',
   'ClientState.cpp',
 ]
 
 IPDL_SOURCES += [
   'ClientIPCTypes.ipdlh',
+  'PClientHandle.ipdl',
+  'PClientHandleOp.ipdl',
+  'PClientManager.ipdl',
+  'PClientManagerOp.ipdl',
+  'PClientNavigateOp.ipdl',
+  'PClientOpenWindowOp.ipdl',
+  'PClientSource.ipdl',
+  'PClientSourceOp.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
 ]
 
 FINAL_LIBRARY = 'xul'
--- a/dom/gamepad/android/AndroidGamepad.cpp
+++ b/dom/gamepad/android/AndroidGamepad.cpp
@@ -70,18 +70,20 @@ public:
       }
     }
   }
 };
 
 void StartGamepadMonitoring()
 {
   AndroidGamepadManager::Init();
-  java::AndroidGamepadManager::Start();
+  java::AndroidGamepadManager::Start(
+      java::GeckoAppShell::GetApplicationContext());
 }
 
 void StopGamepadMonitoring()
 {
-  java::AndroidGamepadManager::Stop();
+  java::AndroidGamepadManager::Stop(
+      java::GeckoAppShell::GetApplicationContext());
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLEmbedElement.cpp
+++ b/dom/html/HTMLEmbedElement.cpp
@@ -58,17 +58,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLEmbedElement,
                                              nsGenericHTMLElement,
                                              nsIRequestObserver,
                                              nsIStreamListener,
                                              nsIFrameLoaderOwner,
                                              nsIObjectLoadingContent,
                                              imgINotificationObserver,
                                              nsIImageLoadingContent,
-                                             imgIOnloadBlocker,
                                              nsIChannelEventSink)
 
 NS_IMPL_ELEMENT_CLONE(HTMLEmbedElement)
 
 #ifdef XP_MACOSX
 
 NS_IMETHODIMP
 HTMLEmbedElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -134,17 +134,16 @@ HTMLImageElement::~HTMLImageElement()
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLImageElement,
                                    nsGenericHTMLElement,
                                    mResponsiveSelector)
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLImageElement,
                                              nsGenericHTMLElement,
                                              nsIImageLoadingContent,
-                                             imgIOnloadBlocker,
                                              imgINotificationObserver)
 
 NS_IMPL_ELEMENT_CLONE(HTMLImageElement)
 
 
 bool
 HTMLImageElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLImageElement_h
 #define mozilla_dom_HTMLImageElement_h
 
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLElement.h"
 #include "nsImageLoadingContent.h"
-#include "imgRequestProxy.h"
 #include "Units.h"
 #include "nsCycleCollectionParticipant.h"
 
 namespace mozilla {
 class EventChainPreVisitor;
 namespace dom {
 
 class ImageLoadTask;
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1248,17 +1248,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLInputElement,
                                              nsGenericHTMLFormElementWithState,
                                              nsIDOMHTMLInputElement,
                                              nsITextControlElement,
                                              imgINotificationObserver,
                                              nsIImageLoadingContent,
-                                             imgIOnloadBlocker,
                                              nsIDOMNSEditableElement,
                                              nsIConstraintValidation)
 
 // nsIDOMNode
 
 nsresult
 HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
                         bool aPreallocateArrays) const
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -99,17 +99,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLObjectElement,
                                              nsGenericHTMLFormElement,
                                              imgINotificationObserver,
                                              nsIRequestObserver,
                                              nsIStreamListener,
                                              nsIFrameLoaderOwner,
                                              nsIObjectLoadingContent,
                                              nsIImageLoadingContent,
-                                             imgIOnloadBlocker,
                                              nsIChannelEventSink,
                                              nsIConstraintValidation)
 
 NS_IMPL_ELEMENT_CLONE(HTMLObjectElement)
 
 #ifdef XP_MACOSX
 
 static nsIWidget* GetWidget(Element* aElement)
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -17,16 +17,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/Unused.h"
 #include "mozilla/TelemetryIPC.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
+#include "mozilla/dom/ClientOpenWindowOpActors.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/VideoDecoderManagerChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/FileCreatorHelper.h"
@@ -3575,16 +3576,36 @@ ContentChild::RecvSetPluginList(const ui
                                 nsTArray<plugins::PluginTag>&& aPluginTags,
                                 nsTArray<plugins::FakePluginTag>&& aFakePluginTags)
 {
   RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
   host->SetPluginsInContent(aPluginEpoch, aPluginTags, aFakePluginTags);
   return IPC_OK();
 }
 
+PClientOpenWindowOpChild*
+ContentChild::AllocPClientOpenWindowOpChild(const ClientOpenWindowArgs& aArgs)
+{
+  return AllocClientOpenWindowOpChild();
+}
+
+IPCResult
+ContentChild::RecvPClientOpenWindowOpConstructor(PClientOpenWindowOpChild* aActor,
+                                                 const ClientOpenWindowArgs& aArgs)
+{
+  InitClientOpenWindowOpChild(aActor, aArgs);
+  return IPC_OK();
+}
+
+bool
+ContentChild::DeallocPClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor)
+{
+  return DeallocClientOpenWindowOpChild(aActor);
+}
+
 mozilla::ipc::IPCResult
 ContentChild::RecvShareCodeCoverageMutex(const CrossProcessMutexHandle& aHandle)
 {
 #ifdef MOZ_CODE_COVERAGE
   CodeCoverageHandler::Init(aHandle);
   return IPC_OK();
 #else
   MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -691,16 +691,26 @@ public:
 
   virtual already_AddRefed<nsIEventTarget> GetEventTargetFor(TabChild* aTabChild) override;
 
   mozilla::ipc::IPCResult
   RecvSetPluginList(const uint32_t& aPluginEpoch,
                     nsTArray<PluginTag>&& aPluginTags,
                     nsTArray<FakePluginTag>&& aFakePluginTags) override;
 
+  virtual PClientOpenWindowOpChild*
+  AllocPClientOpenWindowOpChild(const ClientOpenWindowArgs& aArgs) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPClientOpenWindowOpConstructor(PClientOpenWindowOpChild* aActor,
+                                     const ClientOpenWindowArgs& aArgs) override;
+
+  virtual bool
+  DeallocPClientOpenWindowOpChild(PClientOpenWindowOpChild* aActor) override;
+
 #ifdef NIGHTLY_BUILD
   // Fetch the current number of pending input events.
   //
   // NOTE: This method performs an atomic read, and is safe to call from all threads.
   uint32_t
   GetPendingInputEvents()
   {
     return mPendingInputEvents;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/a11y/Compatibility.h"
 #endif
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
+#include "mozilla/dom/ClientOpenWindowOpActors.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileCreatorHelper.h"
 #include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/IPCBlobUtils.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
@@ -2511,16 +2512,28 @@ ContentParent::OnCompositorUnexpectedShu
 }
 
 void
 ContentParent::OnCompositorDeviceReset()
 {
   Unused << SendReinitRenderingForDeviceReset();
 }
 
+PClientOpenWindowOpParent*
+ContentParent::AllocPClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs)
+{
+  return AllocClientOpenWindowOpParent(aArgs);
+}
+
+bool
+ContentParent::DeallocPClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor)
+{
+  return DeallocClientOpenWindowOpParent(aActor);
+}
+
 void
 ContentParent::MaybeEnableRemoteInputEventQueue()
 {
   MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
   if (!IsInputEventQueueSupported()) {
     return;
   }
   mIsRemoteInputEventQueueEnabled = true;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -631,16 +631,22 @@ public:
   // place to start to kick off work as early as possible in response to such
   // document loads.
   nsresult AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel);
 
   nsresult TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal);
 
   void OnCompositorDeviceReset() override;
 
+  virtual PClientOpenWindowOpParent*
+  AllocPClientOpenWindowOpParent(const ClientOpenWindowArgs& aArgs) override;
+
+  virtual bool
+  DeallocPClientOpenWindowOpParent(PClientOpenWindowOpParent* aActor) override;
+
   // Control the priority of the IPC messages for input events.
   void SetInputPriorityEventEnabled(bool aEnabled);
   bool IsInputPriorityEventEnabled()
   {
     return mIsInputPriorityEventEnabled;
   }
 
   static bool IsInputEventQueueSupported();
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
 /* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
 /* 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 protocol PBackground;
 include protocol PBrowser;
+include protocol PClientOpenWindowOp;
 include protocol PCompositorManager;
 include protocol PContentBridge;
 include protocol PContentPermissionRequest;
 include protocol PCycleCollectWithLogs;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PFileDescriptorSet;
@@ -53,16 +54,17 @@ include PTabContext;
 include URIParams;
 include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
 include PContentPermission;
 include ServiceWorkerConfiguration;
 include GraphicsMessages;
 include MemoryReportTypes;
+include ClientIPCTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 using AlertNotificationType from "mozilla/AlertNotificationIPCSerializer.h";
@@ -270,16 +272,17 @@ struct XPCOMInitData
 /**
  * The PContent protocol is a top-level protocol between the UI process
  * and a content process. There is exactly one PContentParent/PContentChild pair
  * for each content process.
  */
 nested(upto inside_cpow) sync protocol PContent
 {
     manages PBrowser;
+    manages PClientOpenWindowOp;
     manages PContentPermissionRequest;
     manages PCycleCollectWithLogs;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
@@ -669,16 +672,23 @@ child:
     /*
      * IPC message to propagate dynamic scalar definitions, added after the
      * content process is spawned, from the parent to the child.
      * Dynamic scalar definitions added at the process startup are handled
      * using the |TelemetryIPC::AddDynamicScalarDefinitions| functions.
      */
     async AddDynamicScalars(DynamicScalarDefinition[] definitions);
 
+    /*
+     * Message to construct a PClientOpenWindowOp actor.  This is used to
+     * open windows cross-process and receive notification when the operation
+     * has completed.
+     */
+    async PClientOpenWindowOp(ClientOpenWindowArgs aArgs);
+
 parent:
     async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
 
     sync CreateChildProcess(IPCTabContext context,
                             ProcessPriority priority,
                             TabId openerTabId,
                             TabId tabId)
         returns (ContentParentId cpId, bool isForBrowser);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -170,23 +170,21 @@ nsTHashtable<nsPtrHashKey<TabChild>>* Ta
 
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
 StaticMutex sTabChildrenMutex;
 
 TabChildBase::TabChildBase()
   : mTabChildGlobal(nullptr)
 {
-  mozilla::HoldJSObjects(this);
 }
 
 TabChildBase::~TabChildBase()
 {
   mAnonymousGlobalScopes.Clear();
-  mozilla::DropJSObjects(this);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildBase)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TabChildBase)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChildGlobal)
   tmp->nsMessageManagerScriptExecutor::Unlink();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebBrowserChrome)
@@ -439,16 +437,18 @@ TabChild::TabChild(nsIContentChild* aMan
   , mTopLevelDocAccessibleChild(nullptr)
 #endif
   , mPendingDocShellIsActive(false)
   , mPendingDocShellPreserveLayers(false)
   , mPendingDocShellReceivedMessage(false)
   , mPendingDocShellBlockers(0)
   , mWidgetNativeData(0)
 {
+  mozilla::HoldJSObjects(this);
+
   nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this)));  // for capture by the lambda
   mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
                                                    const nsTArray<TouchBehaviorFlags>& aFlags)
   {
     if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
       static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags);
     }
   };
@@ -697,16 +697,29 @@ TabChild::UpdateFrameType()
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   MOZ_ASSERT(docShell);
 
   // TODO: Bug 1252794 - remove frameType from nsIDocShell.idl
   docShell->SetFrameType(IsMozBrowserElement() ? nsIDocShell::FRAME_TYPE_BROWSER :
                            nsIDocShell::FRAME_TYPE_REGULAR);
 }
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(TabChild)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChild, TabChildBase)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChild, TabChildBase)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(TabChild, TabChildBase)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChild)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
   NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
   NS_INTERFACE_MAP_ENTRY(nsITabChild)
@@ -1142,16 +1155,18 @@ TabChild::~TabChild()
   nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
   if (webBrowser) {
     webBrowser->SetContainerWindow(nullptr);
   }
 
   if (mHistoryListener) {
     mHistoryListener->ClearTabChild();
   }
+
+  mozilla::DropJSObjects(this);
 }
 
 mozilla::ipc::IPCResult
 TabChild::RecvLoadURL(const nsCString& aURI,
                       const ShowInfo& aInfo)
 {
   if (!mDidLoadURLInit) {
     mDidLoadURLInit = true;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -325,16 +325,18 @@ public:
   NS_DECL_NSIEMBEDDINGSITEWINDOW
   NS_DECL_NSIWEBBROWSERCHROMEFOCUS
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIWINDOWPROVIDER
   NS_DECL_NSITABCHILD
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITOOLTIPLISTENER
 
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TabChild, TabChildBase)
+
   FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild)
 
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                      const nsAString& aMessage,
                                      StructuredCloneData& aData,
--- a/dom/manifest/Manifest.jsm
+++ b/dom/manifest/Manifest.jsm
@@ -70,17 +70,17 @@ class Manifest {
     // The key for this is the manifests URL that is required to be unique.
     // However arbitrary urls are not safe file paths so lets hash it.
     const fileName = generateHash(manifestUrl) + ".json";
     this._path = OS.Path.join(MANIFESTS_DIR, fileName);
     this._browser = browser;
   }
 
   async initialise() {
-    this._store = new JSONFile({path: this._path});
+    this._store = new JSONFile({path: this._path, saveDelayMs: 100});
     await this._store.load();
   }
 
   async prefetch(browser) {
     const manifestData = await ManifestObtainer.browserObtainManifest(browser);
     const icon = await ManifestIcons.browserFetchIcon(browser, manifestData, 192);
     const data = {
       installed: false,
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -20,17 +20,16 @@ static mozilla::LazyLogModule sGetUserMe
 #include "MediaEngineTabVideoSource.h"
 #include "MediaEngineRemoteVideoSource.h"
 #include "CamerasChild.h"
 #include "nsITabSource.h"
 #include "MediaTrackConstraints.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "VideoEngine.h"
-#include "AndroidJNIWrapper.h"
 #include "AndroidBridge.h"
 #endif
 
 #undef LOG
 #define LOG(args) MOZ_LOG(sGetUserMediaLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 
@@ -277,23 +276,19 @@ MediaEngineWebRTC::EnumerateAudioDevices
   if (aMediaSource == dom::MediaSourceEnum::AudioCapture) {
     RefPtr<MediaEngineWebRTCAudioCaptureSource> audioCaptureSource =
       new MediaEngineWebRTCAudioCaptureSource(nullptr);
     aASources->AppendElement(audioCaptureSource);
     return;
   }
 
 #ifdef MOZ_WIDGET_ANDROID
+  JavaVM* jvm = mozilla::jni::GetVM();
   jobject context = mozilla::AndroidBridge::Bridge()->GetGlobalContextRef();
 
-  // get the JVM
-  JavaVM* jvm;
-  JNIEnv* const env = jni::GetEnvForThread();
-  MOZ_ALWAYS_TRUE(!env->GetJavaVM(&jvm));
-
   if (webrtc::VoiceEngine::SetAndroidObjects(jvm, (void*)context) != 0) {
     LOG(("VoiceEngine:SetAndroidObjects Failed"));
     return;
   }
 #endif
 
   if (!mVoiceEngine) {
     mVoiceEngine = webrtc::VoiceEngine::Create();
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -39,18 +39,17 @@ nsSVGElement::StringInfo SVGFEImageEleme
   { &nsGkAtoms::href, kNameSpaceID_XLink, true }
 };
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ISUPPORTS_INHERITED(SVGFEImageElement, SVGFEImageElementBase,
                             nsIDOMNode, nsIDOMElement, nsIDOMSVGElement,
-                            imgINotificationObserver, nsIImageLoadingContent,
-                            imgIOnloadBlocker)
+                            imgINotificationObserver, nsIImageLoadingContent)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGFEImageElement::SVGFEImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGFEImageElementBase(aNodeInfo)
 {
   // We start out broken
--- a/dom/svg/SVGImageElement.cpp
+++ b/dom/svg/SVGImageElement.cpp
@@ -46,17 +46,17 @@ nsSVGElement::StringInfo SVGImageElement
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
 NS_IMPL_ISUPPORTS_INHERITED(SVGImageElement, SVGImageElementBase,
                             nsIDOMNode, nsIDOMElement,
                             nsIDOMSVGElement,
                             imgINotificationObserver,
-                            nsIImageLoadingContent, imgIOnloadBlocker)
+                            nsIImageLoadingContent)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGImageElement::SVGImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : SVGImageElementBase(aNodeInfo)
 {
   // We start out broken
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -350,23 +350,16 @@ partial interface Navigator {
 
 partial interface Navigator {
   [NewObject]
   Promise<MediaKeySystemAccess>
   requestMediaKeySystemAccess(DOMString keySystem,
                               sequence<MediaKeySystemConfiguration> supportedConfigurations);
 };
 
-#ifdef NIGHTLY_BUILD
-partial interface Navigator {
-  [Func="Navigator::IsE10sEnabled"]
-  readonly attribute boolean mozE10sEnabled;
-};
-#endif
-
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface NavigatorConcurrentHardware {
   readonly attribute unsigned long long hardwareConcurrency;
 };
 
 partial interface Navigator {
   [Pref="security.webauth.webauthn", SameObject]
   readonly attribute CredentialsContainer credentials;
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerHolderToken.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WorkerHolderToken.h"
+
+#include "WorkerPrivate.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+// static
+already_AddRefed<WorkerHolderToken>
+WorkerHolderToken::Create(WorkerPrivate* aWorkerPrivate, Status aShutdownStatus,
+                          Behavior aBehavior)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
+  aWorkerPrivate->AssertIsOnWorkerThread();
+
+  RefPtr<WorkerHolderToken> workerHolder =
+    new WorkerHolderToken(aShutdownStatus, aBehavior);
+
+  if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate, aShutdownStatus))) {
+    return nullptr;
+  }
+
+  return workerHolder.forget();
+}
+
+void
+WorkerHolderToken::AddListener(Listener* aListener)
+{
+  NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
+  MOZ_ASSERT(aListener);
+  MOZ_ASSERT(!mListenerList.Contains(aListener));
+
+  mListenerList.AppendElement(aListener);
+
+  // Allow an actor to be added after we've entered the Notifying case.  We
+  // can't stop the actor creation from racing with out destruction of the
+  // other actors and we need to wait for this extra one to close as well.
+  // Signal it should destroy itself right away.
+  if (mShuttingDown) {
+    aListener->WorkerShuttingDown();
+  }
+}
+
+void
+WorkerHolderToken::RemoveListener(Listener* aListener)
+{
+  NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
+  MOZ_ASSERT(aListener);
+
+  DebugOnly<bool> removed = mListenerList.RemoveElement(aListener);
+
+  MOZ_ASSERT(removed);
+  MOZ_ASSERT(!mListenerList.Contains(aListener));
+}
+
+bool
+WorkerHolderToken::IsShuttingDown() const
+{
+  return mShuttingDown;
+}
+
+WorkerPrivate*
+WorkerHolderToken::GetWorkerPrivate() const
+{
+  NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
+  return mWorkerPrivate;
+}
+
+WorkerHolderToken::WorkerHolderToken(Status aShutdownStatus,
+                                     Behavior aBehavior)
+  : WorkerHolder(aBehavior)
+  , mShutdownStatus(aShutdownStatus)
+  , mShuttingDown(false)
+{
+}
+
+WorkerHolderToken::~WorkerHolderToken()
+{
+  NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
+  MOZ_ASSERT(mListenerList.IsEmpty());
+}
+
+bool
+WorkerHolderToken::Notify(Status aStatus)
+{
+  NS_ASSERT_OWNINGTHREAD(WorkerHolderToken);
+
+  // When the service worker thread is stopped we will get Terminating,
+  // but nothing higher than that.  We must shut things down at Terminating.
+  if (aStatus < mShutdownStatus || mShuttingDown) {
+    return true;
+  }
+
+  mShuttingDown = true;
+
+  // Start the asynchronous destruction of our actors.  These will call back
+  // into RemoveActor() once the actor is destroyed.
+  for (uint32_t i = 0; i < mListenerList.Length(); ++i) {
+    mListenerList[i]->WorkerShuttingDown();
+  }
+
+  return true;
+}
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerHolderToken.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_WorkerHolderToken_h
+#define mozilla_dom_workers_WorkerHolderToken_h
+
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "WorkerHolder.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+class WorkerPrivate;
+
+// This is a ref-counted WorkerHolder implementation.  If you wish
+// to be notified of worker shutdown beginning, then you can implement
+// the Listener interface and call AddListener().
+//
+// This is purely a convenience class to avoid requiring code to
+// extend WorkerHolder all the time.
+class WorkerHolderToken final : public WorkerHolder
+{
+public:
+  // Pure virtual class defining the interface for objects that
+  // wish to be notified of worker shutdown.
+  class Listener
+  {
+  public:
+    virtual void
+    WorkerShuttingDown() = 0;
+  };
+
+  // Attempt to create a WorkerHolderToken().  If the shutdown has already
+  // passed the given shutdown phase or fails for another reason then
+  // nullptr is returned.
+  static already_AddRefed<WorkerHolderToken>
+  Create(workers::WorkerPrivate* aWorkerPrivate, Status aShutdownStatus,
+         Behavior aBehavior = PreventIdleShutdownStart);
+
+  // Add a listener to the token.  Note, this does not hold a strong
+  // reference to the listener.  You must call RemoveListener() before
+  // the listener is destroyed.  This can only be called on the owning
+  // worker thread.
+  void
+  AddListener(Listener* aListener);
+
+  // Remove a previously added listener.  This can only be called on the
+  // owning worker thread.
+  void
+  RemoveListener(Listener* aListener);
+
+  bool
+  IsShuttingDown() const;
+
+  WorkerPrivate*
+  GetWorkerPrivate() const;
+
+private:
+  WorkerHolderToken(Status aShutdownStatus, Behavior aBehavior);
+
+  ~WorkerHolderToken();
+
+  // WorkerHolder methods
+  virtual bool
+  Notify(workers::Status aStatus) override;
+
+  nsTArray<Listener*> mListenerList;
+  const Status mShutdownStatus;
+  bool mShuttingDown;
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(WorkerHolderToken)
+};
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_WorkerHolderToken_h
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -37,16 +37,17 @@ EXPORTS.mozilla.dom.workers += [
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'ServiceWorker.h',
     'ServiceWorkerClient.h',
     'ServiceWorkerClients.h',
     'ServiceWorkerWindowClient.h',
     'SharedWorker.h',
     'WorkerHolder.h',
+    'WorkerHolderToken.h',
 ]
 
 XPIDL_MODULE = 'dom_workers'
 
 XPIDL_SOURCES += [
     'nsIWorkerDebugger.idl',
     'nsIWorkerDebuggerManager.idl',
 ]
@@ -80,16 +81,17 @@ UNIFIED_SOURCES += [
     'ServiceWorkerUnregisterJob.cpp',
     'ServiceWorkerUpdateJob.cpp',
     'ServiceWorkerUpdaterChild.cpp',
     'ServiceWorkerUpdaterParent.cpp',
     'ServiceWorkerWindowClient.cpp',
     'SharedWorker.cpp',
     'WorkerDebuggerManager.cpp',
     'WorkerHolder.cpp',
+    'WorkerHolderToken.cpp',
     'WorkerLocation.cpp',
     'WorkerNavigator.cpp',
     'WorkerPrivate.cpp',
     'WorkerRunnable.cpp',
     'WorkerScope.cpp',
     'WorkerThread.cpp',
 ]
 
--- a/dom/workers/test/serviceworkers/test_bug1408734.html
+++ b/dom/workers/test/serviceworkers/test_bug1408734.html
@@ -5,16 +5,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Bug 1408734</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/SpawnTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
+  <script src="utils.js"></script>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
 // setup prefs
@@ -22,27 +23,26 @@ add_task(() => {
   return SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]});
 });
 
 // test for bug 1408734
 add_task(async () => {
-  let waitForControlled = new Promise((resolve) => {
-    navigator.serviceWorker.oncontrollerchange = resolve;
-  });
-
   // register a service worker
   let registration = await navigator.serviceWorker.register("fetch.js", {scope: "./"});
-  let worker = registration.installing || registration.active;
+  let sw = registration.installing || registration.active;
+
+  // wait for service worker be activated
+  await waitForState(sw, 'activated');
 
   // wait for control changed
-  worker.postMessage('claim');
-  await waitForControlled;
+  sw.postMessage('claim');
+  await waitForControlled(window);
 
   // get the ServiceWorkerRegistration we just register through GetRegistration
   registration = await navigator.serviceWorker.getRegistration("./");
   ok(registration, "should get the registration under scope './'");
 
   // call unregister()
   await registration.unregister();
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -190,16 +190,18 @@ HTMLEditor::HideAnonymousEditingUIs()
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLEditor)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLEditor, TextEditor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTypeInState)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStyleSheets)
 
   tmp->HideAnonymousEditingUIs();
+
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinkHandler)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLEditor, TextEditor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTypeInState)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopLeftHandle)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTopHandle)
@@ -223,16 +225,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineEditedCell)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnBeforeButton)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveColumnButton)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddColumnAfterButton)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowBeforeButton)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRemoveRowButton)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddRowAfterButton)
+
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinkHandler)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(HTMLEditor, EditorBase)
 NS_IMPL_RELEASE_INHERITED(HTMLEditor, EditorBase)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLEditor)
   NS_INTERFACE_MAP_ENTRY(nsIHTMLEditor)
   NS_INTERFACE_MAP_ENTRY(nsIHTMLObjectResizer)
--- a/gfx/ipc/VsyncBridgeParent.cpp
+++ b/gfx/ipc/VsyncBridgeParent.cpp
@@ -23,17 +23,17 @@ VsyncBridgeParent::Start(Endpoint<PVsync
 
   return parent;
 }
 
 VsyncBridgeParent::VsyncBridgeParent()
  : mOpen(false)
 {
   MOZ_COUNT_CTOR(VsyncBridgeParent);
-  mCompositorThreadRef = new CompositorThreadHolderDebug("VsyncBridge");
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VsyncBridgeParent::~VsyncBridgeParent()
 {
   MOZ_COUNT_DTOR(VsyncBridgeParent);
 }
 
 void
--- a/gfx/ipc/VsyncBridgeParent.h
+++ b/gfx/ipc/VsyncBridgeParent.h
@@ -33,15 +33,15 @@ private:
   VsyncBridgeParent();
   ~VsyncBridgeParent();
 
   void Open(Endpoint<PVsyncBridgeParent>&& aEndpoint);
   void ShutdownImpl();
 
 private:
   bool mOpen;
-  RefPtr<layers::CompositorThreadHolderDebug> mCompositorThreadRef;
+  RefPtr<layers::CompositorThreadHolder> mCompositorThreadRef;
 };
 
 } // namespace gfx
 } // namespace mozilla
 
 #endif // include_gfx_ipc_VsyncBridgeParent_h
--- a/gfx/layers/ipc/CompositorManagerParent.cpp
+++ b/gfx/layers/ipc/CompositorManagerParent.cpp
@@ -37,34 +37,30 @@ CompositorManagerParent::CreateSameProce
     MOZ_ASSERT_UNREACHABLE("Already initialized");
     return nullptr;
   }
 
   // The child is responsible for setting up the IPC channel in the same
   // process case because if we open from the child perspective, we can do it
   // on the main thread and complete before we return the manager handles.
   RefPtr<CompositorManagerParent> parent = new CompositorManagerParent();
-  parent->mCompositorThreadHolder =
-    new CompositorThreadHolderDebug("CompositorManagerSame");
   parent->SetOtherProcessId(base::GetCurrentProcId());
   return parent.forget();
 }
 
 /* static */ void
 CompositorManagerParent::Create(Endpoint<PCompositorManagerParent>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // We are creating a manager for the another process, inside the GPU process
   // (or UI process if it subsumbed the GPU process).
   MOZ_ASSERT(aEndpoint.OtherPid() != base::GetCurrentProcId());
 
   RefPtr<CompositorManagerParent> bridge = new CompositorManagerParent();
-  bridge->mCompositorThreadHolder =
-    new CompositorThreadHolderDebug("CompositorManagerContent");
 
   RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PCompositorManagerParent>&&>(
     "CompositorManagerParent::Bind",
     bridge,
     &CompositorManagerParent::Bind,
     Move(aEndpoint));
   CompositorThreadHolder::Loop()->PostTask(runnable.forget());
 }
@@ -105,16 +101,17 @@ CompositorManagerParent::CreateSameProce
     new CompositorBridgeParent(sInstance, aScale, vsyncRate, aOptions,
                                aUseExternalSurfaceSize, aSurfaceSize);
 
   sInstance->mPendingCompositorBridges.AppendElement(bridge);
   return bridge.forget();
 }
 
 CompositorManagerParent::CompositorManagerParent()
+  : mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
 {
 }
 
 CompositorManagerParent::~CompositorManagerParent()
 {
 }
 
 void
--- a/gfx/layers/ipc/CompositorManagerParent.h
+++ b/gfx/layers/ipc/CompositorManagerParent.h
@@ -14,17 +14,17 @@
 #include "mozilla/RefPtr.h"             // for already_AddRefed
 #include "mozilla/layers/PCompositorManagerParent.h"
 #include "nsTArray.h"                   // for AutoTArray
 
 namespace mozilla {
 namespace layers {
 
 class CompositorBridgeParent;
-class CompositorThreadHolderDebug;
+class CompositorThreadHolder;
 
 #ifndef DEBUG
 #define COMPOSITOR_MANAGER_PARENT_EXPLICIT_SHUTDOWN
 #endif
 
 class CompositorManagerParent final : public PCompositorManagerParent
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorManagerParent)
@@ -63,17 +63,17 @@ private:
   ~CompositorManagerParent() override;
 
   void Bind(Endpoint<PCompositorManagerParent>&& aEndpoint);
 
   void DeallocPCompositorManagerParent() override;
 
   void DeferredDestroy();
 
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadHolder;
+  RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
 
   AutoTArray<RefPtr<CompositorBridgeParent>, 1> mPendingCompositorBridges;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/CompositorThread.cpp
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -5,56 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "CompositorThread.h"
 #include "MainThreadUtils.h"
 #include "nsThreadUtils.h"
 #include "CompositorBridgeParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedSurfacesParent.h"
 #include "mozilla/media/MediaSystemResourceService.h"
-#ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"     // for CrashReporter
-#endif
 
 namespace mozilla {
 
 namespace gfx {
 // See VRManagerChild.cpp
 void ReleaseVRManagerParentSingleton();
 } // namespace gfx
 
 namespace layers {
 
-#ifdef MOZ_CRASHREPORTER
-static Atomic<int32_t> sHoldersNextId(0);
-#endif
-
-CompositorThreadHolderDebug::CompositorThreadHolderDebug(const char* aName)
-  : mHolder(CompositorThreadHolder::GetSingleton())
-{
-#ifdef MOZ_CRASHREPORTER
-  if (XRE_IsParentProcess()) {
-    mId.AppendLiteral("gfxCTH:");
-    mId.Append(aName);
-    mId.AppendLiteral(":");
-    mId.AppendInt(++sHoldersNextId);
-    CrashReporter::AnnotateCrashReport(mId, NS_LITERAL_CSTRING("1"));
-  }
-#endif
-}
-
-CompositorThreadHolderDebug::~CompositorThreadHolderDebug()
-{
-#ifdef MOZ_CRASHREPORTER
-  if (XRE_IsParentProcess()) {
-    CrashReporter::AnnotateCrashReport(mId, NS_LITERAL_CSTRING("0"));
-  }
-#endif
-}
-
 static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
 static bool sFinishedCompositorShutDown = false;
 
 CompositorThreadHolder* GetCompositorThreadHolder()
 {
   return sCompositorThreadHolder;
 }
 
--- a/gfx/layers/ipc/CompositorThread.h
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -55,30 +55,14 @@ private:
   base::Thread* const mCompositorThread;
 
   static base::Thread* CreateCompositorThread();
   static void DestroyCompositorThread(base::Thread* aCompositorThread);
 
   friend class CompositorBridgeParent;
 };
 
-class CompositorThreadHolderDebug final
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorThreadHolderDebug)
-
-public:
-  explicit CompositorThreadHolderDebug(const char* aName);
-
-private:
-  ~CompositorThreadHolderDebug();
-
-  RefPtr<CompositorThreadHolder> mHolder;
-#ifdef MOZ_CRASHREPORTER
-  nsAutoCString mId;
-#endif
-};
-
 base::Thread* CompositorThread();
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_CompositorThread_h
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -61,23 +61,20 @@ ImageBridgeParent::Setup()
   }
 }
 
 ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
                                      ProcessId aChildProcessId)
   : mMessageLoop(aLoop)
   , mSetChildThreadPriority(false)
   , mClosed(false)
+  , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mCompositorThreadHolder =
-    new CompositorThreadHolderDebug(IsSameProcess() ? "ImageBridgeSame"
-                                                    : "ImageBridge");
-
   // creates the map only if it has not been created already, so it is safe
   // with several bridges
   {
     MonitorAutoLock lock(*sImageBridgesLock);
     sImageBridges[aChildProcessId] = this;
   }
   SetOtherProcessId(aChildProcessId);
 }
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -139,15 +139,15 @@ private:
   bool mClosed;
 
   /**
    * Map of all living ImageBridgeParent instances
    */
   typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap;
   static ImageBridgeMap sImageBridges;
 
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadHolder;
+  RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // gfx_layers_ipc_ImageBridgeParent_h_
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -16,17 +16,17 @@ using namespace mozilla::gfx;
 
 static VideoBridgeParent* sVideoBridgeSingleton;
 
 VideoBridgeParent::VideoBridgeParent()
   : mClosed(false)
 {
   mSelfRef = this;
   sVideoBridgeSingleton = this;
-  mCompositorThreadRef = new CompositorThreadHolderDebug("VideoBridge");
+  mCompositorThreadRef = CompositorThreadHolder::GetSingleton();
 }
 
 VideoBridgeParent::~VideoBridgeParent()
 {
   sVideoBridgeSingleton = nullptr;
 }
 
 /* static */ VideoBridgeParent*
--- a/gfx/layers/ipc/VideoBridgeParent.h
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -8,17 +8,17 @@
 #define gfx_layers_ipc_VideoBridgeParent_h_
 
 #include "mozilla/layers/PVideoBridgeParent.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 namespace mozilla {
 namespace layers {
 
-class CompositorThreadHolderDebug;
+class CompositorThreadHolder;
 
 class VideoBridgeParent final : public PVideoBridgeParent,
                                 public HostIPCAllocator,
                                 public ShmemAllocator
 {
 public:
   VideoBridgeParent();
   ~VideoBridgeParent();
@@ -59,17 +59,17 @@ public:
   void DeallocShmem(ipc::Shmem& aShmem) override;
 
 private:
   void DeallocPVideoBridgeParent() override;
 
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<VideoBridgeParent> mSelfRef;
-  RefPtr<CompositorThreadHolderDebug> mCompositorThreadRef;
+  RefPtr<CompositorThreadHolder> mCompositorThreadRef;
 
   std::map<uint64_t, PTextureParent*> mTextureMap;
 
   bool mClosed;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/image/IProgressObserver.h
+++ b/image/IProgressObserver.h
@@ -35,20 +35,16 @@ public:
   // Subclasses may or may not be XPCOM classes, so we just require that they
   // implement AddRef and Release.
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   // imgINotificationObserver methods:
   virtual void Notify(int32_t aType, const nsIntRect* aRect = nullptr) = 0;
   virtual void OnLoadComplete(bool aLastPart) = 0;
 
-  // imgIOnloadBlocker methods:
-  virtual void BlockOnload() = 0;
-  virtual void UnblockOnload() = 0;
-
   // Other, internal-only methods:
   virtual void SetHasImage() = 0;
   virtual bool NotificationsDeferred() const = 0;
   virtual void SetNotificationsDeferred(bool aDeferNotifications) = 0;
 
   virtual already_AddRefed<nsIEventTarget> GetEventTarget() const
   {
     return nullptr;
--- a/image/MultipartImage.cpp
+++ b/image/MultipartImage.cpp
@@ -88,18 +88,16 @@ public:
     // notification, so go ahead and notify our owner right away.
     RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
     if (tracker->GetProgress() & FLAG_HAS_ERROR) {
       FinishObserving();
     }
   }
 
   // Other notifications are ignored.
-  virtual void BlockOnload() override { }
-  virtual void UnblockOnload() override { }
   virtual void SetHasImage() override { }
   virtual bool NotificationsDeferred() const override { return false; }
   virtual void SetNotificationsDeferred(bool) override { }
 
 private:
   virtual ~NextPartObserver() { }
 
   void FinishObserving()
@@ -172,17 +170,17 @@ MultipartImage::BeginTransitionToPart(Im
 
 static Progress
 FilterProgress(Progress aProgress)
 {
   // Filter out onload blocking notifications, since we don't want to block
   // onload for multipart images.
   // Filter out errors, since we don't want errors in one part to error out
   // the whole stream.
-  return aProgress & ~(FLAG_ONLOAD_BLOCKED | FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR);
+  return aProgress & ~FLAG_HAS_ERROR;
 }
 
 void
 MultipartImage::FinishTransition()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mNextPart, "Should have a next part here");
 
--- a/image/MultipartImage.h
+++ b/image/MultipartImage.h
@@ -56,21 +56,16 @@ public:
   // Overridden IProgressObserver methods:
   virtual void Notify(int32_t aType,
                       const nsIntRect* aRect = nullptr) override;
   virtual void OnLoadComplete(bool aLastPart) override;
   virtual void SetHasImage() override;
   virtual bool NotificationsDeferred() const override;
   virtual void SetNotificationsDeferred(bool aDeferNotifications) override;
 
-  // We don't allow multipart images to block onload, so we override these
-  // methods to do nothing.
-  virtual void BlockOnload() override { }
-  virtual void UnblockOnload() override { }
-
 protected:
   virtual ~MultipartImage();
 
 private:
   friend class ImageFactory;
   friend class NextPartObserver;
 
   explicit MultipartImage(Image* aFirstPart);
--- a/image/ProgressTracker.cpp
+++ b/image/ProgressTracker.cpp
@@ -41,23 +41,16 @@ CheckProgressConsistency(Progress aOldPr
     MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_FRAME_COMPLETE | FLAG_HAS_ERROR));
   }
   if (aNewProgress & FLAG_FRAME_COMPLETE) {
     MOZ_ASSERT(aNewProgress & FLAG_SIZE_AVAILABLE);
   }
   if (aNewProgress & FLAG_LOAD_COMPLETE) {
     MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_SIZE_AVAILABLE | FLAG_HAS_ERROR));
   }
-  if (aNewProgress & FLAG_ONLOAD_BLOCKED) {
-    // No preconditions.
-  }
-  if (aNewProgress & FLAG_ONLOAD_UNBLOCKED) {
-    MOZ_ASSERT(aNewProgress & FLAG_ONLOAD_BLOCKED);
-    MOZ_ASSERT(aIsMultipart || aNewProgress & (FLAG_SIZE_AVAILABLE | FLAG_HAS_ERROR));
-  }
   if (aNewProgress & FLAG_IS_ANIMATED) {
     // No preconditions; like FLAG_HAS_TRANSPARENCY, we should normally never
     // discover this *after* FLAG_SIZE_AVAILABLE, but unfortunately some corrupt
     // GIFs may fool us.
   }
   if (aNewProgress & FLAG_HAS_TRANSPARENCY) {
     // XXX We'd like to assert that transparency is only set during metadata
     // decode but we don't have any way to assert that until bug 1254892 is fixed.
@@ -328,20 +321,16 @@ SyncNotifyInternal(const T& aObservers,
 
   typedef imgINotificationObserver I;
   ImageObserverNotifier<T> notify(aObservers);
 
   if (aProgress & FLAG_SIZE_AVAILABLE) {
     notify([](IProgressObserver* aObs) { aObs->Notify(I::SIZE_AVAILABLE); });
   }
 
-  if (aProgress & FLAG_ONLOAD_BLOCKED) {
-    notify([](IProgressObserver* aObs) { aObs->BlockOnload(); });
-  }
-
   if (aHasImage) {
     // OnFrameUpdate
     // If there's any content in this frame at all (always true for
     // vector images, true for raster images that have decoded at
     // least one frame) then send OnFrameUpdate.
     if (!aDirtyRect.IsEmpty()) {
       notify([&](IProgressObserver* aObs) {
         aObs->Notify(I::FRAME_UPDATE, &aDirtyRect);
@@ -356,23 +345,16 @@ SyncNotifyInternal(const T& aObservers,
       notify([](IProgressObserver* aObs) { aObs->Notify(I::HAS_TRANSPARENCY); });
     }
 
     if (aProgress & FLAG_IS_ANIMATED) {
       notify([](IProgressObserver* aObs) { aObs->Notify(I::IS_ANIMATED); });
     }
   }
 
-  // Send UnblockOnload before OnStopDecode and OnStopRequest. This allows
-  // observers that can fire events when they receive those notifications to do
-  // so then, instead of being forced to wait for UnblockOnload.
-  if (aProgress & FLAG_ONLOAD_UNBLOCKED) {
-    notify([](IProgressObserver* aObs) { aObs->UnblockOnload(); });
-  }
-
   if (aProgress & FLAG_DECODE_COMPLETE) {
     MOZ_ASSERT(aHasImage, "Stopped decoding without ever having an image?");
     notify([](IProgressObserver* aObs) { aObs->Notify(I::DECODE_COMPLETE); });
   }
 
   if (aProgress & FLAG_LOAD_COMPLETE) {
     notify([=](IProgressObserver* aObs) {
       aObs->OnLoadComplete(aProgress & FLAG_LAST_PART_COMPLETE);
@@ -382,33 +364,19 @@ SyncNotifyInternal(const T& aObservers,
 
 void
 ProgressTracker::SyncNotifyProgress(Progress aProgress,
                                     const nsIntRect& aInvalidRect
                                                   /* = nsIntRect() */)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Use mObservers on main thread only");
 
-  // Don't unblock onload if we're not blocked.
   Progress progress = Difference(aProgress);
-  if (!((mProgress | progress) & FLAG_ONLOAD_BLOCKED)) {
-    progress &= ~FLAG_ONLOAD_UNBLOCKED;
-  }
-
   CheckProgressConsistency(mProgress, mProgress | progress, mIsMultipart);
 
-  // XXX(seth): Hack to work around the fact that some observers have bugs and
-  // need to get onload blocking notifications multiple times. We should fix
-  // those observers and remove this.
-  if ((aProgress & FLAG_DECODE_COMPLETE) &&
-      (mProgress & FLAG_ONLOAD_BLOCKED) &&
-      (mProgress & FLAG_ONLOAD_UNBLOCKED)) {
-    progress |= FLAG_ONLOAD_BLOCKED | FLAG_ONLOAD_UNBLOCKED;
-  }
-
   // Apply the changes.
   mProgress |= progress;
 
   // Send notifications.
   mObservers.Read([&](const ObserverTable* aTable) {
     SyncNotifyInternal(aTable, HasImage(), progress, aInvalidRect);
   });
 
@@ -445,20 +413,16 @@ ProgressTracker::SyncNotify(IProgressObs
 
 void
 ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver)
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "SyncNotifyState and mObservers are not threadsafe");
   RefPtr<IProgressObserver> kungFuDeathGrip(aObserver);
 
-  if (mProgress & FLAG_ONLOAD_BLOCKED && !(mProgress & FLAG_ONLOAD_UNBLOCKED)) {
-    aObserver->UnblockOnload();
-  }
-
   if (!(mProgress & FLAG_LOAD_COMPLETE)) {
     aObserver->OnLoadComplete(true);
   }
 }
 
 already_AddRefed<nsIEventTarget>
 ProgressTracker::GetEventTarget() const
 {
--- a/image/ProgressTracker.h
+++ b/image/ProgressTracker.h
@@ -34,18 +34,16 @@ class Image;
  * See CheckProgressConsistency() for the invariants we enforce about the
  * ordering dependencies betweeen these flags.
  */
 enum {
   FLAG_SIZE_AVAILABLE     = 1u << 0,  // STATUS_SIZE_AVAILABLE
   FLAG_DECODE_COMPLETE    = 1u << 1,  // STATUS_DECODE_COMPLETE
   FLAG_FRAME_COMPLETE     = 1u << 2,  // STATUS_FRAME_COMPLETE
   FLAG_LOAD_COMPLETE      = 1u << 3,  // STATUS_LOAD_COMPLETE
-  FLAG_ONLOAD_BLOCKED     = 1u << 4,
-  FLAG_ONLOAD_UNBLOCKED   = 1u << 5,
   FLAG_IS_ANIMATED        = 1u << 6,  // STATUS_IS_ANIMATED
   FLAG_HAS_TRANSPARENCY   = 1u << 7,  // STATUS_HAS_TRANSPARENCY
   FLAG_LAST_PART_COMPLETE = 1u << 8,
   FLAG_HAS_ERROR          = 1u << 9   // STATUS_ERROR
 };
 
 typedef uint32_t Progress;
 
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -1003,17 +1003,16 @@ RasterImage::OnImageDataComplete(nsIRequ
   }
 
   Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
 
   if (!mHasSize && !mError) {
     // We don't have our size yet, so we'll fire the load event in SetSize().
     MOZ_ASSERT(!canSyncDecodeMetadata,
                "Firing load async after metadata sync decode?");
-    NotifyProgress(FLAG_ONLOAD_BLOCKED);
     mLoadProgress = Some(loadProgress);
     return finalStatus;
   }
 
   NotifyForLoadEvent(loadProgress);
 
   return finalStatus;
 }
@@ -1838,17 +1837,16 @@ RasterImage::NotifyDecodeComplete(const 
   }
 
   // XXX(aosmond): Can we get this far without mFinished == true?
   if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
     // If we were waiting to fire the load event, go ahead and fire it now.
     if (mLoadProgress) {
       NotifyForLoadEvent(*mLoadProgress);
       mLoadProgress = Nothing();
-      NotifyProgress(FLAG_ONLOAD_UNBLOCKED);
     }
 
     // If we were a metadata decode and a full decode was requested, do it.
     if (mWantFullDecode) {
       mWantFullDecode = false;
       RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
     }
   }
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -1192,26 +1192,16 @@ VectorImage::OnStartRequest(nsIRequest* 
   mSVGDocumentWrapper = new SVGDocumentWrapper();
   nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt);
   if (NS_FAILED(rv)) {
     mSVGDocumentWrapper = nullptr;
     mError = true;
     return rv;
   }
 
-  // ProgressTracker::SyncNotifyProgress may release us, so ensure we
-  // stick around long enough to complete our work.
-  RefPtr<VectorImage> kungFuDeathGrip(this);
-
-  // Block page load until the document's ready.  (We unblock it in
-  // OnSVGDocumentLoaded or OnSVGDocumentError.)
-  if (mProgressTracker) {
-    mProgressTracker->SyncNotifyProgress(FLAG_ONLOAD_BLOCKED);
-  }
-
   // Create a listener to wait until the SVG document is fully loaded, which
   // will signal that this image is ready to render. Certain error conditions
   // will prevent us from ever getting this notification, so we also create a
   // listener that waits for parsing to complete and cancels the
   // SVGLoadEventListener if needed. The listeners are automatically attached
   // to the document by their constructors.
   nsIDocument* document = mSVGDocumentWrapper->GetDocument();
   mLoadEventListener = new SVGLoadEventListener(document, this);
@@ -1285,18 +1275,17 @@ VectorImage::OnSVGDocumentLoaded()
   // stick around long enough to complete our work.
   RefPtr<VectorImage> kungFuDeathGrip(this);
 
   // Tell *our* observers that we're done loading.
   if (mProgressTracker) {
     Progress progress = FLAG_SIZE_AVAILABLE |
                         FLAG_HAS_TRANSPARENCY |
                         FLAG_FRAME_COMPLETE |
-                        FLAG_DECODE_COMPLETE |
-                        FLAG_ONLOAD_UNBLOCKED;
+                        FLAG_DECODE_COMPLETE;
 
     if (mHaveAnimations) {
       progress |= FLAG_IS_ANIMATED;
     }
 
     // Merge in any saved progress from OnImageDataComplete.
     if (mLoadProgress) {
       progress |= *mLoadProgress;
@@ -1313,17 +1302,17 @@ void
 VectorImage::OnSVGDocumentError()
 {
   CancelAllListeners();
 
   mError = true;
 
   if (mProgressTracker) {
     // Notify observers about the error and unblock page load.
-    Progress progress = FLAG_ONLOAD_UNBLOCKED | FLAG_HAS_ERROR;
+    Progress progress = FLAG_HAS_ERROR;
 
     // Merge in any saved progress from OnImageDataComplete.
     if (mLoadProgress) {
       progress |= *mLoadProgress;
       mLoadProgress = Nothing();
     }
 
     mProgressTracker->SyncNotifyProgress(progress);
deleted file mode 100644
--- a/image/imgIOnloadBlocker.idl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface imgIRequest;
-
-[uuid(dc126d90-0ee0-4683-b942-2fa66e443abc)]
-interface imgIOnloadBlocker : nsISupports
-{
-    /**
-     * blockOnload
-     * Called when it is appropriate to block onload for the given imgIRequest.
-     *
-     * @param aRequest
-     *        The request that should block onload.
-     */
-    void blockOnload(in imgIRequest aRequest);
-
-    /**
-     * unblockOnload
-     * Called when it is appropriate to unblock onload for the given
-     * imgIRequest.
-     *
-     * @param aRequest
-     *        The request that should unblock onload.
-     */
-    void unblockOnload(in imgIRequest aRequest);
-};
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2186,16 +2186,25 @@ imgLoader::LoadImage(nsIURI* aURI,
   bool isPrivate = false;
 
   if (aLoadingDocument) {
     isPrivate = nsContentUtils::IsInPrivateBrowsing(aLoadingDocument);
   } else if (aLoadGroup) {
     isPrivate = nsContentUtils::IsInPrivateBrowsing(aLoadGroup);
   }
   MOZ_ASSERT(isPrivate == mRespectPrivacy);
+
+  if (aLoadingDocument) {
+    // The given load group should match that of the document if given. If
+    // that isn't the case, then we need to add more plumbing to ensure we
+    // block the document as well.
+    nsCOMPtr<nsILoadGroup> docLoadGroup =
+      aLoadingDocument->GetDocumentLoadGroup();
+    MOZ_ASSERT(docLoadGroup == aLoadGroup);
+  }
 #endif
 
   // Get the default load flags from the loadgroup (if possible)...
   if (aLoadGroup) {
     aLoadGroup->GetLoadFlags(&requestFlags);
   }
   //
   // Merge the default load flags with those passed in via aLoadFlags.
@@ -2447,16 +2456,17 @@ imgLoader::LoadImageWithChannel(nsIChann
                                 nsIStreamListener** listener,
                                 imgRequestProxy** _retval)
 {
   NS_ASSERTION(channel,
                "imgLoader::LoadImageWithChannel -- NULL channel pointer");
 
   MOZ_ASSERT(NS_UsePrivateBrowsing(channel) == mRespectPrivacy);
 
+  LOG_SCOPE(gImgLog, "imgLoader::LoadImageWithChannel");
   RefPtr<imgRequest> request;
 
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCX);
 
   NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
@@ -2541,16 +2551,26 @@ imgLoader::LoadImageWithChannel(nsIChann
         }
       }
     }
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   channel->GetLoadGroup(getter_AddRefs(loadGroup));
 
+#ifdef DEBUG
+  if (doc) {
+    // The load group of the channel should always match that of the
+    // document if given. If that isn't the case, then we need to add more
+    // plumbing to ensure we block the document as well.
+    nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
+    MOZ_ASSERT(docLoadGroup == loadGroup);
+  }
+#endif
+
   // Filter out any load flags not from nsIRequest
   requestFlags &= nsIRequest::LOAD_REQUESTMASK;
 
   rv = NS_OK;
   if (request) {
     // we have this in our cache already.. cancel the current (document) load
 
     // this should fire an OnStopRequest
@@ -2607,16 +2627,21 @@ imgLoader::LoadImageWithChannel(nsIChann
     // Explicitly don't notify our proxy, because we're loading off the
     // network, and necko (or things called from necko, such as
     // imgCacheValidator) are going to call our notifications asynchronously,
     // and we can't make it further asynchronous because observers might rely
     // on imagelib completing its work between the channel's OnStartRequest and
     // OnStopRequest.
   }
 
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  (*_retval)->AddToLoadGroup();
   return rv;
 }
 
 bool
 imgLoader::SupportImageWithMimeType(const char* aMimeType,
                                     AcceptedMimeTypes aAccept
                                       /* = AcceptedMimeTypes::IMAGES */)
 {
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -285,22 +285,16 @@ imgRequest::RemoveProxy(imgRequestProxy*
 
       this->Cancel(NS_BINDING_ABORTED);
     }
 
     /* break the cycle from the cache entry. */
     mCacheEntry = nullptr;
   }
 
-  // If a proxy is removed for a reason other than its owner being
-  // changed, remove the proxy from the loadgroup.
-  if (aStatus != NS_IMAGELIB_CHANGING_OWNER) {
-    proxy->RemoveFromLoadGroup(true);
-  }
-
   return NS_OK;
 }
 
 void
 imgRequest::CancelAndAbort(nsresult aStatus)
 {
   LOG_SCOPE(gImgLog, "imgRequest::CancelAndAbort");
 
@@ -355,17 +349,17 @@ imgRequest::Cancel(nsresult aStatus)
 }
 
 void
 imgRequest::ContinueCancel(nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<ProgressTracker> progressTracker = GetProgressTracker();
-  progressTracker->SyncNotifyProgress(FLAG_HAS_ERROR | FLAG_ONLOAD_UNBLOCKED);
+  progressTracker->SyncNotifyProgress(FLAG_HAS_ERROR);
 
   RemoveFromCache();
 
   if (mRequest && !(progressTracker->GetProgress() & FLAG_LAST_PART_COMPLETE)) {
      mRequest->Cancel(aStatus);
   }
 }
 
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageLogging.h"
 #include "imgRequestProxy.h"
-#include "imgIOnloadBlocker.h"
 #include "imgLoader.h"
 #include "Image.h"
 #include "ImageOps.h"
 #include "nsError.h"
 #include "nsCRTGlue.h"
 #include "imgINotificationObserver.h"
 #include "mozilla/dom/TabGroup.h"       // for TabGroup
 #include "mozilla/dom/DocGroup.h"       // for DocGroup
@@ -110,16 +109,17 @@ imgRequestProxy::imgRequestProxy() :
   mBehaviour(new RequestBehaviour),
   mURI(nullptr),
   mListener(nullptr),
   mLoadFlags(nsIRequest::LOAD_NORMAL),
   mLockCount(0),
   mAnimationConsumers(0),
   mCanceled(false),
   mIsInLoadGroup(false),
+  mForceDispatchLoadGroup(false),
   mListenerIsStrongRef(false),
   mDecodeRequested(false),
   mDeferNotifications(false),
   mHadListener(false),
   mHadDispatch(false)
 {
   /* member initializers and constructor code */
   LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy");
@@ -165,16 +165,17 @@ imgRequestProxy::~imgRequestProxy()
        channel, if still downloading data, from being canceled if 'this' is
        the last observer.  This allows the image to continue to download and
        be cached even if no one is using it currently.
     */
     mCanceled = true;
     GetOwner()->RemoveProxy(this, NS_OK);
   }
 
+  RemoveFromLoadGroup();
   LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy");
 }
 
 nsresult
 imgRequestProxy::Init(imgRequest* aOwner,
                       nsILoadGroup* aLoadGroup,
                       nsIDocument* aLoadingDocument,
                       ImageURL* aURI,
@@ -225,17 +226,17 @@ imgRequestProxy::ChangeOwner(imgRequest*
   while (mLockCount) {
     UnlockImage();
   }
 
   // If we're holding animation requests, undo them.
   uint32_t oldAnimationConsumers = mAnimationConsumers;
   ClearAnimationConsumers();
 
-  GetOwner()->RemoveProxy(this, NS_IMAGELIB_CHANGING_OWNER);
+  GetOwner()->RemoveProxy(this, NS_OK);
 
   mBehaviour->SetOwner(aNewOwner);
 
   // If we were locked, apply the locks here
   for (uint32_t i = 0; i < oldLockCount; i++) {
     LockImage();
   }
 
@@ -353,46 +354,103 @@ imgRequestProxy::AddToOwner(nsIDocument*
 
   GetOwner()->AddProxy(this);
 }
 
 void
 imgRequestProxy::AddToLoadGroup()
 {
   NS_ASSERTION(!mIsInLoadGroup, "Whaa, we're already in the loadgroup!");
+  MOZ_ASSERT(!mForceDispatchLoadGroup);
 
+  /* While in theory there could be a dispatch outstanding to remove this
+     request from the load group, in practice we only add to the load group
+     (when previously not in a load group) at initialization. */
   if (!mIsInLoadGroup && mLoadGroup) {
+    LOG_FUNC(gImgLog, "imgRequestProxy::AddToLoadGroup");
     mLoadGroup->AddRequest(this, nullptr);
     mIsInLoadGroup = true;
   }
 }
 
 void
-imgRequestProxy::RemoveFromLoadGroup(bool releaseLoadGroup)
+imgRequestProxy::RemoveFromLoadGroup()
 {
-  if (!mIsInLoadGroup) {
+  if (!mIsInLoadGroup || !mLoadGroup) {
     return;
   }
 
+  /* Sometimes we may not be able to remove ourselves from the load group in
+     the current context. This is because our listeners are not re-entrant (e.g.
+     we are in the middle of CancelAndForgetObserver or SyncClone). */
+  if (mForceDispatchLoadGroup) {
+    LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup -- dispatch");
+
+    /* We take away the load group from the request temporarily; this prevents
+       additional dispatches via RemoveFromLoadGroup occurring, as well as
+       MoveToBackgroundInLoadGroup from removing and readding. This is safe
+       because we know that once we get here, blocking the load group at all is
+       unnecessary. */
+    mIsInLoadGroup = false;
+    nsCOMPtr<nsILoadGroup> loadGroup = Move(mLoadGroup);
+    RefPtr<imgRequestProxy> self(this);
+    DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
+      "imgRequestProxy::RemoveFromLoadGroup",
+      [self, loadGroup]() -> void {
+        loadGroup->RemoveRequest(self, nullptr, NS_OK);
+      }));
+    return;
+  }
+
+  LOG_FUNC(gImgLog, "imgRequestProxy::RemoveFromLoadGroup");
+
   /* calling RemoveFromLoadGroup may cause the document to finish
      loading, which could result in our death.  We need to make sure
      that we stay alive long enough to fight another battle... at
-     least until we exit this function.
-  */
+     least until we exit this function. */
   nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
-
   mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
+  mLoadGroup = nullptr;
   mIsInLoadGroup = false;
-
-  if (releaseLoadGroup) {
-    // We're done with the loadgroup, release it.
-    mLoadGroup = nullptr;
-  }
 }
 
+void
+imgRequestProxy::MoveToBackgroundInLoadGroup()
+{
+  /* Even if we are still in the load group, we may have taken away the load
+     group reference itself because we are in the process of leaving the group.
+     In that case, there is no need to background the request. */
+  if (!mLoadGroup) {
+    return;
+  }
+
+  /* There is no need to dispatch if we only need to add ourselves to the load
+     group without removal. It is the removal which causes the problematic
+     callbacks (see RemoveFromLoadGroup). */
+  if (mIsInLoadGroup && mForceDispatchLoadGroup) {
+    LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup -- dispatch");
+
+    RefPtr<imgRequestProxy> self(this);
+    DispatchWithTargetIfAvailable(NS_NewRunnableFunction(
+      "imgRequestProxy::MoveToBackgroundInLoadGroup",
+      [self]() -> void {
+        self->MoveToBackgroundInLoadGroup();
+      }));
+    return;
+  }
+
+  LOG_FUNC(gImgLog, "imgRequestProxy::MoveToBackgroundInLoadGroup");
+  nsCOMPtr<imgIRequest> kungFuDeathGrip(this);
+  if (mIsInLoadGroup) {
+    mLoadGroup->RemoveRequest(this, nullptr, NS_OK);
+  }
+
+  mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
+  mLoadGroup->AddRequest(this, nullptr);
+}
 
 /**  nsIRequest / imgIRequest methods **/
 
 NS_IMETHODIMP
 imgRequestProxy::GetName(nsACString& aName)
 {
   aName.Truncate();
 
@@ -432,16 +490,17 @@ imgRequestProxy::Cancel(nsresult status)
 
 void
 imgRequestProxy::DoCancel(nsresult status)
 {
   if (GetOwner()) {
     GetOwner()->RemoveProxy(this, status);
   }
 
+  RemoveFromLoadGroup();
   NullOutListener();
 }
 
 NS_IMETHODIMP
 imgRequestProxy::CancelAndForgetObserver(nsresult aStatus)
 {
   // If mCanceled is true but mListener is non-null, that means
   // someone called Cancel() on us but the imgCancelRunnable is still
@@ -452,32 +511,23 @@ imgRequestProxy::CancelAndForgetObserver
   if (mCanceled && !mListener) {
     return NS_ERROR_FAILURE;
   }
 
   LOG_SCOPE(gImgLog, "imgRequestProxy::CancelAndForgetObserver");
 
   mCanceled = true;
 
-  // Now cheat and make sure our removal from loadgroup happens async
-  bool oldIsInLoadGroup = mIsInLoadGroup;
-  mIsInLoadGroup = false;
-
   if (GetOwner()) {
     GetOwner()->RemoveProxy(this, aStatus);
   }
 
-  mIsInLoadGroup = oldIsInLoadGroup;
-
-  if (mIsInLoadGroup) {
-    nsCOMPtr<nsIRunnable> ev =
-      NewRunnableMethod("imgRequestProxy::DoRemoveFromLoadGroup",
-                        this, &imgRequestProxy::DoRemoveFromLoadGroup);
-    DispatchWithTargetIfAvailable(ev.forget());
-  }
+  mForceDispatchLoadGroup = true;
+  RemoveFromLoadGroup();
+  mForceDispatchLoadGroup = false;
 
   NullOutListener();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::StartDecoding(uint32_t aFlags)
@@ -603,17 +653,20 @@ NS_IMETHODIMP
 imgRequestProxy::GetLoadGroup(nsILoadGroup** loadGroup)
 {
   NS_IF_ADDREF(*loadGroup = mLoadGroup.get());
   return NS_OK;
 }
 NS_IMETHODIMP
 imgRequestProxy::SetLoadGroup(nsILoadGroup* loadGroup)
 {
-  mLoadGroup = loadGroup;
+  if (loadGroup != mLoadGroup) {
+    MOZ_ASSERT_UNREACHABLE("Switching load groups is unsupported!");
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::GetLoadFlags(nsLoadFlags* flags)
 {
   *flags = mLoadFlags;
   return NS_OK;
@@ -769,50 +822,79 @@ imgRequestProxy::PerformClone(imgINotifi
 {
   NS_PRECONDITION(aClone, "Null out param");
 
   LOG_SCOPE(gImgLog, "imgRequestProxy::Clone");
 
   *aClone = nullptr;
   RefPtr<imgRequestProxy> clone = NewClonedProxy();
 
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  if (aLoadingDocument) {
+    loadGroup = aLoadingDocument->GetDocumentLoadGroup();
+  }
+
   // It is important to call |SetLoadFlags()| before calling |Init()| because
   // |Init()| adds the request to the loadgroup.
   // When a request is added to a loadgroup, its load flags are merged
   // with the load flags of the loadgroup.
   // XXXldb That's not true anymore.  Stuff from imgLoader adds the
   // request to the loadgroup.
   clone->SetLoadFlags(mLoadFlags);
-  nsresult rv = clone->Init(mBehaviour->GetOwner(), mLoadGroup,
+  nsresult rv = clone->Init(mBehaviour->GetOwner(), loadGroup,
                             aLoadingDocument, mURI, aObserver);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // Assign to *aClone before calling Notify so that if the caller expects to
   // only be notified for requests it's already holding pointers to it won't be
   // surprised.
   NS_ADDREF(*aClone = clone);
 
   if (GetOwner() && GetOwner()->GetValidator()) {
     // Note that if we have a validator, we don't want to issue notifications at
-    // here because we want to defer until that completes.
+    // here because we want to defer until that completes. AddProxy will add us
+    // to the load group; we cannot avoid that in this case, because we don't
+    // know when the validation will complete, and if it will cause us to
+    // discard our cached state anyways. We are probably already blocked by the
+    // original LoadImage(WithChannel) request in any event.
     clone->SetNotificationsDeferred(true);
     GetOwner()->GetValidator()->AddProxy(clone);
-  } else if (aSyncNotify) {
-    // This is wrong!!! We need to notify asynchronously, but there's code that
-    // assumes that we don't. This will be fixed in bug 580466. Note that if we
-    // have a validator, we won't issue notifications anyways because they are
-    // deferred, so there is no point in requesting.
-    clone->SyncNotifyListener();
   } else {
-    // Without a validator, we can request asynchronous notifications
-    // immediately. If there was a validator, this would override the deferral
-    // and that would be incorrect.
-    clone->NotifyListener();
+    // We only want to add the request to the load group of the owning document
+    // if it is still in progress. Some callers cannot handle a supurious load
+    // group removal (e.g. print preview) so we must be careful. On the other
+    // hand, if after cloning, the original request proxy is cancelled /
+    // destroyed, we need to ensure that any clones still block the load group
+    // if it is incomplete.
+    bool addToLoadGroup = mIsInLoadGroup;
+    if (!addToLoadGroup) {
+      RefPtr<ProgressTracker> tracker = clone->GetProgressTracker();
+      addToLoadGroup = tracker && !(tracker->GetProgress() & FLAG_LOAD_COMPLETE);
+    }
+
+    if (addToLoadGroup) {
+      clone->AddToLoadGroup();
+    }
+
+    if (aSyncNotify) {
+      // This is wrong!!! We need to notify asynchronously, but there's code
+      // that assumes that we don't. This will be fixed in bug 580466. Note that
+      // if we have a validator, we won't issue notifications anyways because
+      // they are deferred, so there is no point in requesting.
+      clone->mForceDispatchLoadGroup = true;
+      clone->SyncNotifyListener();
+      clone->mForceDispatchLoadGroup = false;
+    } else {
+      // Without a validator, we can request asynchronous notifications
+      // immediately. If there was a validator, this would override the deferral
+      // and that would be incorrect.
+      clone->NotifyListener();
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 imgRequestProxy::GetImagePrincipal(nsIPrincipal** aPrincipal)
 {
@@ -996,91 +1078,37 @@ imgRequestProxy::OnLoadComplete(bool aLa
     listener->Notify(this, imgINotificationObserver::LOAD_COMPLETE, nullptr);
   }
 
   // If we're expecting more data from a multipart channel, re-add ourself
   // to the loadgroup so that the document doesn't lose track of the load.
   // If the request is already a background request and there's more data
   // coming, we can just leave the request in the loadgroup as-is.
   if (aLastPart || (mLoadFlags & nsIRequest::LOAD_BACKGROUND) == 0) {
-    RemoveFromLoadGroup(aLastPart);
-    // More data is coming, so change the request to be a background request
-    // and put it back in the loadgroup.
-    if (!aLastPart) {
-      mLoadFlags |= nsIRequest::LOAD_BACKGROUND;
-      AddToLoadGroup();
+    if (aLastPart) {
+      RemoveFromLoadGroup();
+    } else {
+      // More data is coming, so change the request to be a background request
+      // and put it back in the loadgroup.
+      MoveToBackgroundInLoadGroup();
     }
   }
 
   if (mListenerIsStrongRef && aLastPart) {
     NS_PRECONDITION(mListener, "How did that happen?");
     // Drop our strong ref to the listener now that we're done with
     // everything.  Note that this can cancel us and other fun things
     // like that.  Don't add anything in this method after this point.
     imgINotificationObserver* obs = mListener;
     mListenerIsStrongRef = false;
     NS_RELEASE(obs);
   }
 }
 
 void
-imgRequestProxy::BlockOnload()
-{
-  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
-    nsAutoCString name;
-    GetName(name);
-    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::BlockOnload",
-                        "name", name.get());
-  }
-
-  nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
-  if (!blocker) {
-    return;
-  }
-
-  if (!IsOnEventTarget()) {
-    RefPtr<imgRequestProxy> self(this);
-    DispatchWithTarget(NS_NewRunnableFunction("imgRequestProxy::BlockOnload",
-                                    [self]() -> void {
-      self->BlockOnload();
-    }));
-    return;
-  }
-
-  blocker->BlockOnload(this);
-}
-
-void
-imgRequestProxy::UnblockOnload()
-{
-  if (MOZ_LOG_TEST(gImgLog, LogLevel::Debug)) {
-    nsAutoCString name;
-    GetName(name);
-    LOG_FUNC_WITH_PARAM(gImgLog, "imgRequestProxy::UnblockOnload",
-                        "name", name.get());
-  }
-
-  nsCOMPtr<imgIOnloadBlocker> blocker = do_QueryInterface(mListener);
-  if (!blocker) {
-    return;
-  }
-
-  if (!IsOnEventTarget()) {
-    RefPtr<imgRequestProxy> self(this);
-    DispatchWithTarget(NS_NewRunnableFunction("imgRequestProxy::UnblockOnload",
-                                    [self]() -> void {
-      self->UnblockOnload();
-    }));
-    return;
-  }
-
-  blocker->UnblockOnload(this);
-}
-
-void
 imgRequestProxy::NullOutListener()
 {
   // If we have animation consumers, then they don't matter anymore
   if (mListener) {
     ClearAnimationConsumers();
   }
 
   if (mListenerIsStrongRef) {
--- a/image/imgRequestProxy.h
+++ b/image/imgRequestProxy.h
@@ -78,18 +78,19 @@ public:
                 ImageURL* aURI,
                 imgINotificationObserver* aObserver);
 
   nsresult ChangeOwner(imgRequest* aNewOwner); // this will change mOwner.
                                                // Do not call this if the
                                                // previous owner has already
                                                // sent notifications out!
 
+  // Add the request to the load group, if any. This should only be called once
+  // during initialization.
   void AddToLoadGroup();
-  void RemoveFromLoadGroup(bool releaseLoadGroup);
 
   inline bool HasObserver() const {
     return mListener != nullptr;
   }
 
   // Asynchronously notify this proxy's listener of the current state of the
   // image, and, if we have an imgRequest mOwner, any status changes that
   // happen between the time this function is called and the time the
@@ -101,20 +102,16 @@ public:
   // asynchronously-called function.
   void SyncNotifyListener();
 
   // imgINotificationObserver methods:
   virtual void Notify(int32_t aType,
                       const mozilla::gfx::IntRect* aRect = nullptr) override;
   virtual void OnLoadComplete(bool aLastPart) override;
 
-  // imgIOnloadBlocker methods:
-  virtual void BlockOnload() override;
-  virtual void UnblockOnload() override;
-
   // Other, internal-only methods:
   virtual void SetHasImage() override;
 
   // Whether we want notifications from ProgressTracker to be deferred until
   // an event it has scheduled has been fired.
   virtual bool NotificationsDeferred() const override
   {
     return mDeferNotifications;
@@ -163,26 +160,28 @@ protected:
         return NS_OK;
       }
 
     private:
       RefPtr<imgRequestProxy> mOwner;
       nsresult mStatus;
   };
 
+  /* Remove from and forget the load group. */
+  void RemoveFromLoadGroup();
+
+  /* Remove from the load group and readd as a background request. */
+  void MoveToBackgroundInLoadGroup();
+
   /* Finish up canceling ourselves */
   void DoCancel(nsresult status);
 
   /* Do the proper refcount management to null out mListener */
   void NullOutListener();
 
-  void DoRemoveFromLoadGroup() {
-    RemoveFromLoadGroup(true);
-  }
-
   // Return the ProgressTracker associated with mOwner and/or mImage. It may
   // live either on mOwner or mImage, depending on whether
   //   (a) we have an mOwner at all
   //   (b) whether mOwner has instantiated its image yet
   already_AddRefed<ProgressTracker> GetProgressTracker() const;
 
   nsITimedChannel* TimedChannel()
   {
@@ -231,16 +230,17 @@ private:
   RefPtr<mozilla::dom::TabGroup> mTabGroup;
   nsCOMPtr<nsIEventTarget> mEventTarget;
 
   nsLoadFlags mLoadFlags;
   uint32_t    mLockCount;
   uint32_t    mAnimationConsumers;
   bool mCanceled : 1;
   bool mIsInLoadGroup : 1;
+  bool mForceDispatchLoadGroup : 1;
   bool mListenerIsStrongRef : 1;
   bool mDecodeRequested : 1;
 
   // Whether we want to defer our notifications by the non-virtual Observer
   // interfaces as image loads proceed.
   bool mDeferNotifications : 1;
   bool mHadListener : 1;
   bool mHadDispatch : 1;
--- a/image/moz.build
+++ b/image/moz.build
@@ -24,17 +24,16 @@ XPCSHELL_TESTS_MANIFESTS += ['test/unit/
 
 XPIDL_SOURCES += [
     'imgICache.idl',
     'imgIContainer.idl',
     'imgIContainerDebug.idl',
     'imgIEncoder.idl',
     'imgILoader.idl',
     'imgINotificationObserver.idl',
-    'imgIOnloadBlocker.idl',
     'imgIRequest.idl',
     'imgIScriptedNotificationObserver.idl',
     'imgITools.idl',
     'nsIIconURI.idl',
 ]
 
 XPIDL_MODULE = 'imglib2'
 
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -11,16 +11,17 @@
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasChild.h"
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/SchedulerGroup.h"
+#include "mozilla/dom/ClientManagerActors.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/ipc/PendingIPCBlobChild.h"
@@ -566,16 +567,28 @@ BackgroundChildImpl::AllocPGamepadTestCh
 bool
 BackgroundChildImpl::DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<dom::GamepadTestChannelChild*>(aActor);
   return true;
 }
 
+mozilla::dom::PClientManagerChild*
+BackgroundChildImpl::AllocPClientManagerChild()
+{
+  return mozilla::dom::AllocClientManagerChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPClientManagerChild(mozilla::dom::PClientManagerChild* aActor)
+{
+  return mozilla::dom::DeallocClientManagerChild(aActor);
+}
+
 #ifdef EARLY_BETA_OR_EARLIER
 void
 BackgroundChildImpl::OnChannelReceivedMessage(const Message& aMsg)
 {
   if (aMsg.type() == layout::PVsync::MessageType::Msg_Notify__ID) {
     // Not really necessary to look at the message payload, it will be
     // <0.5ms away from TimeStamp::Now()
     SchedulerGroup::MarkVsyncReceived();
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -199,16 +199,22 @@ protected:
   DeallocPGamepadEventChannelChild(PGamepadEventChannelChild* aActor) override;
 
   virtual PGamepadTestChannelChild*
   AllocPGamepadTestChannelChild() override;
 
   virtual bool
   DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor) override;
 
+  virtual PClientManagerChild*
+  AllocPClientManagerChild() override;
+
+  virtual bool
+  DeallocPClientManagerChild(PClientManagerChild* aActor) override;
+
 #ifdef EARLY_BETA_OR_EARLIER
   virtual void
   OnChannelReceivedMessage(const Message& aMsg) override;
 #endif
 
   virtual PWebAuthnTransactionChild*
   AllocPWebAuthnTransactionChild() override;
 
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -8,16 +8,17 @@
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasParent.h"
 #endif
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/ClientManagerActors.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/FileSystemBase.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GamepadEventChannelParent.h"
 #include "mozilla/dom/GamepadTestChannelParent.h"
 #include "mozilla/dom/PGamepadEventChannelParent.h"
 #include "mozilla/dom/PGamepadTestChannelParent.h"
@@ -962,16 +963,28 @@ BackgroundParentImpl::DeallocPHttpBackgr
 
   // release extra refcount hold by AllocPHttpBackgroundChannelParent
   RefPtr<net::HttpBackgroundChannelParent> actor =
     dont_AddRef(static_cast<net::HttpBackgroundChannelParent*>(aActor));
 
   return true;
 }
 
+mozilla::dom::PClientManagerParent*
+BackgroundParentImpl::AllocPClientManagerParent()
+{
+  return mozilla::dom::AllocClientManagerParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPClientManagerParent(mozilla::dom::PClientManagerParent* aActor)
+{
+  return mozilla::dom::DeallocClientManagerParent(aActor);
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mozilla::ipc::AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -257,14 +257,20 @@ protected:
   virtual PHttpBackgroundChannelParent*
   AllocPHttpBackgroundChannelParent(const uint64_t& aChannelId) override;
 
   virtual mozilla::ipc::IPCResult
   RecvPHttpBackgroundChannelConstructor(PHttpBackgroundChannelParent *aActor,
                                         const uint64_t& aChannelId) override;
   virtual bool
   DeallocPHttpBackgroundChannelParent(PHttpBackgroundChannelParent *aActor) override;
+
+  virtual PClientManagerParent*
+  AllocPClientManagerParent() override;
+
+  virtual bool
+  DeallocPClientManagerParent(PClientManagerParent* aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -6,16 +6,17 @@ include protocol PAsmJSCacheEntry;
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundIndexedDBUtils;
 include protocol PBackgroundStorage;
 include protocol PBackgroundTest;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
+include protocol PClientManager;
 include protocol PFileDescriptorSet;
 include protocol PFileSystemRequest;
 include protocol PGamepadEventChannel;
 include protocol PGamepadTestChannel;
 include protocol PHttpBackgroundChannel;
 include protocol PIPCBlobInputStream;
 include protocol PPendingIPCBlob;
 include protocol PTemporaryIPCBlob;
@@ -56,16 +57,17 @@ sync protocol PBackground
   manages PBackgroundIDBFactory;
   manages PBackgroundIndexedDBUtils;
   manages PBackgroundStorage;
   manages PBackgroundTest;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
+  manages PClientManager;
   manages PFileDescriptorSet;
   manages PFileSystemRequest;
   manages PGamepadEventChannel;
   manages PGamepadTestChannel;
   manages PHttpBackgroundChannel;
   manages PIPCBlobInputStream;
   manages PPendingIPCBlob;
   manages PTemporaryIPCBlob;
@@ -131,16 +133,18 @@ parent:
   async PGamepadTestChannel();
 
   async PHttpBackgroundChannel(uint64_t channelId);
 
   async PWebAuthnTransaction();
 
   async PTemporaryIPCBlob();
 
+  async PClientManager();
+
 child:
   async PCache();
   async PCacheStreamControl();
 
   async PParentToChildStream();
 
   async PPendingIPCBlob(IPCBlob blob);
 
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -846,17 +846,17 @@ static const uint32_t JSCLASS_FOREGROUND
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
 static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
-    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 38;
+    JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 37;
 
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                              \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -4,16 +4,18 @@
  * 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 js_SliceBudget_h
 #define js_SliceBudget_h
 
 #include <stdint.h>
 
+#include "jstypes.h"
+
 namespace js {
 
 struct JS_PUBLIC_API(TimeBudget)
 {
     int64_t budget;
 
     explicit TimeBudget(int64_t milliseconds) { budget = milliseconds; }
 };
--- a/js/src/builtin/Generator.js
+++ b/js/src/builtin/Generator.js
@@ -65,79 +65,16 @@ function StarGeneratorReturn(val) {
         return resumeGenerator(this, rval, "close");
     } catch (e) {
         if (!StarGeneratorObjectIsClosed(this))
             GeneratorSetClosed(this);
         throw e;
     }
 }
 
-function LegacyGeneratorNext(val) {
-    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
-        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorNext");
-
-    if (LegacyGeneratorObjectIsClosed(this))
-        ThrowStopIteration();
-
-    if (GeneratorIsRunning(this))
-        ThrowTypeError(JSMSG_NESTING_GENERATOR);
-
-    try {
-        return resumeGenerator(this, val, "next");
-    } catch (e) {
-        if (!LegacyGeneratorObjectIsClosed(this))
-            GeneratorSetClosed(this);
-        throw e;
-    }
-}
-_SetCanonicalName(LegacyGeneratorNext, "next");
-
-function LegacyGeneratorThrow(val) {
-    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
-        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, val, "LegacyGeneratorThrow");
-
-    if (LegacyGeneratorObjectIsClosed(this))
-        throw val;
-
-    if (GeneratorIsRunning(this))
-        ThrowTypeError(JSMSG_NESTING_GENERATOR);
-
-    try {
-        return resumeGenerator(this, val, "throw");
-    } catch (e) {
-        if (!LegacyGeneratorObjectIsClosed(this))
-            GeneratorSetClosed(this);
-        throw e;
-    }
-}
-
-// Called by js::CloseIterator.
-function LegacyGeneratorCloseInternal() {
-    assert(IsObject(this), "Not an object: " + ToString(this));
-    assert(IsLegacyGeneratorObject(this), "Not a legacy generator object: " + ToString(this));
-    assert(!LegacyGeneratorObjectIsClosed(this), "Already closed: " + ToString(this));
-
-    if (GeneratorIsRunning(this))
-        ThrowTypeError(JSMSG_NESTING_GENERATOR);
-
-    resumeGenerator(this, undefined, "close");
-    if (!LegacyGeneratorObjectIsClosed(this))
-        CloseClosingLegacyGeneratorObject(this);
-}
-
-function LegacyGeneratorClose() {
-    if (!IsObject(this) || !IsLegacyGeneratorObject(this))
-        return callFunction(CallLegacyGeneratorMethodIfWrapped, this, "LegacyGeneratorClose");
-
-    if (LegacyGeneratorObjectIsClosed(this))
-        return undefined;
-
-    callFunction(LegacyGeneratorCloseInternal, this);
-}
-
 function InterpretGeneratorResume(gen, val, kind) {
     // If we want to resume a generator in the interpreter, the script containing
     // the resumeGenerator/JSOP_RESUME also has to run in the interpreter. The
     // forceInterpreter() call below compiles to a bytecode op that prevents us
     // from JITing this script.
     forceInterpreter();
     if (kind === "next")
        return resumeGenerator(gen, val, "next");
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -6,25 +6,23 @@
 
 #include "builtin/MapObject.h"
 
 #include "jscntxt.h"
 #include "jsiter.h"
 #include "jsobj.h"
 
 #include "ds/OrderedHashTable.h"
-#include "gc/Marking.h"
 #include "js/Utility.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/SelfHosting.h"
 #include "vm/Symbol.h"
 
-#include "jsobjinlines.h"
-
+#include "gc/Marking-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsNaN;
 using mozilla::NumberEqualsInt32;
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -223,17 +223,16 @@ GetPropertyDefault(JSContext* cx, Handle
         return true;
     }
     return GetProperty(cx, obj, obj, id, result);
 }
 
 enum class GeneratorStyle
 {
     None,
-    Legacy,
     ES6
 };
 
 /*
  * Builder class that constructs JavaScript AST node objects. See:
  *
  *     https://developer.mozilla.org/en/SpiderMonkey/Parser_API
  *
@@ -1608,24 +1607,21 @@ NodeBuilder::function(ASTType type, Toke
     RootedValue isExpressionVal(cx, BooleanValue(isExpression));
 
     RootedValue cb(cx, callbacks[type]);
     if (!cb.isNull()) {
         return callback(cb, opt(id), array, body, isGeneratorVal, isExpressionVal, pos, dst);
     }
 
     if (isGenerator) {
-        // Distinguish ES6 generators from legacy generators.
-        RootedValue styleVal(cx);
-        JSAtom* styleStr = generatorStyle == GeneratorStyle::ES6
-                           ? Atomize(cx, "es6", 3)
-                           : Atomize(cx, "legacy", 6);
+        MOZ_ASSERT(generatorStyle == GeneratorStyle::ES6);
+        JSAtom* styleStr = Atomize(cx, "es6", 3);
         if (!styleStr)
             return false;
-        styleVal.setString(styleStr);
+        RootedValue styleVal(cx, StringValue(styleStr));
         return newNode(type, pos,
                        "id", id,
                        "params", array,
                        "defaults", defarray,
                        "body", body,
                        "rest", rest,
                        "generator", isGeneratorVal,
                        "async", isAsyncVal,
@@ -3428,18 +3424,16 @@ ASTSerializer::identifier(ParseNode* pn,
 bool
 ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst)
 {
     RootedFunction func(cx, pn->pn_funbox->function());
 
     GeneratorStyle generatorStyle =
         pn->pn_funbox->isStarGenerator()
         ? GeneratorStyle::ES6
-        : pn->pn_funbox->isLegacyGenerator()
-        ? GeneratorStyle::Legacy
         : GeneratorStyle::None;
 
     bool isAsync = pn->pn_funbox->isAsync();
     bool isExpression = pn->pn_funbox->isExprBody();
 
     RootedValue id(cx);
     RootedAtom funcAtom(cx, func->explicitName());
     if (!optIdentifier(funcAtom, nullptr, &id))
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/TypedObject-inl.h
@@ -0,0 +1,24 @@
+/* -*- 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 builtin_TypedObject_inl_h
+#define builtin_TypedObject_inl_h
+
+#include "builtin/TypedObject.h"
+
+#include "gc/ObjectKind-inl.h"
+
+/* static */
+js::gc::AllocKind
+js::InlineTypedObject::allocKindForTypeDescriptor(TypeDescr* descr)
+{
+    size_t nbytes = descr->size();
+    MOZ_ASSERT(nbytes <= MaximumSize);
+
+    return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
+}
+
+#endif // builtin_TypedObject_inl_h
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1,15 +1,15 @@
 /* -*- 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 "builtin/TypedObject.h"
+#include "builtin/TypedObject-inl.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jscompartment.h"
 #include "jsfun.h"
 #include "jsutil.h"
 
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -702,22 +702,17 @@ class InlineTypedObject : public TypedOb
   protected:
     uint8_t* inlineTypedMem() const {
         return (uint8_t*) &data_;
     }
 
   public:
     static const size_t MaximumSize = JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
 
-    static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
-        size_t nbytes = descr->size();
-        MOZ_ASSERT(nbytes <= MaximumSize);
-
-        return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
-    }
+    static inline gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr);
 
     uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
         return inlineTypedMem();
     }
 
     uint8_t* inlineTypedMemForGC() const {
         return inlineTypedMem();
     }
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -679,17 +679,16 @@ frontend::CompileLazyFunction(JSContext*
         return false;
     Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
                                               /* foldConstants = */ true, usedNames, nullptr,
                                               lazy);
     if (!parser.checkOptions())
         return false;
 
     Rooted<JSFunction*> fun(cx, lazy->functionNonDelazifying());
-    MOZ_ASSERT(!lazy->isLegacyGenerator());
     ParseNode* pn = parser.standaloneLazyFunction(fun, lazy->toStringStart(),
                                                   lazy->strict(), lazy->generatorKind(),
                                                   lazy->asyncKind());
     if (!pn)
         return false;
 
     RootedScriptSource sourceObject(cx, lazy->sourceObject());
     MOZ_ASSERT(sourceObject);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -32,17 +32,16 @@
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Stack.h"
 #include "wasm/AsmJS.h"
 
 #include "jsatominlines.h"
-#include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
@@ -396,17 +395,16 @@ class BytecodeEmitter::EmitterScope : pu
     }
 
     void updateFrameFixedSlots(BytecodeEmitter* bce, const BindingIter& bi) {
         nextFrameSlot_ = bi.nextFrameSlot();
         if (nextFrameSlot_ > bce->maxFixedSlots)
             bce->maxFixedSlots = nextFrameSlot_;
         MOZ_ASSERT_IF(bce->sc->isFunctionBox() &&
                       (bce->sc->asFunctionBox()->isStarGenerator() ||
-                       bce->sc->asFunctionBox()->isLegacyGenerator() ||
                        bce->sc->asFunctionBox()->isAsync()),
                       bce->maxFixedSlots == 0);
     }
 
     MOZ_MUST_USE bool putNameInCache(BytecodeEmitter* bce, JSAtom* name, NameLocation loc) {
         NameLocationMap& cache = *nameCache_;
         NameLocationMap::AddPtr p = cache.lookupForAdd(name);
         MOZ_ASSERT(!p);
@@ -4829,17 +4827,16 @@ BytecodeEmitter::isRunOnceLambda()
         (emitterMode != LazyFunction || !lazyScript->treatAsRunOnce()))
     {
         return false;
     }
 
     FunctionBox* funbox = sc->asFunctionBox();
     return !funbox->argumentsHasLocalBinding() &&
            !funbox->isStarGenerator() &&
-           !funbox->isLegacyGenerator() &&
            !funbox->isAsync() &&
            !funbox->function()->explicitName();
 }
 
 bool
 BytecodeEmitter::emitYieldOp(JSOp op)
 {
     if (op == JSOP_FINALYIELDRVAL)
--- a/js/src/frontend/LanguageExtensions.h
+++ b/js/src/frontend/LanguageExtensions.h
@@ -20,17 +20,17 @@ namespace js {
  * disuse, remove it without reassigning its value to a new or existing
  * initializer.  The *only* initializer whose value should ever change is
  * DeprecatedLanguageExtension::Count.
  */
 enum class DeprecatedLanguageExtension
 {
     ForEach = 0, // JS 1.6+
     // NO LONGER USING 1
-    LegacyGenerator = 2, // JS 1.7+
+    // NO LONGER USING 2
     ExpressionClosure = 3, // Added in JS 1.8
     // NO LONGER USING 4
     // NO LONGER USING 5
     // NO LONGER USING 6
     // NO LONGER USING 7
     // NO LONGER USING 8
     // NO LONGER USING 9
     BlockScopeFunRedecl = 10,
--- a/js/src/frontend/ParseContext.h
+++ b/js/src/frontend/ParseContext.h
@@ -615,37 +615,31 @@ class ParseContext : public Nestable<Par
     bool superScopeNeedsHomeObject() const {
         return superScopeNeedsHomeObject_;
     }
 
     bool useAsmOrInsideUseAsm() const {
         return sc_->isFunctionBox() && sc_->asFunctionBox()->useAsmOrInsideUseAsm();
     }
 
-    // Most functions start off being parsed as non-generators.
-    // Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
     // An ES6 generator is marked as a "star generator" before its body is parsed.
     GeneratorKind generatorKind() const {
         return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
     }
 
-    bool isLegacyGenerator() const {
-        return generatorKind() == LegacyGenerator;
-    }
-
     bool isStarGenerator() const {
         return generatorKind() == StarGenerator;
     }
 
     bool isAsync() const {
         return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
     }
 
     bool needsDotGeneratorName() const {
-        return isStarGenerator() || isLegacyGenerator() || isAsync();
+        return isStarGenerator() || isAsync();
     }
 
     FunctionAsyncKind asyncKind() const {
         return isAsync() ? AsyncFunction : SyncFunction;
     }
 
     bool isArrowFunction() const {
         return sc_->isFunctionBox() && sc_->asFunctionBox()->function()->isArrow();
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2124,37 +2124,16 @@ Parser<FullParseHandler, char16_t>::fini
     if (!propagateFreeNamesAndMarkClosedOverBindings(scope))
         return nullptr;
     Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(scope);
     if (!bindings)
         return nullptr;
     return handler.newLexicalScope(*bindings, body);
 }
 
-static bool
-IsArgumentsUsedInLegacyGenerator(JSContext* cx, Scope* scope)
-{
-    JSAtom* argumentsName = cx->names().arguments;
-    for (ScopeIter si(scope); si; si++) {
-        if (si.scope()->is<LexicalScope>()) {
-            // Using a shadowed lexical 'arguments' is okay.
-            for (::BindingIter bi(si.scope()); bi; bi++) {
-                if (bi.name() == argumentsName)
-                    return false;
-            }
-        } else if (si.scope()->is<FunctionScope>()) {
-            // It's an error to use 'arguments' in a legacy generator expression.
-            JSScript* script = si.scope()->as<FunctionScope>().script();
-            return script->isGeneratorExp() && script->isLegacyGenerator();
-        }
-    }
-
-    return false;
-}
-
 template <>
 ParseNode*
 Parser<FullParseHandler, char16_t>::evalBody(EvalSharedContext* evalsc)
 {
     SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr);
     if (!evalpc.init())
         return nullptr;
 
@@ -2176,29 +2155,16 @@ Parser<FullParseHandler, char16_t>::eval
         if (!checkStatementsEOF())
             return nullptr;
 
         body = finishLexicalScope(lexicalScope, body);
         if (!body)
             return nullptr;
     }
 
-    // It's an error to use 'arguments' in a legacy generator expression.
-    //
-    // If 'arguments' appears free (i.e. not a declared name) or if the
-    // declaration does not shadow the enclosing script's 'arguments'
-    // binding (i.e. not a lexical declaration), check the enclosing
-    // script.
-    if (hasUsedName(context->names().arguments)) {
-        if (IsArgumentsUsedInLegacyGenerator(context, pc->sc()->compilationEnclosingScope())) {
-            error(JSMSG_BAD_GENEXP_BODY, js_arguments_str);
-            return nullptr;
-        }
-    }
-
 #ifdef DEBUG
     if (evalpc.superScopeNeedsHomeObject() && evalsc->compilationEnclosingScope()) {
         // If superScopeNeedsHomeObject_ is set and we are an entry-point
         // ParseContext, then we must be emitting an eval script, and the
         // outer function must already be marked as needing a home object
         // since it contains an eval.
         ScopeIter si(evalsc->compilationEnclosingScope());
         for (; si; si++) {
@@ -2778,28 +2744,16 @@ Parser<ParseHandler, CharT>::functionBod
         }
     }
 
     switch (pc->generatorKind()) {
       case NotGenerator:
         MOZ_ASSERT_IF(!pc->isAsync(), pc->lastYieldOffset == startYieldOffset);
         break;
 
-      case LegacyGenerator:
-        MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
-
-        // These should throw while parsing the yield expression.
-        MOZ_ASSERT(kind != Arrow);
-        MOZ_ASSERT(!IsGetterKind(kind));
-        MOZ_ASSERT(!IsSetterKind(kind));
-        MOZ_ASSERT(!IsConstructorKind(kind));
-        MOZ_ASSERT(kind != Method);
-        MOZ_ASSERT(type != ExpressionBody);
-        break;
-
       case StarGenerator:
         MOZ_ASSERT(kind != Arrow);
         MOZ_ASSERT(type == StatementListBody);
         break;
     }
 
     if (pc->needsDotGeneratorName()) {
         MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
@@ -3291,17 +3245,16 @@ Parser<FullParseHandler, char16_t>::skip
                                                           FunctionSyntaxKind kind, bool tryAnnexB)
 {
     // When a lazily-parsed function is called, we only fully parse (and emit)
     // that function, not any of its nested children. The initial syntax-only
     // parse recorded the free variables of nested functions and their extents,
     // so we can skip over them after accounting for their free variables.
 
     RootedFunction fun(context, handler.nextLazyInnerFunction());
-    MOZ_ASSERT(!fun->isLegacyGenerator());
     FunctionBox* funbox = newFunctionBox(pn, fun, toStringStart, Directives(/* strict = */ false),
                                          fun->generatorKind(), fun->asyncKind());
     if (!funbox)
         return false;
 
     LazyScript* lazy = fun->lazyScript();
     if (lazy->needsHomeObject())
         funbox->setNeedsHomeObject();
@@ -6688,27 +6641,17 @@ Parser<ParseHandler, CharT>::returnState
     if (exprNode) {
         if (!matchOrInsertSemicolonAfterExpression())
             return null();
     } else {
         if (!matchOrInsertSemicolonAfterNonExpression())
             return null();
     }
 
-    Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
-    if (!pn)
-        return null();
-
-    /* Disallow "return v;" in legacy generators. */
-    if (pc->isLegacyGenerator() && exprNode) {
-        errorAt(begin, JSMSG_BAD_GENERATOR_RETURN);
-        return null();
-    }
-
-    return pn;
+    return handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::yieldExpression(InHandling inHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
     uint32_t begin = pos().begin;
@@ -6753,96 +6696,18 @@ Parser<ParseHandler, CharT>::yieldExpres
             exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
             if (!exprNode)
                 return null();
         }
         if (kind == PNK_YIELD_STAR)
             return handler.newYieldStarExpression(begin, exprNode);
         return handler.newYieldExpression(begin, exprNode);
       }
-
       case NotGenerator:
-        // We are in code that has not seen a yield, but we are in JS 1.7 or
-        // later.  Try to transition to being a legacy generator.
-        MOZ_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
-        MOZ_ASSERT(pc->lastYieldOffset == ParseContext::NoYieldOffset);
-
-        if (!abortIfSyntaxParser())
-            return null();
-
-        if (!pc->isFunctionBox()) {
-            error(JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
-            return null();
-        }
-
-        if (pc->functionBox()->isArrow()) {
-            errorAt(begin, JSMSG_YIELD_IN_ARROW, js_yield_str);
-            return null();
-        }
-
-        if (pc->functionBox()->function()->isMethod() ||
-            pc->functionBox()->function()->isGetter() ||
-            pc->functionBox()->function()->isSetter())
-        {
-            errorAt(begin, JSMSG_YIELD_IN_METHOD, js_yield_str);
-            return null();
-        }
-
-        if (pc->funHasReturnExpr
-#if JS_HAS_EXPR_CLOSURES
-            || pc->functionBox()->isExprBody()
-#endif
-            )
-        {
-            /* As in Python (see PEP-255), disallow return v; in generators. */
-            errorAt(begin, JSMSG_BAD_FUNCTION_YIELD);
-            return null();
-        }
-
-        pc->functionBox()->setGeneratorKind(LegacyGenerator);
-        addTelemetry(DeprecatedLanguageExtension::LegacyGenerator);
-        if (!warnOnceAboutLegacyGenerator())
-            return null();
-
-        MOZ_FALLTHROUGH;
-
-      case LegacyGenerator:
-      {
-        // We are in a legacy generator: a function that has already seen a
-        // yield.
-        MOZ_ASSERT(pc->isFunctionBox());
-
-        pc->lastYieldOffset = begin;
-
-        // Legacy generators do not require a value.
-        Node exprNode;
-        TokenKind tt = TOK_EOF;
-        if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
-            return null();
-        switch (tt) {
-          case TOK_EOF:
-          case TOK_EOL:
-          case TOK_SEMI:
-          case TOK_RC:
-          case TOK_RB:
-          case TOK_RP:
-          case TOK_COLON:
-          case TOK_COMMA:
-            // No value.
-            exprNode = null();
-            tokenStream.addModifierException(TokenStream::NoneIsOperand);
-            break;
-          default:
-            exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
-            if (!exprNode)
-                return null();
-        }
-
-        return handler.newYieldExpression(begin, exprNode);
-      }
+        break;
     }
 
     MOZ_CRASH("yieldExpr");
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::withStatement(YieldHandling yieldHandling)
@@ -10441,27 +10306,13 @@ ParserBase::warnOnceAboutForEach()
     if (!context->compartment()->warnedAboutForEach) {
         if (!warning(JSMSG_DEPRECATED_FOR_EACH))
             return false;
         context->compartment()->warnedAboutForEach = true;
     }
     return true;
 }
 
-bool
-ParserBase::warnOnceAboutLegacyGenerator()
-{
-    if (context->helperThread())
-        return true;
-
-    if (!context->compartment()->warnedAboutLegacyGenerator) {
-        if (!warning(JSMSG_DEPRECATED_LEGACY_GENERATOR))
-            return false;
-        context->compartment()->warnedAboutLegacyGenerator = true;
-    }
-    return true;
-}
-
 template class Parser<FullParseHandler, char16_t>;
 template class Parser<SyntaxParseHandler, char16_t>;
 
 } /* namespace frontend */
 } /* namespace js */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -166,23 +166,19 @@ class ParserBase : public StrictModeGett
                const char16_t* chars, size_t length, bool foldConstants,
                UsedNameTracker& usedNames, LazyScript* lazyOuterFunction);
     ~ParserBase();
 
     const char* getFilename() const { return tokenStream.getFilename(); }
     JSVersion versionNumber() const { return tokenStream.versionNumber(); }
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
-    // 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.
+    // Determine whether |yield| is a valid name in the current context.
     bool yieldExpressionsSupported() {
-        return (versionNumber() >= JSVERSION_1_7 && !pc->isAsync()) ||
-               pc->isStarGenerator() ||
-               pc->isLegacyGenerator();
+        return pc->isStarGenerator();
     }
 
     virtual bool strictMode() { return pc->sc()->strict(); }
     bool setLocalStrictMode(bool strict) {
         MOZ_ASSERT(tokenStream.debugHasNoLookahead());
         return pc->sc()->setLocalStrictMode(strict);
     }
 
@@ -236,17 +232,16 @@ class ParserBase : public StrictModeGett
     MOZ_MUST_USE bool extraWarningAt(uint32_t offset, unsigned errorNumber, ...);
 
     bool isValidStrictBinding(PropertyName* name);
 
     void addTelemetry(DeprecatedLanguageExtension e);
 
     bool warnOnceAboutExprClosure();
     bool warnOnceAboutForEach();
-    bool warnOnceAboutLegacyGenerator();
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return options().forEachStatementOption && versionNumber() >= JSVERSION_1_6;
 #endif
     }
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -464,17 +464,16 @@ class FunctionBox : public ObjectBox, pu
         return enclosingScope_;
     }
 
     bool needsCallObjectRegardlessOfBindings() const {
         return hasExtensibleScope() ||
                needsHomeObject() ||
                isDerivedClassConstructor() ||
                isStarGenerator() ||
-               isLegacyGenerator() ||
                isAsync();
     }
 
     bool hasExtraBodyVarScope() const {
         return hasParameterExprs &&
                (extraVarScopeBindings_ ||
                 needsExtraBodyVarEnvironmentRegardlessOfBindings());
     }
@@ -484,25 +483,24 @@ class FunctionBox : public ObjectBox, pu
         return hasExtensibleScope() || needsDotGeneratorName();
     }
 
     bool isLikelyConstructorWrapper() const {
         return usesArguments && usesApply && usesThis && !usesReturn;
     }
 
     GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
-    bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == StarGenerator; }
     FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); }
 
     bool needsFinalYield() const {
-        return isStarGenerator() || isLegacyGenerator() || isAsync();
+        return isStarGenerator() || isAsync();
     }
     bool needsDotGeneratorName() const {
-        return isStarGenerator() || isLegacyGenerator() || isAsync();
+        return isStarGenerator() || isAsync();
     }
     bool needsIteratorResult() const {
         return isStarGenerator();
     }
 
     bool isAsync() const { return asyncKind() == AsyncFunction; }
     bool isArrow() const { return function()->isArrow(); }
 
@@ -515,17 +513,17 @@ class FunctionBox : public ObjectBox, pu
     void setIsExprBody() {
         isExprBody_ = true;
     }
 
     void setGeneratorKind(GeneratorKind kind) {
         // A generator kind can be set at initialization, or when "yield" is
         // first seen.  In both cases the transition can only happen from
         // NotGenerator.
-        MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator());
+        MOZ_ASSERT(!isStarGenerator());
         generatorKindBits_ = GeneratorKindAsBits(kind);
     }
 
     bool hasExtensibleScope()        const { return funCxFlags.hasExtensibleScope; }
     bool hasThisBinding()            const { return funCxFlags.hasThisBinding; }
     bool argumentsHasLocalBinding()  const { return funCxFlags.argumentsHasLocalBinding; }
     bool definitelyNeedsArgsObj()    const { return funCxFlags.definitelyNeedsArgsObj; }
     bool needsHomeObject()           const { return funCxFlags.needsHomeObject; }
@@ -619,16 +617,15 @@ SharedContext::asModuleContext()
 // frame, as the generator frame will be copied out to the heap and released
 // only by GC.
 inline bool
 SharedContext::allBindingsClosedOver()
 {
     return bindingsAccessedDynamically() ||
            (isFunctionBox() &&
             (asFunctionBox()->isStarGenerator() ||
-             asFunctionBox()->isLegacyGenerator() ||
              asFunctionBox()->isAsync()));
 }
 
 } // namespace frontend
 } // namespace js
 
 #endif /* frontend_SharedContext_h */
new file mode 100644
--- /dev/null
+++ b/js/src/gc/AllocKind.h
@@ -0,0 +1,275 @@
+/* -*- 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/. */
+
+/*
+ * GC-internal definition of GC cell kinds.
+ */
+
+#ifndef gc_AllocKind_h
+#define gc_AllocKind_h
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/EnumeratedArray.h"
+#include "mozilla/EnumeratedRange.h"
+
+#include <stdint.h>
+
+#include "js/TraceKind.h"
+
+namespace js {
+namespace gc {
+
+/* The GC allocation kinds. */
+// FIXME: uint8_t would make more sense for the underlying type, but causes
+// miscompilations in GCC (fixed in 4.8.5 and 4.9.3). See also bug 1143966.
+enum class AllocKind {
+    FIRST,
+    OBJECT_FIRST = FIRST,
+    FUNCTION = FIRST,
+    FUNCTION_EXTENDED,
+    OBJECT0,
+    OBJECT0_BACKGROUND,
+    OBJECT2,
+    OBJECT2_BACKGROUND,
+    OBJECT4,
+    OBJECT4_BACKGROUND,
+    OBJECT8,
+    OBJECT8_BACKGROUND,
+    OBJECT12,
+    OBJECT12_BACKGROUND,
+    OBJECT16,
+    OBJECT16_BACKGROUND,
+    OBJECT_LIMIT,
+    OBJECT_LAST = OBJECT_LIMIT - 1,
+    SCRIPT,
+    LAZY_SCRIPT,
+    SHAPE,
+    ACCESSOR_SHAPE,
+    BASE_SHAPE,
+    OBJECT_GROUP,
+    FAT_INLINE_STRING,
+    STRING,
+    EXTERNAL_STRING,
+    FAT_INLINE_ATOM,
+    ATOM,
+    SYMBOL,
+    JITCODE,
+    SCOPE,
+    REGEXP_SHARED,
+    LIMIT,
+    LAST = LIMIT - 1
+};
+
+// Macro to enumerate the different allocation kinds supplying information about
+// the trace kind, C++ type and allocation size.
+#define FOR_EACH_OBJECT_ALLOCKIND(D) \
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(FUNCTION,            Object,        JSObject,          JSFunction) \
+    D(FUNCTION_EXTENDED,   Object,        JSObject,          FunctionExtended) \
+    D(OBJECT0,             Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT0_BACKGROUND,  Object,        JSObject,          JSObject_Slots0) \
+    D(OBJECT2,             Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT2_BACKGROUND,  Object,        JSObject,          JSObject_Slots2) \
+    D(OBJECT4,             Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT4_BACKGROUND,  Object,        JSObject,          JSObject_Slots4) \
+    D(OBJECT8,             Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT8_BACKGROUND,  Object,        JSObject,          JSObject_Slots8) \
+    D(OBJECT12,            Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT12_BACKGROUND, Object,        JSObject,          JSObject_Slots12) \
+    D(OBJECT16,            Object,        JSObject,          JSObject_Slots16) \
+    D(OBJECT16_BACKGROUND, Object,        JSObject,          JSObject_Slots16)
+
+#define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
+ /* AllocKind              TraceKind      TypeName           SizedType */ \
+    D(SCRIPT,              Script,        JSScript,          JSScript) \
+    D(LAZY_SCRIPT,         LazyScript,    js::LazyScript,    js::LazyScript) \
+    D(SHAPE,               Shape,         js::Shape,         js::Shape) \
+    D(ACCESSOR_SHAPE,      Shape,         js::AccessorShape, js::AccessorShape) \
+    D(BASE_SHAPE,          BaseShape,     js::BaseShape,     js::BaseShape) \
+    D(OBJECT_GROUP,        ObjectGroup,   js::ObjectGroup,   js::ObjectGroup) \
+    D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString) \
+    D(STRING,              String,        JSString,          JSString) \
+    D(EXTERNAL_STRING,     String,        JSExternalString,  JSExternalString) \
+    D(FAT_INLINE_ATOM,     String,        js::FatInlineAtom, js::FatInlineAtom) \
+    D(ATOM,                String,        js::NormalAtom,    js::NormalAtom) \
+    D(SYMBOL,              Symbol,        JS::Symbol,        JS::Symbol) \
+    D(JITCODE,             JitCode,       js::jit::JitCode,  js::jit::JitCode) \
+    D(SCOPE,               Scope,         js::Scope,         js::Scope) \
+    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
+
+#define FOR_EACH_ALLOCKIND(D) \
+    FOR_EACH_OBJECT_ALLOCKIND(D) \
+    FOR_EACH_NONOBJECT_ALLOCKIND(D)
+
+static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
+                                          "please audit them carefully!");
+static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
+                                                 "being 0, please audit them carefully!");
+
+inline bool
+IsAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::FIRST && kind <= AllocKind::LIMIT;
+}
+
+inline bool
+IsValidAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::FIRST && kind <= AllocKind::LAST;
+}
+
+inline bool
+IsObjectAllocKind(AllocKind kind)
+{
+    return kind >= AllocKind::OBJECT_FIRST && kind <= AllocKind::OBJECT_LAST;
+}
+
+inline bool
+IsShapeAllocKind(AllocKind kind)
+{
+    return kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE;
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over all alloc kinds.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
+AllAllocKinds()
+{
+    return mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT);
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over all object alloc kinds.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
+ObjectAllocKinds()
+{
+    return mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
+}
+
+// Returns a sequence for use in a range-based for loop,
+// to iterate over alloc kinds from |first| to |limit|, exclusive.
+inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
+SomeAllocKinds(AllocKind first = AllocKind::FIRST, AllocKind limit = AllocKind::LIMIT)
+{
+    MOZ_ASSERT(IsAllocKind(first), "|first| is not a valid AllocKind!");
+    MOZ_ASSERT(IsAllocKind(limit), "|limit| is not a valid AllocKind!");
+    return mozilla::MakeEnumeratedRange(first, limit);
+}
+
+// AllAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
+// with each index corresponding to a particular alloc kind.
+template<typename ValueType> using AllAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>;
+
+// ObjectAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
+// with each index corresponding to a particular object alloc kind.
+template<typename ValueType> using ObjectAllocKindArray =
+    mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>;
+
+static inline JS::TraceKind
+MapAllocToTraceKind(AllocKind kind)
+{
+    static const JS::TraceKind map[] = {
+#define EXPAND_ELEMENT(allocKind, traceKind, type, sizedType) \
+        JS::TraceKind::traceKind,
+FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
+#undef EXPAND_ELEMENT
+    };
+
+    static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT),
+                  "AllocKind-to-TraceKind mapping must be in sync");
+    return map[size_t(kind)];
+}
+
+/*
+ * This must be an upper bound, but we do not need the least upper bound, so
+ * we just exclude non-background objects.
+ */
+static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
+    size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
+
+static inline bool
+IsNurseryAllocable(AllocKind kind)
+{
+    MOZ_ASSERT(IsValidAllocKind(kind));
+    static const bool map[] = {
+        true,      /* AllocKind::FUNCTION */
+        true,      /* AllocKind::FUNCTION_EXTENDED */
+        false,     /* AllocKind::OBJECT0 */
+        true,      /* AllocKind::OBJECT0_BACKGROUND */
+        false,     /* AllocKind::OBJECT2 */
+        true,      /* AllocKind::OBJECT2_BACKGROUND */
+        false,     /* AllocKind::OBJECT4 */
+        true,      /* AllocKind::OBJECT4_BACKGROUND */
+        false,     /* AllocKind::OBJECT8 */
+        true,      /* AllocKind::OBJECT8_BACKGROUND */
+        false,     /* AllocKind::OBJECT12 */
+        true,      /* AllocKind::OBJECT12_BACKGROUND */
+        false,     /* AllocKind::OBJECT16 */
+        true,      /* AllocKind::OBJECT16_BACKGROUND */
+        false,     /* AllocKind::SCRIPT */
+        false,     /* AllocKind::LAZY_SCRIPT */
+        false,     /* AllocKind::SHAPE */
+        false,     /* AllocKind::ACCESSOR_SHAPE */
+        false,     /* AllocKind::BASE_SHAPE */
+        false,     /* AllocKind::OBJECT_GROUP */
+        false,     /* AllocKind::FAT_INLINE_STRING */
+        false,     /* AllocKind::STRING */
+        false,     /* AllocKind::EXTERNAL_STRING */
+        false,     /* AllocKind::FAT_INLINE_ATOM */
+        false,     /* AllocKind::ATOM */
+        false,     /* AllocKind::SYMBOL */
+        false,     /* AllocKind::JITCODE */
+        false,     /* AllocKind::SCOPE */
+        false,     /* AllocKind::REGEXP_SHARED */
+    };
+    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
+    return map[size_t(kind)];
+}
+
+static inline bool
+IsBackgroundFinalized(AllocKind kind)
+{
+    MOZ_ASSERT(IsValidAllocKind(kind));
+    static const bool map[] = {
+        true,      /* AllocKind::FUNCTION */
+        true,      /* AllocKind::FUNCTION_EXTENDED */
+        false,     /* AllocKind::OBJECT0 */
+        true,      /* AllocKind::OBJECT0_BACKGROUND */
+        false,     /* AllocKind::OBJECT2 */
+        true,      /* AllocKind::OBJECT2_BACKGROUND */
+        false,     /* AllocKind::OBJECT4 */
+        true,      /* AllocKind::OBJECT4_BACKGROUND */
+        false,     /* AllocKind::OBJECT8 */
+        true,      /* AllocKind::OBJECT8_BACKGROUND */
+        false,     /* AllocKind::OBJECT12 */
+        true,      /* AllocKind::OBJECT12_BACKGROUND */
+        false,     /* AllocKind::OBJECT16 */
+        true,      /* AllocKind::OBJECT16_BACKGROUND */
+        false,     /* AllocKind::SCRIPT */
+        true,      /* AllocKind::LAZY_SCRIPT */
+        true,      /* AllocKind::SHAPE */
+        true,      /* AllocKind::ACCESSOR_SHAPE */
+        true,      /* AllocKind::BASE_SHAPE */
+        true,      /* AllocKind::OBJECT_GROUP */
+        true,      /* AllocKind::FAT_INLINE_STRING */
+        true,      /* AllocKind::STRING */
+        true,      /* AllocKind::EXTERNAL_STRING */
+        true,      /* AllocKind::FAT_INLINE_ATOM */
+        true,      /* AllocKind::ATOM */
+        true,      /* AllocKind::SYMBOL */
+        false,     /* AllocKind::JITCODE */
+        true,      /* AllocKind::SCOPE */
+        true,      /* AllocKind::REGEXP_SHARED */
+    };
+    JS_STATIC_ASSERT(JS_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT));
+    return map[size_t(kind)];
+}
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif /* gc_AllocKind_h */
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -3,27 +3,27 @@
  * 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 "gc/Allocator.h"
 
 #include "jscntxt.h"
 
-#include "gc/ArenaList.h"
 #include "gc/GCInternals.h"
 #include "gc/GCTrace.h"
 #include "gc/Nursery.h"
 #include "jit/JitCompartment.h"
 #include "threading/CpuCount.h"
 #include "vm/Runtime.h"
 #include "vm/String.h"
 
 #include "jsobjinlines.h"
 
+#include "gc/ArenaList-inl.h"
 #include "gc/Heap-inl.h"
 
 using namespace js;
 using namespace gc;
 
 template <typename T, AllowGC allowGC /* = CanGC */>
 JSObject*
 js::Allocate(JSContext* cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap,
--- a/js/src/gc/Allocator.h
+++ b/js/src/gc/Allocator.h
@@ -6,16 +6,17 @@
 
 #ifndef gc_Allocator_h
 #define gc_Allocator_h
 
 #include "gc/Heap.h"
 #include "js/RootingAPI.h"
 
 namespace js {
+
 struct Class;
 
 // Allocate a new GC thing. After a successful allocation the caller must
 // fully initialize the thing before calling any function that can potentially
 // trigger GC. This will ensure that GC tracing never sees junk values stored
 // in the partially initialized thing.
 //
 // Note that JSObject allocation must use the longer signature below that
new file mode 100644
--- /dev/null
+++ b/js/src/gc/ArenaList-inl.h
@@ -0,0 +1,353 @@
+/* -*- 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_ArenaList_inl_h
+#define gc_ArenaList_inl_h
+
+#include "gc/ArenaList.h"
+
+#include "gc/Heap.h"
+
+void
+js::gc::SortedArenaListSegment::append(Arena* arena)
+{
+    MOZ_ASSERT(arena);
+    MOZ_ASSERT_IF(head, head->getAllocKind() == arena->getAllocKind());
+    *tailp = arena;
+    tailp = &arena->next;
+}
+
+inline
+js::gc::ArenaList::ArenaList()
+{
+    clear();
+}
+
+void
+js::gc::ArenaList::copy(const ArenaList& other)
+{
+    other.check();
+    head_ = other.head_;
+    cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
+    check();
+}
+
+inline
+js::gc::ArenaList::ArenaList(const ArenaList& other)
+{
+    copy(other);
+}
+
+js::gc::ArenaList&
+js::gc::ArenaList::operator=(const ArenaList& other)
+{
+    copy(other);
+    return *this;
+}
+
+inline
+js::gc::ArenaList::ArenaList(const SortedArenaListSegment& segment)
+{
+    head_ = segment.head;
+    cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
+    check();
+}
+
+// This does checking just of |head_| and |cursorp_|.
+void
+js::gc::ArenaList::check() const
+{
+#ifdef DEBUG
+    // If the list is empty, it must have this form.
+    MOZ_ASSERT_IF(!head_, cursorp_ == &head_);
+
+    // If there's an arena following the cursor, it must not be full.
+    Arena* cursor = *cursorp_;
+    MOZ_ASSERT_IF(cursor, cursor->hasFreeThings());
+#endif
+}
+
+void
+js::gc::ArenaList::clear()
+{
+    head_ = nullptr;
+    cursorp_ = &head_;
+    check();
+}
+
+js::gc::ArenaList
+js::gc::ArenaList::copyAndClear()
+{
+    ArenaList result = *this;
+    clear();
+    return result;
+}
+
+bool
+js::gc::ArenaList::isEmpty() const
+{
+    check();
+    return !head_;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::head() const
+{
+    check();
+    return head_;
+}
+
+bool
+js::gc::ArenaList::isCursorAtHead() const
+{
+    check();
+    return cursorp_ == &head_;
+}
+
+bool
+js::gc::ArenaList::isCursorAtEnd() const
+{
+    check();
+    return !*cursorp_;
+}
+
+void
+js::gc::ArenaList::moveCursorToEnd()
+{
+    while (!isCursorAtEnd())
+        cursorp_ = &(*cursorp_)->next;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::arenaAfterCursor() const
+{
+    check();
+    return *cursorp_;
+}
+
+js::gc::Arena*
+js::gc::ArenaList::takeNextArena()
+{
+    check();
+    Arena* arena = *cursorp_;
+    if (!arena)
+        return nullptr;
+    cursorp_ = &arena->next;
+    check();
+    return arena;
+}
+
+void
+js::gc::ArenaList::insertAtCursor(Arena* a)
+{
+    check();
+    a->next = *cursorp_;
+    *cursorp_ = a;
+    // At this point, the cursor is sitting before |a|. Move it after |a|
+    // if necessary.
+    if (!a->hasFreeThings())
+        cursorp_ = &a->next;
+    check();
+}
+
+void
+js::gc::ArenaList::insertBeforeCursor(Arena* a)
+{
+    check();
+    a->next = *cursorp_;
+    *cursorp_ = a;
+    cursorp_ = &a->next;
+    check();
+}
+
+js::gc::ArenaList&
+js::gc::ArenaList::insertListWithCursorAtEnd(const ArenaList& other)
+{
+    check();
+    other.check();
+    MOZ_ASSERT(other.isCursorAtEnd());
+    if (other.isCursorAtHead())
+        return *this;
+    // Insert the full arenas of |other| after those of |this|.
+    *other.cursorp_ = *cursorp_;
+    *cursorp_ = other.head_;
+    cursorp_ = other.cursorp_;
+    check();
+    return *this;
+}
+
+js::gc::SortedArenaList::SortedArenaList(size_t thingsPerArena)
+{
+    reset(thingsPerArena);
+}
+
+void
+js::gc::SortedArenaList::setThingsPerArena(size_t thingsPerArena)
+{
+    MOZ_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
+    thingsPerArena_ = thingsPerArena;
+}
+
+void
+js::gc::SortedArenaList::reset(size_t thingsPerArena)
+{
+    setThingsPerArena(thingsPerArena);
+    // Initialize the segments.
+    for (size_t i = 0; i <= thingsPerArena; ++i)
+        segments[i].clear();
+}
+
+void
+js::gc::SortedArenaList::insertAt(Arena* arena, size_t nfree)
+{
+    MOZ_ASSERT(nfree <= thingsPerArena_);
+    segments[nfree].append(arena);
+}
+
+void
+js::gc::SortedArenaList::extractEmpty(Arena** empty)
+{
+    SortedArenaListSegment& segment = segments[thingsPerArena_];
+    if (segment.head) {
+        *segment.tailp = *empty;
+        *empty = segment.head;
+        segment.clear();
+    }
+}
+
+js::gc::ArenaList
+js::gc::SortedArenaList::toArenaList()
+{
+    // Link the non-empty segment tails up to the non-empty segment heads.
+    size_t tailIndex = 0;
+    for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
+        if (headAt(headIndex)) {
+            segments[tailIndex].linkTo(headAt(headIndex));
+            tailIndex = headIndex;
+        }
+    }
+    // Point the tail of the final non-empty segment at null. Note that if
+    // the list is empty, this will just set segments[0].head to null.
+    segments[tailIndex].linkTo(nullptr);
+    // Create an ArenaList with head and cursor set to the head and tail of
+    // the first segment (if that segment is empty, only the head is used).
+    return ArenaList(segments[0]);
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstArena(AllocKind thingKind) const
+{
+    return arenaLists(thingKind).head();
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstArenaToSweep(AllocKind thingKind) const
+{
+    return arenaListsToSweep(thingKind);
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getFirstSweptArena(AllocKind thingKind) const
+{
+    if (thingKind != incrementalSweptArenaKind.ref())
+        return nullptr;
+    return incrementalSweptArenas.ref().head();
+}
+
+js::gc::Arena*
+js::gc::ArenaLists::getArenaAfterCursor(AllocKind thingKind) const
+{
+    return arenaLists(thingKind).arenaAfterCursor();
+}
+
+bool
+js::gc::ArenaLists::arenaListsAreEmpty() const
+{
+    for (auto i : AllAllocKinds()) {
+        /*
+         * The arena cannot be empty if the background finalization is not yet
+         * done.
+         */
+        if (backgroundFinalizeState(i) != BFS_DONE)
+            return false;
+        if (!arenaLists(i).isEmpty())
+            return false;
+    }
+    return true;
+}
+
+void
+js::gc::ArenaLists::unmarkAll()
+{
+    for (auto i : AllAllocKinds()) {
+        /* The background finalization must have stopped at this point. */
+        MOZ_ASSERT(backgroundFinalizeState(i) == BFS_DONE);
+        for (Arena* arena = arenaLists(i).head(); arena; arena = arena->next)
+            arena->unmarkAll();
+    }
+}
+
+bool
+js::gc::ArenaLists::doneBackgroundFinalize(AllocKind kind) const
+{
+    return backgroundFinalizeState(kind) == BFS_DONE;
+}
+
+bool
+js::gc::ArenaLists::needBackgroundFinalizeWait(AllocKind kind) const
+{
+    return backgroundFinalizeState(kind) != BFS_DONE;
+}
+
+void
+js::gc::ArenaLists::purge()
+{
+    for (auto i : AllAllocKinds())
+        freeLists(i) = &placeholder;
+}
+
+bool
+js::gc::ArenaLists::arenaIsInUse(Arena* arena, AllocKind kind) const
+{
+    MOZ_ASSERT(arena);
+    return arena == freeLists(kind)->getArenaUnchecked();
+}
+
+MOZ_ALWAYS_INLINE js::gc::TenuredCell*
+js::gc::ArenaLists::allocateFromFreeList(AllocKind thingKind, size_t thingSize)
+{
+    return freeLists(thingKind)->allocate(thingSize);
+}
+
+void
+js::gc::ArenaLists::checkEmptyFreeLists()
+{
+#ifdef DEBUG
+    for (auto i : AllAllocKinds())
+        checkEmptyFreeList(i);
+#endif
+}
+
+bool
+js::gc::ArenaLists::checkEmptyArenaLists()
+{
+    bool empty = true;
+#ifdef DEBUG
+    for (auto i : AllAllocKinds()) {
+        if (!checkEmptyArenaList(i))
+            empty = false;
+    }
+#endif
+    return empty;
+}
+
+void
+js::gc::ArenaLists::checkEmptyFreeList(AllocKind kind)
+{
+    MOZ_ASSERT(freeLists(kind)->isEmpty());
+}
+
+#endif // gc_ArenaList_inl_h
--- a/js/src/gc/ArenaList.h
+++ b/js/src/gc/ArenaList.h
@@ -1,35 +1,48 @@
 /* -*- 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/. */
 
+/*
+ * GC-internal definitions of ArenaList and associated heap data structures.
+ */
+
 #ifndef gc_ArenaList_h
 #define gc_ArenaList_h
 
-#include "gc/Heap.h"
+#include "gc/AllocKind.h"
+#include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "threading/ProtectedData.h"
 
 namespace JS {
 
 struct Zone;
 
 } /* namespace JS */
 
 namespace js {
 
+class FreeOp;
 class Nursery;
 class TenuringTracer;
 
+namespace gcstats {
+struct Statistics;
+}
+
 namespace gc {
 
+class Arena;
 struct FinalizePhase;
+class FreeSpan;
+class TenuredCell;
 
 /*
  * A single segment of a SortedArenaList. Each segment has a head and a tail,
  * which track the start and end of a segment for O(1) append and concatenation.
  */
 struct SortedArenaListSegment
 {
     Arena* head;
@@ -40,22 +53,17 @@ struct SortedArenaListSegment
         tailp = &head;
     }
 
     bool isEmpty() const {
         return tailp == &head;
     }
 
     // Appends |arena| to this segment.
-    void append(Arena* arena) {
-        MOZ_ASSERT(arena);
-        MOZ_ASSERT_IF(head, head->getAllocKind() == arena->getAllocKind());
-        *tailp = arena;
-        tailp = &arena->next;
-    }
+    inline void append(Arena* arena);
 
     // Points the tail of this segment at |arena|, which may be null. Note
     // that this does not change the tail itself, but merely which arena
     // follows it. This essentially turns the tail into a cursor (see also the
     // description of ArenaList), but from the perspective of a SortedArenaList
     // this makes no difference.
     void linkTo(Arena* arena) {
         *tailp = arena;
@@ -96,148 +104,57 @@ class ArenaList {
     //     cursor, and therefore |*cursorp_| points to the arena following the
     //     cursor.
     //
     // |cursorp_| is never null.
     //
     Arena* head_;
     Arena** cursorp_;
 
-    void copy(const ArenaList& other) {
-        other.check();
-        head_ = other.head_;
-        cursorp_ = other.isCursorAtHead() ? &head_ : other.cursorp_;
-        check();
-    }
+    inline void copy(const ArenaList& other);
 
   public:
-    ArenaList() {
-        clear();
-    }
-
-    ArenaList(const ArenaList& other) {
-        copy(other);
-    }
+    inline ArenaList();
+    inline ArenaList(const ArenaList& other);
 
-    ArenaList& operator=(const ArenaList& other) {
-        copy(other);
-        return *this;
-    }
-
-    explicit ArenaList(const SortedArenaListSegment& segment) {
-        head_ = segment.head;
-        cursorp_ = segment.isEmpty() ? &head_ : segment.tailp;
-        check();
-    }
+    inline ArenaList& operator=(const ArenaList& other);
 
-    // This does checking just of |head_| and |cursorp_|.
-    void check() const {
-#ifdef DEBUG
-        // If the list is empty, it must have this form.
-        MOZ_ASSERT_IF(!head_, cursorp_ == &head_);
+    inline explicit ArenaList(const SortedArenaListSegment& segment);
 
-        // If there's an arena following the cursor, it must not be full.
-        Arena* cursor = *cursorp_;
-        MOZ_ASSERT_IF(cursor, cursor->hasFreeThings());
-#endif
-    }
+    inline void check() const;
 
-    void clear() {
-        head_ = nullptr;
-        cursorp_ = &head_;
-        check();
-    }
-
-    ArenaList copyAndClear() {
-        ArenaList result = *this;
-        clear();
-        return result;
-    }
-
-    bool isEmpty() const {
-        check();
-        return !head_;
-    }
+    inline void clear();
+    inline ArenaList copyAndClear();
+    inline bool isEmpty() const;
 
     // This returns nullptr if the list is empty.
-    Arena* head() const {
-        check();
-        return head_;
-    }
-
-    bool isCursorAtHead() const {
-        check();
-        return cursorp_ == &head_;
-    }
+    inline Arena* head() const;
 
-    bool isCursorAtEnd() const {
-        check();
-        return !*cursorp_;
-    }
+    inline bool isCursorAtHead() const;
+    inline bool isCursorAtEnd() const;
 
-    void moveCursorToEnd() {
-        while (!isCursorAtEnd())
-            cursorp_ = &(*cursorp_)->next;
-    }
+    inline void moveCursorToEnd();
 
     // This can return nullptr.
-    Arena* arenaAfterCursor() const {
-        check();
-        return *cursorp_;
-    }
+    inline Arena* arenaAfterCursor() const;
 
     // This returns the arena after the cursor and moves the cursor past it.
-    Arena* takeNextArena() {
-        check();
-        Arena* arena = *cursorp_;
-        if (!arena)
-            return nullptr;
-        cursorp_ = &arena->next;
-        check();
-        return arena;
-    }
+    inline Arena* takeNextArena();
 
     // This does two things.
     // - Inserts |a| at the cursor.
     // - Leaves the cursor sitting just before |a|, if |a| is not full, or just
     //   after |a|, if |a| is full.
-    void insertAtCursor(Arena* a) {
-        check();
-        a->next = *cursorp_;
-        *cursorp_ = a;
-        // At this point, the cursor is sitting before |a|. Move it after |a|
-        // if necessary.
-        if (!a->hasFreeThings())
-            cursorp_ = &a->next;
-        check();
-    }
+    inline void insertAtCursor(Arena* a);
 
     // Inserts |a| at the cursor, then moves the cursor past it.
-    void insertBeforeCursor(Arena* a) {
-        check();
-        a->next = *cursorp_;
-        *cursorp_ = a;
-        cursorp_ = &a->next;
-        check();
-    }
+    inline void insertBeforeCursor(Arena* a);
 
     // This inserts |other|, which must be full, at the cursor of |this|.
-    ArenaList& insertListWithCursorAtEnd(const ArenaList& other) {
-        check();
-        other.check();
-        MOZ_ASSERT(other.isCursorAtEnd());
-        if (other.isCursorAtHead())
-            return *this;
-        // Insert the full arenas of |other| after those of |this|.
-        *other.cursorp_ = *cursorp_;
-        *cursorp_ = other.head_;
-        cursorp_ = other.cursorp_;
-        check();
-        return *this;
-    }
+    inline ArenaList& insertListWithCursorAtEnd(const ArenaList& other);
 
     Arena* removeRemainingArenas(Arena** arenap);
     Arena** pickArenasToRelocate(size_t& arenaTotalOut, size_t& relocTotalOut);
     Arena* relocateArenas(Arena* toRelocate, Arena* relocated,
                           js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
 };
 
 /*
@@ -264,72 +181,37 @@ class SortedArenaList
     size_t thingsPerArena_;
     SortedArenaListSegment segments[MaxThingsPerArena + 1];
 
     // Convenience functions to get the nth head and tail.
     Arena* headAt(size_t n) { return segments[n].head; }
     Arena** tailAt(size_t n) { return segments[n].tailp; }
 
   public:
-    explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena) {
-        reset(thingsPerArena);
-    }
+    inline explicit SortedArenaList(size_t thingsPerArena = MaxThingsPerArena);
 
-    void setThingsPerArena(size_t thingsPerArena) {
-        MOZ_ASSERT(thingsPerArena && thingsPerArena <= MaxThingsPerArena);
-        thingsPerArena_ = thingsPerArena;
-    }
+    inline void setThingsPerArena(size_t thingsPerArena);
 
     // Resets the first |thingsPerArena| segments of this list for further use.
-    void reset(size_t thingsPerArena = MaxThingsPerArena) {
-        setThingsPerArena(thingsPerArena);
-        // Initialize the segments.
-        for (size_t i = 0; i <= thingsPerArena; ++i)
-            segments[i].clear();
-    }
+    inline void reset(size_t thingsPerArena = MaxThingsPerArena);
 
     // Inserts an arena, which has room for |nfree| more things, in its segment.
-    void insertAt(Arena* arena, size_t nfree) {
-        MOZ_ASSERT(nfree <= thingsPerArena_);
-        segments[nfree].append(arena);
-    }
+    inline void insertAt(Arena* arena, size_t nfree);
 
     // Remove all empty arenas, inserting them as a linked list.
-    void extractEmpty(Arena** empty) {
-        SortedArenaListSegment& segment = segments[thingsPerArena_];
-        if (segment.head) {
-            *segment.tailp = *empty;
-            *empty = segment.head;
-            segment.clear();
-        }
-    }
+    inline void extractEmpty(Arena** empty);
 
     // Links up the tail of each non-empty segment to the head of the next
     // non-empty segment, creating a contiguous list that is returned as an
     // ArenaList. This is not a destructive operation: neither the head nor tail
     // of any segment is modified. However, note that the Arenas in the
     // resulting ArenaList should be treated as read-only unless the
     // SortedArenaList is no longer needed: inserting or removing arenas would
     // invalidate the SortedArenaList.
-    ArenaList toArenaList() {
-        // Link the non-empty segment tails up to the non-empty segment heads.
-        size_t tailIndex = 0;
-        for (size_t headIndex = 1; headIndex <= thingsPerArena_; ++headIndex) {
-            if (headAt(headIndex)) {
-                segments[tailIndex].linkTo(headAt(headIndex));
-                tailIndex = headIndex;
-            }
-        }
-        // Point the tail of the final non-empty segment at null. Note that if
-        // the list is empty, this will just set segments[0].head to null.
-        segments[tailIndex].linkTo(nullptr);
-        // Create an ArenaList with head and cursor set to the head and tail of
-        // the first segment (if that segment is empty, only the head is used).
-        return ArenaList(segments[0]);
-    }
+    inline ArenaList toArenaList();
 };
 
 enum class ShouldCheckThresholds
 {
     DontCheckThresholds = 0,
     CheckThresholds = 1
 };
 
@@ -396,114 +278,47 @@ class ArenaLists
   public:
     explicit ArenaLists(JSRuntime* rt, ZoneGroup* group);
     ~ArenaLists();
 
     const void* addressOfFreeList(AllocKind thingKind) const {
         return reinterpret_cast<const void*>(&freeLists_.refNoCheck()[thingKind]);
     }
 
-    Arena* getFirstArena(AllocKind thingKind) const {
-        return arenaLists(thingKind).head();
-    }
-
-    Arena* getFirstArenaToSweep(AllocKind thingKind) const {
-        return arenaListsToSweep(thingKind);
-    }
+    inline Arena* getFirstArena(AllocKind thingKind) const;
+    inline Arena* getFirstArenaToSweep(AllocKind thingKind) const;
+    inline Arena* getFirstSweptArena(AllocKind thingKind) const;
+    inline Arena* getArenaAfterCursor(AllocKind thingKind) const;
 
-    Arena* getFirstSweptArena(AllocKind thingKind) const {
-        if (thingKind != incrementalSweptArenaKind.ref())
-            return nullptr;
-        return incrementalSweptArenas.ref().head();
-    }
-
-    Arena* getArenaAfterCursor(AllocKind thingKind) const {
-        return arenaLists(thingKind).arenaAfterCursor();
-    }
+    inline bool arenaListsAreEmpty() const;
 
-    bool arenaListsAreEmpty() const {
-        for (auto i : AllAllocKinds()) {
-            /*
-             * The arena cannot be empty if the background finalization is not yet
-             * done.
-             */
-            if (backgroundFinalizeState(i) != BFS_DONE)
-                return false;
-            if (!arenaLists(i).isEmpty())
-                return false;
-        }
-        return true;
-    }
+    inline void unmarkAll();
 
-    void unmarkAll() {
-        for (auto i : AllAllocKinds()) {
-            /* The background finalization must have stopped at this point. */
-            MOZ_ASSERT(backgroundFinalizeState(i) == BFS_DONE);
-            for (Arena* arena = arenaLists(i).head(); arena; arena = arena->next)
-                arena->unmarkAll();
-        }
-    }
+    inline bool doneBackgroundFinalize(AllocKind kind) const;
+    inline bool needBackgroundFinalizeWait(AllocKind kind) const;
 
-    bool doneBackgroundFinalize(AllocKind kind) const {
-        return backgroundFinalizeState(kind) == BFS_DONE;
-    }
-
-    bool needBackgroundFinalizeWait(AllocKind kind) const {
-        return backgroundFinalizeState(kind) != BFS_DONE;
-    }
-
-    /*
-     * Clear the free lists so we won't try to allocate from swept arenas.
-     */
-    void purge() {
-        for (auto i : AllAllocKinds())
-            freeLists(i) = &placeholder;
-    }
+    /* Clear the free lists so we won't try to allocate from swept arenas. */
+    inline void purge();
 
     inline void prepareForIncrementalGC();
 
     /* Check if this arena is in use. */
-    bool arenaIsInUse(Arena* arena, AllocKind kind) const {
-        MOZ_ASSERT(arena);
-        return arena == freeLists(kind)->getArenaUnchecked();
-    }
+    inline bool arenaIsInUse(Arena* arena, AllocKind kind) const;
 
-    MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind, size_t thingSize) {
-        return freeLists(thingKind)->allocate(thingSize);
-    }
+    MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind, size_t thingSize);
 
-    /*
-     * Moves all arenas from |fromArenaLists| into |this|.
-     */
+    /* Moves all arenas from |fromArenaLists| into |this|. */
     void adoptArenas(JSRuntime* runtime, ArenaLists* fromArenaLists, bool targetZoneIsCollecting);
 
     /* True if the Arena in question is found in this ArenaLists */
     bool containsArena(JSRuntime* runtime, Arena* arena);
 
-    void checkEmptyFreeLists() {
-#ifdef DEBUG
-        for (auto i : AllAllocKinds())
-            checkEmptyFreeList(i);
-#endif
-    }
-
-    bool checkEmptyArenaLists() {
-        bool empty = true;
-#ifdef DEBUG
-        for (auto i : AllAllocKinds()) {
-            if (!checkEmptyArenaList(i))
-                empty = false;
-        }
-#endif
-        return empty;
-    }
-
-    void checkEmptyFreeList(AllocKind kind) {
-        MOZ_ASSERT(freeLists(kind)->isEmpty());
-    }
+    inline void checkEmptyFreeLists();
+    inline bool checkEmptyArenaLists();
+    inline void checkEmptyFreeList(AllocKind kind);
 
     bool checkEmptyArenaList(AllocKind kind);
 
     bool relocateArenas(JS::Zone* zone, Arena*& relocatedListOut, JS::gcreason::Reason reason,
                         js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
 
     void queueForegroundObjectsForSweep(FreeOp* fop);
     void queueForegroundThingsForSweep(FreeOp* fop);
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -35,16 +35,17 @@ RuntimeFromActiveCooperatingThreadIsHeap
 extern bool
 CurrentThreadIsIonCompiling();
 #endif
 
 extern void
 TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name);
 
 namespace gc {
+
 class Arena;
 enum class AllocKind;
 struct Chunk;
 class TenuredCell;
 
 // A GC cell is the base class for all GC things.
 struct Cell
 {
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -1,25 +1,87 @@
 /* -*- 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/. */
 
+/*
+ * GC-internal enum definitions.
+ */
+
 #ifndef gc_GCEnum_h
 #define gc_GCEnum_h
 
 #include <stdint.h>
 
 namespace js {
 namespace gc {
 
-/* Mark colors to pass to markIfUnmarked. */
+// Mark colors to pass to markIfUnmarked.
 enum class MarkColor : uint32_t
 {
     Black = 0,
     Gray
 };
 
+// The phases of an incremental GC.
+#define GCSTATES(D) \
+    D(NotActive) \
+    D(MarkRoots) \
+    D(Mark) \
+    D(Sweep) \
+    D(Finalize) \
+    D(Compact) \
+    D(Decommit)
+enum class State {
+#define MAKE_STATE(name) name,
+    GCSTATES(MAKE_STATE)
+#undef MAKE_STATE
+};
+
+// Reasons we reset an ongoing incremental GC or perform a non-incremental GC.
+#define GC_ABORT_REASONS(D) \
+    D(None) \
+    D(NonIncrementalRequested) \
+    D(AbortRequested) \
+    D(Unused1) \
+    D(IncrementalDisabled) \
+    D(ModeChange) \
+    D(MallocBytesTrigger) \
+    D(GCBytesTrigger) \
+    D(ZoneChange) \
+    D(CompartmentRevived)
+enum class AbortReason {
+#define MAKE_REASON(name) name,
+    GC_ABORT_REASONS(MAKE_REASON)
+#undef MAKE_REASON
+};
+
+#define JS_FOR_EACH_ZEAL_MODE(D)       \
+    D(RootsChange, 1)                  \
+    D(Alloc, 2)                        \
+    D(FrameGC, 3)                      \
+    D(VerifierPre, 4)                  \
+    D(FrameVerifierPre, 5)             \
+    D(GenerationalGC, 7)               \
+    D(IncrementalRootsThenFinish, 8)   \
+    D(IncrementalMarkAllThenFinish, 9) \
+    D(IncrementalMultipleSlices, 10)   \
+    D(IncrementalMarkingValidator, 11) \
+    D(ElementsBarrier, 12)             \
+    D(CheckHashTablesOnMinorGC, 13)    \
+    D(Compact, 14)                     \
+    D(CheckHeapAfterGC, 15)            \
+    D(CheckNursery, 16)                \
+    D(IncrementalSweepThenFinish, 17)
+
+enum class ZealMode {
+#define ZEAL_MODE(name, value) name = value,
+    JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
+#undef ZEAL_MODE
+    Limit = 17
+};
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_GCEnum_h */
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -1,23 +1,28 @@
 /* -*- 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/. */
 
+/*
+ * GC-internal definitions.
+ */
+
 #ifndef gc_GCInternals_h
 #define gc_GCInternals_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 
+#include "gc/RelocationOverlay.h"
 #include "gc/Zone.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 
 namespace js {
 namespace gc {
 
 void FinishGC(JSContext* cx);
@@ -229,12 +234,36 @@ class MOZ_RAII AutoAssertEmptyNursery
  * waiting on jonco's review.
  */
 class MOZ_RAII AutoEmptyNursery : public AutoAssertEmptyNursery
 {
   public:
     explicit AutoEmptyNursery(JSContext* cx);
 };
 
+extern void
+DelayCrossCompartmentGrayMarking(JSObject* src);
+
+inline bool
+IsOOMReason(JS::gcreason::Reason reason)
+{
+    return reason == JS::gcreason::LAST_DITCH ||
+           reason == JS::gcreason::MEM_PRESSURE;
+}
+
+inline void
+RelocationOverlay::forwardTo(Cell* cell)
+{
+    MOZ_ASSERT(!isForwarded());
+    // The location of magic_ is important because it must never be valid to see
+    // the value Relocated there in a GC thing that has not been moved.
+    static_assert(offsetof(RelocationOverlay, magic_) == offsetof(JSObject, group_) &&
+                  offsetof(RelocationOverlay, magic_) == offsetof(js::Shape, base_) &&
+                  offsetof(RelocationOverlay, magic_) == offsetof(JSString, d.u1.flags),
+                  "RelocationOverlay::magic_ is in the wrong location");
+    magic_ = Relocated;
+    newLocation_ = cell;
+}
+
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_GCInternals_h */
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -6,27 +6,27 @@
 
 #ifndef gc_GCRuntime_h
 #define gc_GCRuntime_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/Maybe.h"
 
-#include "jsfriendapi.h"
+#include "jsapi.h"
+#include "jsatom.h"
 
 #include "gc/ArenaList.h"
 #include "gc/AtomMarking.h"
 #include "gc/GCHelperState.h"
 #include "gc/GCMarker.h"
 #include "gc/GCParallelTask.h"
 #include "gc/Nursery.h"
 #include "gc/Statistics.h"
 #include "gc/StoreBuffer.h"
-#include "gc/Tracer.h"
 #include "js/GCAnnotations.h"
 #include "js/UniquePtr.h"
 
 namespace js {
 
 class AutoLockGC;
 class AutoLockGCBgAlloc;
 class AutoLockHelperThreadState;
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -6,29 +6,28 @@
 
 #ifndef gc_Heap_h
 #define gc_Heap_h
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/EnumeratedArray.h"
-#include "mozilla/EnumeratedRange.h"
 #include "mozilla/PodOperations.h"
 
 #include <stddef.h>
 #include <stdint.h>
 
 #include "jsfriendapi.h"
 #include "jspubtd.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/BitArray.h"
+#include "gc/AllocKind.h"
 #include "gc/GCEnum.h"
 #include "gc/Memory.h"
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "js/RootingAPI.h"
 #include "js/TracingAPI.h"
 
 #include "vm/Printer.h"
@@ -55,185 +54,16 @@ struct Chunk;
  * estimated lifetime or lifetime requirements of objects allocated from that
  * site.
  */
 enum InitialHeap : uint8_t {
     DefaultHeap,
     TenuredHeap
 };
 
-/* The GC allocation kinds. */
-// FIXME: uint8_t would make more sense for the underlying type, but causes
-// miscompilations in GCC (fixed in 4.8.5 and 4.9.3). See also bug 1143966.
-enum class AllocKind {
-    FIRST,
-    OBJECT_FIRST = FIRST,
-    FUNCTION = FIRST,
-    FUNCTION_EXTENDED,
-    OBJECT0,
-    OBJECT0_BACKGROUND,
-    OBJECT2,
-    OBJECT2_BACKGROUND,
-    OBJECT4,
-    OBJECT4_BACKGROUND,
-    OBJECT8,
-    OBJECT8_BACKGROUND,
-    OBJECT12,
-    OBJECT12_BACKGROUND,
-    OBJECT16,
-    OBJECT16_BACKGROUND,
-    OBJECT_LIMIT,
-    OBJECT_LAST = OBJECT_LIMIT - 1,
-    SCRIPT,
-    LAZY_SCRIPT,
-    SHAPE,
-    ACCESSOR_SHAPE,
-    BASE_SHAPE,
-    OBJECT_GROUP,
-    FAT_INLINE_STRING,
-    STRING,
-    EXTERNAL_STRING,
-    FAT_INLINE_ATOM,
-    ATOM,
-    SYMBOL,
-    JITCODE,
-    SCOPE,
-    REGEXP_SHARED,
-    LIMIT,
-    LAST = LIMIT - 1
-};
-
-// Macro to enumerate the different allocation kinds supplying information about
-// the trace kind, C++ type and allocation size.
-#define FOR_EACH_OBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind      TypeName           SizedType */ \
-    D(FUNCTION,            Object,        JSObject,          JSFunction) \
-    D(FUNCTION_EXTENDED,   Object,        JSObject,          FunctionExtended) \
-    D(OBJECT0,             Object,        JSObject,          JSObject_Slots0) \
-    D(OBJECT0_BACKGROUND,  Object,        JSObject,          JSObject_Slots0) \
-    D(OBJECT2,             Object,        JSObject,          JSObject_Slots2) \
-    D(OBJECT2_BACKGROUND,  Object,        JSObject,          JSObject_Slots2) \
-    D(OBJECT4,             Object,        JSObject,          JSObject_Slots4) \
-    D(OBJECT4_BACKGROUND,  Object,        JSObject,          JSObject_Slots4) \
-    D(OBJECT8,             Object,        JSObject,          JSObject_Slots8) \
-    D(OBJECT8_BACKGROUND,  Object,        JSObject,          JSObject_Slots8) \
-    D(OBJECT12,            Object,        JSObject,          JSObject_Slots12) \
-    D(OBJECT12_BACKGROUND, Object,        JSObject,          JSObject_Slots12) \
-    D(OBJECT16,            Object,        JSObject,          JSObject_Slots16) \
-    D(OBJECT16_BACKGROUND, Object,        JSObject,          JSObject_Slots16)
-
-#define FOR_EACH_NONOBJECT_ALLOCKIND(D) \
- /* AllocKind              TraceKind      TypeName           SizedType */ \
-    D(SCRIPT,              Script,        JSScript,          JSScript) \
-    D(LAZY_SCRIPT,         LazyScript,    js::LazyScript,    js::LazyScript) \
-    D(SHAPE,               Shape,         js::Shape,         js::Shape) \
-    D(ACCESSOR_SHAPE,      Shape,         js::AccessorShape, js::AccessorShape) \
-    D(BASE_SHAPE,          BaseShape,     js::BaseShape,     js::BaseShape) \
-    D(OBJECT_GROUP,        ObjectGroup,   js::ObjectGroup,   js::ObjectGroup) \
-    D(FAT_INLINE_STRING,   String,        JSFatInlineString, JSFatInlineString) \
-    D(STRING,              String,        JSString,          JSString) \
-    D(EXTERNAL_STRING,     String,        JSExternalString,  JSExternalString) \
-    D(FAT_INLINE_ATOM,     String,        js::FatInlineAtom, js::FatInlineAtom) \
-    D(ATOM,                String,        js::NormalAtom,    js::NormalAtom) \
-    D(SYMBOL,              Symbol,        JS::Symbol,        JS::Symbol) \
-    D(JITCODE,             JitCode,       js::jit::JitCode,  js::jit::JitCode) \
-    D(SCOPE,               Scope,         js::Scope,         js::Scope) \
-    D(REGEXP_SHARED,       RegExpShared,  js::RegExpShared,  js::RegExpShared)
-
-#define FOR_EACH_ALLOCKIND(D) \
-    FOR_EACH_OBJECT_ALLOCKIND(D) \
-    FOR_EACH_NONOBJECT_ALLOCKIND(D)
-
-static_assert(int(AllocKind::FIRST) == 0, "Various places depend on AllocKind starting at 0, "
-                                          "please audit them carefully!");
-static_assert(int(AllocKind::OBJECT_FIRST) == 0, "Various places depend on AllocKind::OBJECT_FIRST "
-                                                 "being 0, please audit them carefully!");
-
-inline bool
-IsAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::FIRST && kind <= AllocKind::LIMIT;
-}
-
-inline bool
-IsValidAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::FIRST && kind <= AllocKind::LAST;
-}
-
-inline bool
-IsObjectAllocKind(AllocKind kind)
-{
-    return kind >= AllocKind::OBJECT_FIRST && kind <= AllocKind::OBJECT_LAST;
-}
-
-inline bool
-IsShapeAllocKind(AllocKind kind)
-{
-    return kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE;
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over all alloc kinds.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
-AllAllocKinds()
-{
-    return mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT);
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over all object alloc kinds.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT))
-ObjectAllocKinds()
-{
-    return mozilla::MakeEnumeratedRange(AllocKind::OBJECT_FIRST, AllocKind::OBJECT_LIMIT);
-}
-
-// Returns a sequence for use in a range-based for loop,
-// to iterate over alloc kinds from |first| to |limit|, exclusive.
-inline decltype(mozilla::MakeEnumeratedRange(AllocKind::FIRST, AllocKind::LIMIT))
-SomeAllocKinds(AllocKind first = AllocKind::FIRST, AllocKind limit = AllocKind::LIMIT)
-{
-    MOZ_ASSERT(IsAllocKind(first), "|first| is not a valid AllocKind!");
-    MOZ_ASSERT(IsAllocKind(limit), "|limit| is not a valid AllocKind!");
-    return mozilla::MakeEnumeratedRange(first, limit);
-}
-
-// AllAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
-// with each index corresponding to a particular alloc kind.
-template<typename ValueType> using AllAllocKindArray =
-    mozilla::EnumeratedArray<AllocKind, AllocKind::LIMIT, ValueType>;
-
-// ObjectAllocKindArray<ValueType> gives an enumerated array of ValueTypes,
-// with each index corresponding to a particular object alloc kind.
-template<typename ValueType> using ObjectAllocKindArray =
-    mozilla::EnumeratedArray<AllocKind, AllocKind::OBJECT_LIMIT, ValueType>;
-
-static inline JS::TraceKind
-MapAllocToTraceKind(AllocKind kind)
-{
-    static const JS::TraceKind map[] = {
-#define EXPAND_ELEMENT(allocKind, traceKind, type, sizedType) \
-        JS::TraceKind::traceKind,
-FOR_EACH_ALLOCKIND(EXPAND_ELEMENT)
-#undef EXPAND_ELEMENT
-    };
-
-    static_assert(MOZ_ARRAY_LENGTH(map) == size_t(AllocKind::LIMIT),
-                  "AllocKind-to-TraceKind mapping must be in sync");
-    return map[size_t(kind)];
-}
-
-/*
- * This must be an upper bound, but we do not need the least upper bound, so
- * we just exclude non-background objects.
- */
-static const size_t MAX_BACKGROUND_FINALIZE_KINDS =
-    size_t(AllocKind::LIMIT) - size_t(AllocKind::OBJECT_LIMIT) / 2;
-
 /* Cells are aligned to CellAlignShift, so the largest tagged null pointer is: */
 const uintptr_t LargestTaggedNullCellPointer = (1 << CellAlignShift) - 1;
 
 /*
  * The minimum cell size ends up as twice the cell alignment because the mark
  * bitmap contains one bit per CellBytesPerMarkBit bytes (which is equal to
  * CellAlignBytes) and we need two mark bits per cell.
  */
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Iteration-inl.h
@@ -0,0 +1,121 @@
+/* -*- 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/. */
+
+/*
+ * GC-internal iterators for various data structures.
+ */
+
+#ifndef gc_Iteration_inl_h
+#define gc_Iteration_inl_h
+
+#include "jsgcinlines.h"
+
+namespace js {
+namespace gc {
+
+class ArenaCellIterUnderGC : public ArenaCellIterImpl
+{
+  public:
+    explicit ArenaCellIterUnderGC(Arena* arena)
+      : ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
+    {
+        MOZ_ASSERT(CurrentThreadIsPerformingGC());
+    }
+};
+
+class ArenaCellIterUnderFinalize : public ArenaCellIterImpl
+{
+  public:
+    explicit ArenaCellIterUnderFinalize(Arena* arena)
+      : ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
+    {
+        MOZ_ASSERT(CurrentThreadIsGCSweeping());
+    }
+};
+
+class ArenaCellIterUnbarriered : public ArenaCellIterImpl
+{
+  public:
+    explicit ArenaCellIterUnbarriered(Arena* arena)
+      : ArenaCellIterImpl(arena, CellIterDoesntNeedBarrier)
+    {}
+};
+
+class GrayObjectIter : public ZoneCellIter<js::gc::TenuredCell> {
+  public:
+    explicit GrayObjectIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<js::gc::TenuredCell>() {
+        initForTenuredIteration(zone, kind);
+    }
+
+    JSObject* get() const { return ZoneCellIter<js::gc::TenuredCell>::get<JSObject>(); }
+    operator JSObject*() const { return get(); }
+    JSObject* operator ->() const { return get(); }
+};
+
+class GCZonesIter
+{
+  private:
+    ZonesIter zone;
+
+  public:
+    explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms) : zone(rt, selector) {
+        MOZ_ASSERT(JS::CurrentThreadIsHeapBusy());
+        if (!zone->isCollectingFromAnyThread())
+            next();
+    }
+
+    bool done() const { return zone.done(); }
+
+    void next() {
+        MOZ_ASSERT(!done());
+        do {
+            zone.next();
+        } while (!zone.done() && !zone->isCollectingFromAnyThread());
+    }
+
+    JS::Zone* get() const {
+        MOZ_ASSERT(!done());
+        return zone;
+    }
+
+    operator JS::Zone*() const { return get(); }
+    JS::Zone* operator->() const { return get(); }
+};
+
+typedef CompartmentsIterT<GCZonesIter> GCCompartmentsIter;
+
+/* Iterates over all zones in the current sweep group. */
+class SweepGroupZonesIter {
+    JS::Zone* current;
+
+  public:
+    explicit SweepGroupZonesIter(JSRuntime* rt) {
+        MOZ_ASSERT(CurrentThreadIsPerformingGC());
+        current = rt->gc.getCurrentSweepGroup();
+    }
+
+    bool done() const { return !current; }
+
+    void next() {
+        MOZ_ASSERT(!done());
+        current = current->nextNodeInGroup();
+    }
+
+    JS::Zone* get() const {
+        MOZ_ASSERT(!done());
+        return current;
+    }
+
+    operator JS::Zone*() const { return get(); }
+    JS::Zone* operator->() const { return get(); }
+};
+
+typedef CompartmentsIterT<SweepGroupZonesIter> SweepGroupCompartmentsIter;
+
+} // namespace gc
+} // namespace js
+
+#endif // gc_Iteration_h
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -1,14 +1,16 @@
 /* -*- 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 "gc/Iteration-inl.h"
+
 #include "mozilla/DebugOnly.h"
 
 #include "jscompartment.h"
 
 #include "gc/GCInternals.h"
 #include "js/HashTable.h"
 #include "vm/Runtime.h"
 
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Marking-inl.h
@@ -0,0 +1,128 @@
+/* -*- 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_Marking_inl_h
+#define gc_Marking_inl_h
+
+#include "gc/Marking.h"
+
+#include "gc/RelocationOverlay.h"
+
+namespace js {
+namespace gc {
+
+template <typename T>
+struct MightBeForwarded
+{
+    static_assert(mozilla::IsBaseOf<Cell, T>::value,
+                  "T must derive from Cell");
+    static_assert(!mozilla::IsSame<Cell, T>::value && !mozilla::IsSame<TenuredCell, T>::value,
+                  "T must not be Cell or TenuredCell");
+
+    static const bool value = mozilla::IsBaseOf<JSObject, T>::value ||
+                              mozilla::IsBaseOf<Shape, T>::value ||
+                              mozilla::IsBaseOf<BaseShape, T>::value ||
+                              mozilla::IsBaseOf<JSString, T>::value ||
+                              mozilla::IsBaseOf<JSScript, T>::value ||
+                              mozilla::IsBaseOf<js::LazyScript, T>::value ||
+                              mozilla::IsBaseOf<js::Scope, T>::value ||
+                              mozilla::IsBaseOf<js::RegExpShared, T>::value;
+};
+
+template <typename T>
+inline bool
+IsForwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    if (!MightBeForwarded<T>::value) {
+        MOZ_ASSERT(!overlay->isForwarded());
+        return false;
+    }
+
+    return overlay->isForwarded();
+}
+
+struct IsForwardedFunctor : public BoolDefaultAdaptor<Value, false> {
+    template <typename T> bool operator()(T* t) { return IsForwarded(t); }
+};
+
+inline bool
+IsForwarded(const JS::Value& value)
+{
+    return DispatchTyped(IsForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T*
+Forwarded(T* t)
+{
+    RelocationOverlay* overlay = RelocationOverlay::fromCell(t);
+    MOZ_ASSERT(overlay->isForwarded());
+    return reinterpret_cast<T*>(overlay->forwardingAddress());
+}
+
+struct ForwardedFunctor : public IdentityDefaultAdaptor<Value> {
+    template <typename T> inline Value operator()(T* t) {
+        return js::gc::RewrapTaggedPointer<Value, T>::wrap(Forwarded(t));
+    }
+};
+
+inline Value
+Forwarded(const JS::Value& value)
+{
+    return DispatchTyped(ForwardedFunctor(), value);
+}
+
+template <typename T>
+inline T
+MaybeForwarded(T t)
+{
+    if (IsForwarded(t))
+        t = Forwarded(t);
+    MakeAccessibleAfterMovingGC(t);
+    return t;
+}
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+template <typename T>
+inline bool
+IsGCThingValidAfterMovingGC(T* t)
+{
+    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(T* t)
+{
+    if (t)
+        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
+}
+
+template <typename T>
+inline void
+CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
+{
+    CheckGCThingAfterMovingGC(t.unbarrieredGet());
+}
+
+struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
+    template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
+};
+
+inline void
+CheckValueAfterMovingGC(const JS::Value& value)
+{
+    DispatchTyped(CheckValueAfterMovingGCFunctor(), value);
+}
+
+#endif // JSGC_HASH_TABLE_CHECKS
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif // gc_Marking_inl_h
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1,15 +1,15 @@
 /* -*- 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 "gc/Marking.h"
+#include "gc/Marking-inl.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsprf.h"
@@ -29,18 +29,18 @@
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 #include "wasm/WasmJS.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
-#include "jsobjinlines.h"
-
+
+#include "gc/Iteration-inl.h"
 #include "gc/Nursery-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/String-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
@@ -975,25 +975,35 @@ template <typename V, typename S> struct
 
 template <typename S, typename T>
 void
 js::GCMarker::traverseEdge(S source, const T& thing)
 {
     DispatchTyped(TraverseEdgeFunctor<T, S>(), thing, this, source);
 }
 
+namespace {
+
+template <typename T> struct ParticipatesInCC {};
+#define EXPAND_PARTICIPATES_IN_CC(_, type, addToCCKind) \
+    template <> struct ParticipatesInCC<type> { static const bool value = addToCCKind; };
+JS_FOR_EACH_TRACEKIND(EXPAND_PARTICIPATES_IN_CC)
+#undef EXPAND_PARTICIPATES_IN_CC
+
+} // namespace
+
 template <typename T>
 bool
 js::GCMarker::mark(T* thing)
 {
     AssertShouldMarkInZone(thing);
-    MOZ_ASSERT(!IsInsideNursery(gc::TenuredCell::fromPointer(thing)));
-    return gc::ParticipatesInCC<T>::value
-           ? gc::TenuredCell::fromPointer(thing)->markIfUnmarked(markColor())
-           : gc::TenuredCell::fromPointer(thing)->markIfUnmarked(gc::MarkColor::Black);
+    MOZ_ASSERT(!IsInsideNursery(TenuredCell::fromPointer(thing)));
+    return ParticipatesInCC<T>::value
+           ? TenuredCell::fromPointer(thing)->markIfUnmarked(markColor())
+           : TenuredCell::fromPointer(thing)->markIfUnmarked(MarkColor::Black);
 }
 
 
 /*** Inline, Eager GC Marking *********************************************************************/
 
 // Each of the eager, inline marking paths is directly preceeded by the
 // out-of-line, generic tracing code for comparison. Both paths must end up
 // traversing equivalent subgraphs.
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -1,14 +1,19 @@
 /* -*- 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/. */
 
+/*
+ * Marking and sweeping APIs for use by implementations of different GC cell
+ * kinds.
+ */
+
 #ifndef gc_Marking_h
 #define gc_Marking_h
 
 #include "vm/TaggedProto.h"
 
 class JSLinearString;
 class JSRope;
 struct JSRuntime;
@@ -137,11 +142,61 @@ UnmarkGrayShapeRecursively(Shape* shape)
 template<typename T>
 void
 CheckTracedThing(JSTracer* trc, T* thing);
 
 template<typename T>
 void
 CheckTracedThing(JSTracer* trc, T thing);
 
+namespace gc {
+
+// Functions for checking and updating GC thing pointers that might have been
+// moved by compacting GC. Overloads are also provided that work with Values.
+//
+// IsForwarded    - check whether a pointer refers to an GC thing that has been
+//                  moved.
+//
+// Forwarded      - return a pointer to the new location of a GC thing given a
+//                  pointer to old location.
+//
+// MaybeForwarded - used before dereferencing a pointer that may refer to a
+//                  moved GC thing without updating it. For JSObjects this will
+//                  also update the object's shape pointer if it has been moved
+//                  to allow slots to be accessed.
+
+template <typename T>
+inline bool IsForwarded(T* t);
+inline bool IsForwarded(const JS::Value& value);
+
+template <typename T>
+inline T* Forwarded(T* t);
+
+inline Value Forwarded(const JS::Value& value);
+
+template <typename T>
+inline T MaybeForwarded(T t);
+
+inline void
+MakeAccessibleAfterMovingGC(void* anyp) {}
+
+inline void
+MakeAccessibleAfterMovingGC(JSObject* obj); // Defined in jsobjinlines.h.
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+template <typename T>
+inline bool IsGCThingValidAfterMovingGC(T* t);
+
+template <typename T>
+inline void CheckGCThingAfterMovingGC(T* t);
+
+template <typename T>
+inline void CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t);
+
+inline void CheckValueAfterMovingGC(const JS::Value& value);
+
+#endif // JSGC_HASH_TABLE_CHECKS
+
+} /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_Marking_h */
--- a/js/src/gc/Nursery-inl.h
+++ b/js/src/gc/Nursery-inl.h
@@ -8,16 +8,17 @@
 #ifndef gc_Nursery_inl_h
 #define gc_Nursery_inl_h
 
 #include "gc/Nursery.h"
 
 #include "jscntxt.h"
 
 #include "gc/Heap.h"
+#include "gc/RelocationOverlay.h"
 #include "gc/Zone.h"
 #include "js/TracingAPI.h"
 #include "vm/Runtime.h"
 #include "vm/SharedMem.h"
 
 template<typename T>
 bool
 js::Nursery::isInside(const SharedMem<T>& p) const
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -24,18 +24,17 @@
 #if defined(DEBUG)
 #include "vm/EnvironmentObject.h"
 #endif
 #include "vm/JSONPrinter.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/TypeInference.h"
 
-#include "jsobjinlines.h"
-
+#include "gc/Marking-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace gc;
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
new file mode 100644
--- /dev/null
+++ b/js/src/gc/ObjectKind-inl.h
@@ -0,0 +1,174 @@
+/* -*- 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/. */
+
+/*
+ * GC-internal helper functions for getting the AllocKind used to allocate a
+ * JSObject and related information.
+ */
+
+#ifndef gc_ObjectKind_inl_h
+#define gc_ObjectKind_inl_h
+
+#include "vm/NativeObject.h"
+
+namespace js {
+namespace gc {
+
+static inline bool
+CanBeFinalizedInBackground(AllocKind kind, const Class* clasp)
+{
+    MOZ_ASSERT(IsObjectAllocKind(kind));
+    /* If the class has no finalizer or a finalizer that is safe to call on
+     * a different thread, we change the alloc kind. For example,
+     * AllocKind::OBJECT0 calls the finalizer on the active thread,
+     * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
+     * IsBackgroundFinalized is called to prevent recursively incrementing
+     * the alloc kind; kind may already be a background finalize kind.
+     */
+    return (!IsBackgroundFinalized(kind) &&
+            (!clasp->hasFinalize() || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
+}
+
+static inline AllocKind
+GetBackgroundAllocKind(AllocKind kind)
+{
+    MOZ_ASSERT(!IsBackgroundFinalized(kind));
+    MOZ_ASSERT(IsObjectAllocKind(kind));
+    return AllocKind(size_t(kind) + 1);
+}
+
+/* Capacity for slotsToThingKind */
+const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
+
+extern const AllocKind slotsToThingKind[];
+
+/* Get the best kind to use when making an object with the given slot count. */
+static inline AllocKind
+GetGCObjectKind(size_t numSlots)
+{
+    if (numSlots >= SLOTS_TO_THING_KIND_LIMIT)
+        return AllocKind::OBJECT16;
+    return slotsToThingKind[numSlots];
+}
+
+static inline AllocKind
+GetGCObjectKind(const Class* clasp)
+{
+    if (clasp == FunctionClassPtr)
+        return AllocKind::FUNCTION;
+
+    MOZ_ASSERT(!clasp->isProxy(), "Proxies should use GetProxyGCObjectKind");
+
+    uint32_t nslots = JSCLASS_RESERVED_SLOTS(clasp);
+    if (clasp->flags & JSCLASS_HAS_PRIVATE)
+        nslots++;
+    return GetGCObjectKind(nslots);
+}
+
+/* As for GetGCObjectKind, but for dense array allocation. */
+static inline AllocKind
+GetGCArrayKind(size_t numElements)
+{
+    /*
+     * Dense arrays can use their fixed slots to hold their elements array
+     * (less two Values worth of ObjectElements header), but if more than the
+     * maximum number of fixed slots is needed then the fixed slots will be
+     * unused.
+     */
+    JS_STATIC_ASSERT(ObjectElements::VALUES_PER_HEADER == 2);
+    if (numElements > NativeObject::MAX_DENSE_ELEMENTS_COUNT ||
+        numElements + ObjectElements::VALUES_PER_HEADER >= SLOTS_TO_THING_KIND_LIMIT)
+    {
+        return AllocKind::OBJECT2;
+    }
+    return slotsToThingKind[numElements + ObjectElements::VALUES_PER_HEADER];
+}
+
+static inline AllocKind
+GetGCObjectFixedSlotsKind(size_t numFixedSlots)
+{
+    MOZ_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
+    return slotsToThingKind[numFixedSlots];
+}
+
+// Get the best kind to use when allocating an object that needs a specific
+// number of bytes.
+static inline AllocKind
+GetGCObjectKindForBytes(size_t nbytes)
+{
+    MOZ_ASSERT(nbytes <= JSObject::MAX_BYTE_SIZE);
+
+    if (nbytes <= sizeof(NativeObject))
+        return AllocKind::OBJECT0;
+    nbytes -= sizeof(NativeObject);
+
+    size_t dataSlots = AlignBytes(nbytes, sizeof(Value)) / sizeof(Value);
+    MOZ_ASSERT(nbytes <= dataSlots * sizeof(Value));
+    return GetGCObjectKind(dataSlots);
+}
+
+/* Get the number of fixed slots and initial capacity associated with a kind. */
+static inline size_t
+GetGCKindSlots(AllocKind thingKind)
+{
+    /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
+    switch (thingKind) {
+      case AllocKind::FUNCTION:
+      case AllocKind::OBJECT0:
+      case AllocKind::OBJECT0_BACKGROUND:
+        return 0;
+      case AllocKind::FUNCTION_EXTENDED:
+      case AllocKind::OBJECT2:
+      case AllocKind::OBJECT2_BACKGROUND:
+        return 2;
+      case AllocKind::OBJECT4:
+      case AllocKind::OBJECT4_BACKGROUND:
+        return 4;
+      case AllocKind::OBJECT8:
+      case AllocKind::OBJECT8_BACKGROUND:
+        return 8;
+      case AllocKind::OBJECT12:
+      case AllocKind::OBJECT12_BACKGROUND:
+        return 12;
+      case AllocKind::OBJECT16:
+      case AllocKind::OBJECT16_BACKGROUND:
+        return 16;
+      default:
+        MOZ_CRASH("Bad object alloc kind");
+    }
+}
+
+static inline size_t
+GetGCKindSlots(AllocKind thingKind, const Class* clasp)
+{
+    size_t nslots = GetGCKindSlots(thingKind);
+
+    /* An object's private data uses the space taken by its last fixed slot. */
+    if (clasp->flags & JSCLASS_HAS_PRIVATE) {
+        MOZ_ASSERT(nslots > 0);
+        nslots--;
+    }
+
+    /*
+     * Functions have a larger alloc kind than AllocKind::OBJECT to reserve
+     * space for the extra fields in JSFunction, but have no fixed slots.
+     */
+    if (clasp == FunctionClassPtr)
+        nslots = 0;
+
+    return nslots;
+}
+
+static inline size_t
+GetGCKindBytes(AllocKind thingKind)
+{
+    return sizeof(JSObject_Slots0) + GetGCKindSlots(thingKind) * sizeof(Value);
+}
+
+} // namespace gc
+} // namespace js
+
+#endif // gc_ObjectKind_inl_h
new file mode 100644
--- /dev/null
+++ b/js/src/gc/RelocationOverlay.h
@@ -0,0 +1,75 @@
+/* -*- 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/. */
+
+/*
+ * GC-internal definition of relocation overlay used while moving cells.
+ */
+
+#ifndef gc_RelocationOverlay_h
+#define gc_RelocationOverlay_h
+
+#include "mozilla/Assertions.h"
+
+#include <stdint.h>
+
+namespace js {
+namespace gc {
+
+struct Cell;
+
+/*
+ * This structure overlays a Cell that has been moved and provides a way to find
+ * its new location. It's used during generational and compacting GC.
+ */
+class RelocationOverlay
+{
+    /* The low bit is set so this should never equal a normal pointer. */
+    static const uintptr_t Relocated = uintptr_t(0xbad0bad1);
+
+    /* Set to Relocated when moved. */
+    uintptr_t magic_;
+
+    /* The location |this| was moved to. */
+    Cell* newLocation_;
+
+    /* A list entry to track all relocated things. */
+    RelocationOverlay* next_;
+
+  public:
+    static RelocationOverlay* fromCell(Cell* cell) {
+        return reinterpret_cast<RelocationOverlay*>(cell);
+    }
+
+    bool isForwarded() const {
+        return magic_ == Relocated;
+    }
+
+    Cell* forwardingAddress() const {
+        MOZ_ASSERT(isForwarded());
+        return newLocation_;
+    }
+
+    void forwardTo(Cell* cell);
+
+    RelocationOverlay*& nextRef() {
+        MOZ_ASSERT(isForwarded());
+        return next_;
+    }
+
+    RelocationOverlay* next() const {
+        MOZ_ASSERT(isForwarded());
+        return next_;
+    }
+
+    static bool isCellForwarded(Cell* cell) {
+        return fromCell(cell)->isForwarded();
+    }
+};
+
+} // namespace gc
+} // namespace js
+
+#endif /* gc_RelocationOverlay_h */
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -21,16 +21,17 @@
 #include "jit/MacroAssembler.h"
 #include "js/HashTable.h"
 #include "vm/Debugger.h"
 #include "vm/JSONParser.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 
+#include "gc/Iteration-inl.h"
 #include "gc/Nursery-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayEnd;
 
 using JS::AutoGCRooter;
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdio.h>
 
+#include "jsgc.h"
 #include "jsprf.h"
 #include "jsutil.h"
 
 #include "gc/Memory.h"
 #include "vm/Debugger.h"
 #include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 #include "vm/Time.h"
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -10,20 +10,23 @@
 #include "mozilla/Array.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsalloc.h"
-#include "jsgc.h"
 #include "jspubtd.h"
+#include "NamespaceImports.h"
 
+#include "gc/GCEnum.h"
 #include "js/GCAPI.h"
+#include "js/SliceBudget.h"
+#include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "vm/JSONPrinter.h"
 
 using mozilla::Maybe;
 
 namespace js {
 
 class GCParallelTask;
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -18,16 +18,18 @@
 #include "gc/GCInternals.h"
 #include "gc/Zone.h"
 #include "js/GCAPI.h"
 #include "js/HashTable.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 
+#include "gc/Marking-inl.h"
+
 using namespace js;
 using namespace js::gc;
 
 #ifdef JS_GC_ZEAL
 
 /*
  * Write barrier verification
  *
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -10,16 +10,17 @@
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitCompartment.h"
 #include "vm/Debugger.h"
 #include "vm/Runtime.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
+#include "gc/Marking-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
 
 JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
   : JS::shadow::Zone(rt, &rt->gc.marker),
--- a/js/src/irregexp/RegExpParser.cpp
+++ b/js/src/irregexp/RegExpParser.cpp
@@ -28,16 +28,18 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "irregexp/RegExpParser.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Move.h"
 
+#include "jsgc.h"
+
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpCharacters.h"
 #include "vm/ErrorReporting.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::irregexp;
 
--- a/js/src/jit-test/lib/syntax.js
+++ b/js/src/jit-test/lib/syntax.js
@@ -1148,23 +1148,16 @@ function test_syntax(postfixes, check_er
 
   // Expression closures
 
   test("function f() 1 ");
   test("function f() 1; ");
   test("(function () 1 ");
   test("(function () 1); ");
 
-  // Legacy generator
-
-  test("function f() { (yield ");
-  test("function f() { (yield 1 ");
-  test("function f() { f(yield ");
-  test("function f() { f(yield 1 ");
-
   // for each...in
 
   enableForEach();
   test("for each ");
   test("for each (");
   test("for each (x ");
   test("for each (x in ");
   test("for each (x in y ");
--- a/js/src/jit-test/tests/TypedObject/bug1098961.js
+++ b/js/src/jit-test/tests/TypedObject/bug1098961.js
@@ -1,9 +1,9 @@
 if (!this.hasOwnProperty("TypedObject"))
     quit();
 
-Array.prototype[Symbol.iterator] = function() {
+Array.prototype[Symbol.iterator] = function*() {
     for (var i = 3; --i >= 0;) {
         yield this[i]
     }
 }
 new TypedObject.ArrayType(TypedObject.int32, 0).build(1, x => 1)
--- a/js/src/jit-test/tests/TypedObject/fuzz1.js
+++ b/js/src/jit-test/tests/TypedObject/fuzz1.js
@@ -1,10 +1,10 @@
 // |jit-test| error:RangeError
 
 if (!this.hasOwnProperty("TypedObject"))
   throw new RangeError();
 
-function eval() {
+function* eval() {
     yield(undefined)
 }
 new TypedObject.StructType();
 eval();
--- a/js/src/jit-test/tests/arguments/bug843985.js
+++ b/js/src/jit-test/tests/arguments/bug843985.js
@@ -1,5 +1,5 @@
 
-for (x in (function() {
+for (x in (function*() {
     eval("arguments[0]");
     yield;
 })())(function() {})
--- a/js/src/jit-test/tests/arguments/defaults-bug759904.js
+++ b/js/src/jit-test/tests/arguments/defaults-bug759904.js
@@ -1,4 +1,4 @@
-function a(b=3) {
+function* a(b=3) {
     yield
 }
 a()
--- a/js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
+++ b/js/src/jit-test/tests/arguments/defaults-invalid-syntax.js
@@ -22,9 +22,9 @@ assertThrowsInstanceOf(function () {
     eval("function f(a,a,b=1) {}");
 }, SyntaxError);
 assertThrowsInstanceOf(function () {
     eval("function f(a,b=1,a=1) {}");
 }, SyntaxError);
 assertThrowsInstanceOf(function () {
     eval("function f(a=1,b=1,a=1) {}");
 }, SyntaxError);
-function silly_but_okay(a=(function () { yield 97; })) {}
+function silly_but_okay(a=(function* () { yield 97; })) {}
--- a/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js
+++ b/js/src/jit-test/tests/arguments/strict-args-generator-flushstack.js
@@ -1,26 +1,26 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 var args;
 
-function upToTen()
+function* upToTen()
 {
   "use strict";
   eval("args = arguments;");
   for (var i = 0; i < 9; i++)
     yield i;
 }
 
 var gen = upToTen();
 
 var i = 0;
-for (var v in gen)
+for (var v of gen)
 {
   assertEq(v, i);
   i++;
 }
 
 assertEq(i, 9);
 
 assertEq(Object.prototype.toString.call(args), "[object Arguments]");
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug521694.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Binary: cache/js-dbg-64-93d2eef52108-linux
-// Flags:
-//
-for(e in((function x(){yield(p=x())})())){}
--- a/js/src/jit-test/tests/auto-regress/bug563034.js
+++ b/js/src/jit-test/tests/auto-regress/bug563034.js
@@ -1,11 +1,11 @@
 // Binary: cache/js-dbg-64-c9212eb6175b-linux
 // Flags:
 //
 function f(a) {
-   function g() {
+   function* g() {
        yield function () { return a; };
    }
    return g();
 }
 var x;
-f(7).next()();
+assertEq(f(7).next().value(), 7);
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug622167.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Binary: cache/js-dbg-64-fe4489fb36ab-linux
-// Flags:
-//
-function
- eval( ) { eval();}
-try {
-DoWhile_3();
-} catch(e) {
-}
-function DoWhile_3() {
-  var result2 = "pass";
-woohooboy: {
-    eval('(function() { x getter= function(){} ; var x5, x = 0x99; })();');
-  }
-}
-test();
-function test()
-{
-  function foopy()
-  {
-    var f = function(){ r = arguments; test(); yield 170; }
-    try { for (var i in f()) { } } catch (iterError) { }
-  }
-  foopy();
-  gc();
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug678090.js
+++ /dev/null
@@ -1,24 +0,0 @@
-// |jit-test| error:ReferenceError
-
-// Binary: cache/js-dbg-64-609f37c36bd7-linux
-// Flags: -j -m -a
-//
-
-function toSource(arr) {
-  for (i=0; i<len; i++) {}
-}
-test();
-function test() {
-  function gen() {
-    var c = test;
-    try {
-      yield c;
-    } finally {
-      this.toSource();
-    }
-  }
-  var iter = gen();
-  for (i in iter) {
-    500();
-  }
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/auto-regress/bug726799.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// |jit-test| need-for-each
-
-// Binary: cache/js-dbg-32-ebafee0cea36-linux
-// Flags: -m -n
-//
-function tryItOut(code) {
-    f = eval("(function(){" + code + "})")
-    for (e in f()) {}
-}
-tryItOut("\
-    for each(x in[0,0,0,0,0,0,0]) {\
-        function f(b) {\
-            Object.defineProperty(b,\"\",({t:f}))\
-        }\
-        for each(d in[(1),String,String,String,String,(0),String,(1),String]) {\
-            try{\
-                f(d);\
-                yield\
-            }catch(e){}\
-        }\
-    }\
-")
--- a/js/src/jit-test/tests/auto-regress/bug797493.js
+++ b/js/src/jit-test/tests/auto-regress/bug797493.js
@@ -6,10 +6,10 @@
 
 var g = newGlobal();
 var dbg = new Debugger(g);
 dbg.onDebuggerStatement = function handleDebugger(frame) {
     frame.onPop = function handlePop(c) {
       poppedFrames.indexOf(this)
     }
 };
-g.eval("function g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
-assertEq(g.eval("var t = 0; for (j in g()) t += j; t;"), 45);
+g.eval("function* g() { for (var i = 0; i < 10; i++) { debugger; yield i; } }");
+assertEq(g.eval("var t = 0; for (j of g()) t += j; t;"), 45);
--- a/js/src/jit-test/tests/baseline/bug842429.js
+++ b/js/src/jit-test/tests/baseline/bug842429.js
@@ -1,11 +1,11 @@
-function gen() {
+function* gen() {
     try {
 	yield 3;
     } finally {
 	quit();
     }
 }
 try {
-    for (var i in gen())
+    for (var i of gen())
 	foo();
 } catch (e) {}
--- a/js/src/jit-test/tests/baseline/bug847446.js
+++ b/js/src/jit-test/tests/baseline/bug847446.js
@@ -1,21 +1,22 @@
 // |jit-test| error: ReferenceError
 var k = 0;
 function test() {
-    function gen()  {
+    function* gen()  {
 	try {
 	    try {
 		yield 1;
 	    } finally {
 		if (k++ < 60)
 		    actual += "Inner finally";
 	    }
 	} finally { }
     }
     try {
-	for (var i in gen())
-	    test();
+	var g = gen();
+	assertEq(g.next().value, 1);
+	g.next();
     } catch (e) {
 	throw e;
     }
 }
 test();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1411947.js
@@ -0,0 +1,6 @@
+if (helperThreadCount() === 0)
+  quit();
+evalInCooperativeThread(`
+startgc(9469, "shrinking");
+offThreadCompileScript("");
+`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1412298.js
@@ -0,0 +1,6 @@
+if (helperThreadCount() === 0)
+  quit();
+evalInCooperativeThread(`
+      const dbg = new Debugger();
+      evalInWorker("");
+`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1412654.js
@@ -0,0 +1,4 @@
+// |jit-test| error: can't clone
+var gv = newGlobal();
+gv.f = (class get {});
+gv.eval('f = clone(f);');
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug528644.js
+++ /dev/null
@@ -1,16 +0,0 @@
-// Don't crash
-
-function g(foo) {
-  for (a in foo) {
-  }
-}
-
-var makegen = eval("\n\
-  (function(b) {\n\
-      var h = \n\
-        eval(\"new function() { yield print(b) }\" ); \n\
-    return h\n\
-  })\n\
-");
-
-g(makegen());
--- a/js/src/jit-test/tests/basic/bug560234b.js
+++ b/js/src/jit-test/tests/basic/bug560234b.js
@@ -1,12 +1,12 @@
 function f(a) {
-   function g() {
+   function* g() {
        yield function () { return a; };
    }
    if (a == 8)
        return g();
    a = 3;
 }
 var x;
 for (var i = 0; i < 9; i++)
    x = f(i);
-x.next()();  // ReferenceError: a is not defined.
+x.next().value();  // ReferenceError: a is not defined.
--- a/js/src/jit-test/tests/basic/bug631082.js
+++ b/js/src/jit-test/tests/basic/bug631082.js
@@ -1,11 +1,11 @@
 var t;
 (function () {
-    t = (function() {
+    t = (function*() {
             yield k();
         })();
     function h() {
     }
     function k() {
         return function() { h(); };
     }
 })();
--- a/js/src/jit-test/tests/basic/bug716013.js
+++ b/js/src/jit-test/tests/basic/bug716013.js
@@ -1,4 +1,4 @@
-f = (function() {
+f = (function*() {
     for (x in [arguments, arguments]) yield(gczeal(4, function(){}))
-})
-for (i in f()) {}
+});
+for (i of f()) {}
--- a/js/src/jit-test/tests/basic/bug727921.js
+++ b/js/src/jit-test/tests/basic/bug727921.js
@@ -1,11 +1,11 @@
-(function() {
+(function*() {
     {
         let d;
         yield
     }
 })()
 eval("\
     (function(){\
-        schedulegc(5), 'a'.replace(/a/,function(){yield})\
+        schedulegc(5), 'a'.replace(/a/,function*(){yield})\
     })\
 ")()
--- a/js/src/jit-test/tests/basic/bug744356.js
+++ b/js/src/jit-test/tests/basic/bug744356.js
@@ -1,14 +1,14 @@
 // |jit-test| error: ReferenceError;
 gczeal(4);
-function gen() {
+function* gen() {
   var c = [1, "x"];
   try {
     yield c;
   } finally {
     gc();
   }
 }
 var iter = gen();
-for (i in iter) {
+for (i of iter) {
   (SECTION)();
 }
--- a/js/src/jit-test/tests/basic/bug767234.js
+++ b/js/src/jit-test/tests/basic/bug767234.js
@@ -1,9 +1,9 @@
-function gen()
+function* gen()
 {
     var local = new Date();
     yield 1;
     local = null;
     gc();
     gcslice(0); // Start IGC, but don't mark anything.
     yield 2;
 }
--- a/js/src/jit-test/tests/basic/bug852016-2.js
+++ b/js/src/jit-test/tests/basic/bug852016-2.js
@@ -1,8 +1,8 @@
 
-function foo(str) {
+function* foo(str) {
   var x = eval(str); yield x[0];
 }
 for (var i = 0; i < 5; i++)
-  assertEq(foo("[4,5,6]").next(), 4);
+  assertEq(foo("[4,5,6]").next().value, 4);
 for (var i = 0; i < 5; i++)
-  assertEq(foo("arguments").next(), "arguments");
+  assertEq(foo("arguments").next().value, "arguments");
--- a/js/src/jit-test/tests/basic/eif-generator.js
+++ b/js/src/jit-test/tests/basic/eif-generator.js
@@ -1,58 +1,58 @@
 load(libdir + "evalInFrame.js");
 
-function f() {
+function* f() {
     {
         let x = 1;
         while (true) {
             yield evalInFrame(0, "x");
             x++;
             {
                 let y = 1;
                 yield evalInFrame(0, "++y");
                 yield evalInFrame(0, "++y");
             }
         }
     }
 }
 
 var gen = f();
-assertEq(gen.next(), 1);
-assertEq(gen.next(), 2);
+assertEq(gen.next().value, 1);
+assertEq(gen.next().value, 2);
 gc();
-assertEq(gen.next(), 3);
+assertEq(gen.next().value, 3);
 gc();
-assertEq(gen.next(), 2);
-assertEq(gen.next(), 2);
+assertEq(gen.next().value, 2);
+assertEq(gen.next().value, 2);
 gc();
-assertEq(gen.next(), 3);
+assertEq(gen.next().value, 3);
 gc();
-assertEq(gen.next(), 3);
-assertEq(gen.next(), 2);
+assertEq(gen.next().value, 3);
+assertEq(gen.next().value, 2);
 gc();
-assertEq(gen.next(), 3);
+assertEq(gen.next().value, 3);
 gen = null;
 gc();
 
-function g() {
+function* g() {
     {
         let x = 1;
         while (true) {
             var inner = function (inc) { x += inc; return evalInFrame(0, "x") };
             assertEq(inner(0), x);
             yield inner;
             assertEq(inner(0), x);
         }
     }
 }
 
 var gen = g();
-var g1 = gen.next();
-var g2 = gen.next();
+var g1 = gen.next().value;
+var g2 = gen.next().value;
 gc();
 assertEq(g1(1), 2);
 assertEq(g2(1), 3);
 gc();
 assertEq(g1(1), 4);
 assertEq(g2(1), 5);
 gen = g1 = g2 = null;
 gc();
--- a/js/src/jit-test/tests/basic/spread-call.js
+++ b/js/src/jit-test/tests/basic/spread-call.js
@@ -27,17 +27,17 @@ function checkCommon(f, makeFn) {
               if (this.i < 4)
                   return { value: this.i++, done: false };
               else
                   return { value: undefined, done: true };
           }
       };
   };
   assertEqArray(makeFn("...arg")(f, itr), [1, 2, 3]);
-  function gen() {
+  function* gen() {
       for (let i = 1; i < 4; i ++)
           yield i;
   }
   assertEqArray(makeFn("...arg")(f, gen()), [1, 2, 3]);
 
   assertEqArray(makeFn("...arg=[1, 2, 3]")(f), [1, 2, 3]);
 
   // 12.2.4.1.2 Runtime Semantics: ArrayAccumulation
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBrandedVsGeneric.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const C = function (a, b, c) {
-    return function C() {
-        this.m1 = function () { return a; };
-        this.m2 = function () { return b; };
-        this.m3 = function () { return c; };
-    }
-}(2,3,4);
-var c = new C();
-var d = function (e) {return {m0: function () { return e; }}}(5);
-for (var i = 0; i < 5; i++)
-    d.m0();
-C.call(d);
-d.__iterator__ = function() {yield 55};
-for (i = 0; i < 5; i++) {
-    for (j in d)
-        print(j);
-}
--- a/js/src/jit-test/tests/basic/testBug550210.js
+++ b/js/src/jit-test/tests/basic/testBug550210.js
@@ -1,16 +1,16 @@
 function g(e) {
     return ("" + e);
 }
 
-function blah() {
+function* blah() {
     do {
         yield;
     } while ({}(p = arguments));
 }
 rv = blah();
 try {
-    for (a in rv) ;
+    for (a of rv) ;
 } catch (e) {
     print("" + g(e));
 }
 gc();
--- a/js/src/jit-test/tests/basic/testBug579602.js
+++ b/js/src/jit-test/tests/basic/testBug579602.js
@@ -1,15 +1,15 @@
 // don't panic
 
-f = function() {
+f = function*() {
   x = yield
 }
 rv = f()
-for (a in rv) (function() {})
+for (a of rv) (function() {})
 x = new Proxy({}, (function() {
   return {
     defineProperty: gc
   }
 })());
 with({
   d: (({
     x: Object.defineProperty(x, "", ({
--- a/js/src/jit-test/tests/basic/testBug593559.js
+++ b/js/src/jit-test/tests/basic/testBug593559.js
@@ -1,9 +1,9 @@
-var gen = (function () {yield})();
+var gen = (function* () {yield})();
 var t = gen.throw;
 try {
     new t;
 } catch (e) {
     actual = e;
 }
 assertEq(actual.name, "TypeError");
 assertEq(/is not a constructor/.test(actual.message), true);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testBug603193.js
+++ /dev/null
@@ -1,12 +0,0 @@
-function g(code) {