Merge inbound to mozilla-central r=merge a=merge
authorCosmin Sabou <csabou@mozilla.com>
Tue, 21 Nov 2017 00:01:02 +0200
changeset 444507 081c06e175b2b4431b7af5ea594ff0373e97b70a
parent 444471 0bbed2e6a4cc96649fc925cf6f8795b67956ed82 (current diff)
parent 444506 770f7a81d7dae2d1ebd310c20ef2f925df055948 (diff)
child 444523 5f6fb7f92a7cf07c18a02c0f3ca12d65d8a40bcd
child 444565 e75a922032c701a6321b5c41fab0611fc03dc4d1
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
081c06e175b2 / 59.0a1 / 20171120222519 / files
nightly linux64
081c06e175b2 / 59.0a1 / 20171120222519 / files
nightly mac
081c06e175b2 / 59.0a1 / 20171120222519 / files
nightly win32
081c06e175b2 / 59.0a1 / 20171120222519 / files
nightly win64
081c06e175b2 / 59.0a1 / 20171120222519 / 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 inbound to mozilla-central r=merge a=merge
accessible/tests/mochitest/name/test_general.html
dom/base/FragmentOrElement.cpp
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/chrome/content/browser.js
testing/web-platform/meta/dom/events/EventTarget-constructible.any.js.ini
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -858,17 +858,17 @@ ROLE(DOCUMENT_FRAME,
      eNoNameRule)
 
 ROLE(HEADING,
      "heading",
      ATK_ROLE_HEADING,
      @"AXHeading",
      USE_ROLE_STRING,
      IA2_ROLE_HEADING,
-     eNameFromSubtreeIfReqRule)
+     eNameFromSubtreeRule)
 
 ROLE(PAGE,
      "page",
      ATK_ROLE_PAGE,
      NSAccessibilityGroupRole,
      USE_ROLE_STRING,
      IA2_ROLE_PAGE,
      eNoNameRule)
--- a/accessible/jsat/OutputGenerator.jsm
+++ b/accessible/jsat/OutputGenerator.jsm
@@ -172,20 +172,23 @@ var OutputGenerator = {
     }
 
     let description = aAccessible.description;
     if (description) {
       // Compare against the calculated name unconditionally, regardless of name rule,
       // so we can make sure we don't speak duplicated descriptions
       let tmpName = name || aAccessible.name;
       if (tmpName && (description !== tmpName)) {
-        name = name || "";
-        name = this.outputOrder === OUTPUT_DESC_FIRST ?
-          description + " - " + name :
-          name + " - " + description;
+        if (name) {
+          name = this.outputOrder === OUTPUT_DESC_FIRST ?
+            description + " - " + name :
+            name + " - " + description;
+        } else {
+          name = description;
+        }
       }
     }
 
     if (!name || !name.trim()) {
       return;
     }
     aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? "push" : "unshift"](name);
   },
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -25,20 +25,20 @@ https://bugzilla.mozilla.org/show_bug.cg
         var tests = [{
           accOrElmOrID: "anchor",
           expectedUtterance: [[{"string": "link"}, "title"],
             ["title", {"string": "link"}]],
           expectedBraille: [[{"string": "linkAbbr"}, "title"],
             ["title", {"string": "linkAbbr"}]]
         }, {
           accOrElmOrID: "anchor_titleandtext",
-          expectedUtterance: [[{"string": "link"}, "goes to the tests -",
-            "Tests"], ["Tests", "- goes to the tests", {"string": "link"}]],
-          expectedBraille:   [[{"string": "linkAbbr"}, "goes to the tests -",
-            "Tests"], ["Tests", "- goes to the tests", {"string": "linkAbbr"}]],
+          expectedUtterance: [[{"string": "link"}, "goes to the tests",
+            "Tests"], ["Tests", "goes to the tests", {"string": "link"}]],
+          expectedBraille:   [[{"string": "linkAbbr"}, "goes to the tests",
+            "Tests"], ["Tests", "goes to the tests", {"string": "linkAbbr"}]],
         }, {
           accOrElmOrID: "anchor_duplicatedtitleandtext",
           expectedUtterance: [[{"string": "link"}, "Tests"],
             ["Tests", {"string": "link"}]],
           expectedBraille: [[{"string": "linkAbbr"}, "Tests"],
             ["Tests", {"string": "linkAbbr"}]]
         }, {
           accOrElmOrID: "anchor_arialabelandtext",
--- a/accessible/tests/mochitest/name/test_general.html
+++ b/accessible/tests/mochitest/name/test_general.html
@@ -143,16 +143,19 @@
       testName("tablemenuitem", "menuitem 1");
 
       // Get the name from child acronym title attribute rather than from
       // acronym content.
       testName("label_with_acronym", "O A T F World Wide Web");
 
       testName("testArticle", "Test article");
 
+      testName("h1", "heading");
+      testName("aria_heading", "aria_heading");
+
       // ////////////////////////////////////////////////////////////////////////
       // title attribute
 
       // If nothing is left. Let's try title attribute.
       testName("btn_title", "title");
 
       // ////////////////////////////////////////////////////////////////////////
       // textarea name
@@ -483,16 +486,19 @@
     <acronym title="O A T F">OATF</acronym>
     <abbr title="World Wide Web">WWW</abbr>
   </label>
 
   <div id="testArticle" role="article" title="Test article">
     <p>This is a paragraph inside the article.</p>
   </div>
 
+  <h1 id="h1" title="oops">heading</h1>
+  <div role="heading" id="aria_heading">aria_heading</div>
+
   <!-- name from title attribute -->
   <span id="btn_title" role="group" title="title">15</span>
 
   <!-- A textarea nested in a label with a text child (bug #453371). -->
   <form>
     <label>Story
       <textarea id="textareawithchild" name="name">Foo</textarea>
       is ended.
--- a/browser/base/content/test/alerts/browser.ini
+++ b/browser/base/content/test/alerts/browser.ini
@@ -3,11 +3,12 @@ support-files =
   head.js
   file_dom_notifications.html
 
 [browser_notification_close.js]
 skip-if = os == 'win' # Bug 1227785
 [browser_notification_do_not_disturb.js]
 skip-if = os == 'win' # Bug 1352791
 [browser_notification_open_settings.js]
+skip-if = os == 'win' # Bug 1411118
 [browser_notification_remove_permission.js]
 [browser_notification_replace.js]
 [browser_notification_tab_switching.js]
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -23,21 +23,16 @@ WIN32_INSTALLER_IN ?= $(ABS_DIST)/$(PKG_
 RETRIEVE_WINDOWS_INSTALLER = 1
 
 MOZ_LANGPACK_EID=langpack-$(AB_CD)@firefox.mozilla.org
 # For Nightly, we know where to get the builds from to do local repacks
 ifdef NIGHTLY_BUILD
 export EN_US_BINARY_URL ?= https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central
 endif
 
-L10N_PREF_JS_EXPORTS = $(call MERGE_FILE,firefox-l10n.js)
-L10N_PREF_JS_EXPORTS_PATH = $(FINAL_TARGET)/$(PREF_DIR)
-L10N_PREF_JS_EXPORTS_FLAGS = $(PREF_PPFLAGS) --silence-missing-directive-warnings
-PP_TARGETS += L10N_PREF_JS_EXPORTS
-
 ifneq (,$(filter cocoa,$(MOZ_WIDGET_TOOLKIT)))
 MOZ_PKG_MAC_DSSTORE=$(ABS_DIST)/branding/dsstore
 MOZ_PKG_MAC_BACKGROUND=$(ABS_DIST)/branding/background.png
 MOZ_PKG_MAC_ICON=$(ABS_DIST)/branding/disk.icns
 MOZ_PKG_MAC_EXTRA=--symlink '/Applications:/ '
 endif
 
 MOZ_SFX_PACKAGE=$(topsrcdir)/other-licenses/7zstub/firefox/7zSD.sfx
@@ -180,23 +175,16 @@ ifeq ($(OS_ARCH),WINNT)
 else
 	cat $< | \
 	  sed -e 's/^InfoText=/Info=/' -e 's/^TitleText=/Title=/' | \
 	  sed -e 's/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/' > \
 	  $(FINAL_TARGET)/../updater.ini
 endif
 endif
 
-ifndef IS_LANGPACK
-ifdef MOZ_CRASHREPORTER
-libs:: $(call MERGE_FILE,crashreporter/crashreporter-override.ini)
-	$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)
-endif
-endif
-
 ident:
 	@printf 'fx_revision '
 	@$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \
 	    $(STAGEDIST)/application.ini App SourceStamp
 	@printf 'buildid '
 	@$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \
 	    $(STAGEDIST)/application.ini App BuildID
 
--- a/browser/locales/moz.build
+++ b/browser/locales/moz.build
@@ -1,16 +1,22 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 JAR_MANIFESTS += ['jar.mn']
 
+# If DIST_SUBDIR ever gets unset in browser this path might be wrong due to PREF_DIR changing.
+LOCALIZED_PP_FILES.defaults.preferences += ['en-US/firefox-l10n.js']
+
+if CONFIG['MOZ_CRASHREPORTER']:
+    LOCALIZED_FILES += ['en-US/crashreporter/crashreporter-override.ini']
+
 with Files("**"):
     BUG_COMPONENT = ("Toolkit", "Build Config")
 
 with Files("all-locales"):
     BUG_COMPONENT = ("Core", "Localization")
 
 with Files("en-US/**"):
     BUG_COMPONENT = ("Core", "Localization")
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -10,17 +10,16 @@
 // We intentionally shadow non-virtual methods, but gcc gets confused.
 #ifdef __GNUC__
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
 #endif
 
 #include <stdint.h>
 #include "jspubtd.h"
-#include "js/GCAPI.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsID.h"
 #include "nsIDOMDOMException.h"
 #include "nsWrapperCache.h"
 #include "xpcexception.h"
 #include "nsString.h"
 #include "mozilla/dom/BindingDeclarations.h"
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -85,18 +85,16 @@
 #include "nsTextFragment.h"
 #include "nsContentCID.h"
 
 #include "nsIDOMEventListener.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWidget.h"
 
-#include "js/GCAPI.h"
-
 #include "nsNodeInfoManager.h"
 #include "nsICategoryManager.h"
 #include "nsGenericHTMLElement.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsIControllers.h"
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsIScrollableFrame.h"
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -7,17 +7,16 @@
 #ifndef nsIScriptContext_h__
 #define nsIScriptContext_h__
 
 #include "nscore.h"
 #include "nsStringGlue.h"
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "jspubtd.h"
-#include "js/GCAPI.h"
 
 class nsIScriptGlobalObject;
 
 #define NS_ISCRIPTCONTEXT_IID \
 { 0x54cbe9cf, 0x7282, 0x421a, \
  { 0x91, 0x6f, 0xd0, 0x70, 0x73, 0xde, 0xb8, 0xc0 } }
 
 class nsIOffThreadScriptReceiver;
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -3,17 +3,16 @@
 /* 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 nsWrapperCacheInline_h___
 #define nsWrapperCacheInline_h___
 
 #include "nsWrapperCache.h"
-#include "js/GCAPI.h"
 #include "js/TracingAPI.h"
 
 inline JSObject*
 nsWrapperCache::GetWrapperPreserveColor() const
 {
   JSObject* obj = mWrapper;
   if (obj && js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
     // The object has been found to be dead and is in the process of being
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -312,17 +312,16 @@ DOMInterfaces = {
 
 'EventTarget': {
     # When we get rid of hasXPConnectImpls, we can get rid of the
     # couldBeDOMBinding stuff in GetOrCreateDOMReflector.
     #
     # We can also get rid of the UnwrapArg bits in
     # the dom QueryInterface (in BindingUtils.cpp) at that point.
     'hasXPConnectImpls': True,
-    'concrete': False,
     'jsImplParent': 'mozilla::DOMEventTargetHelper',
 },
 
 'Exception': {
     'headerFile': 'mozilla/dom/DOMException.h',
     'binaryNames': {
         'message': 'messageMoz',
     },
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2376,32 +2376,21 @@ class MethodDefiner(PropertyDefiner):
                     raise TypeError("Legacy queryInterface member shouldn't be static")
                 signatures = m.signatures()
 
                 def argTypeIsIID(arg):
                     return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID'
                 if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]):
                     raise TypeError("There should be only one queryInterface method with 1 argument of type IID")
 
-                # Make sure to not stick QueryInterface on abstract interfaces that
-                # have hasXPConnectImpls (like EventTarget).  So only put it on
-                # interfaces that are concrete and all of whose ancestors are abstract.
-                def allAncestorsAbstract(iface):
-                    if not iface.parent:
-                        return True
-                    desc = self.descriptor.getDescriptor(iface.parent.identifier.name)
-                    if desc.concrete:
-                        return False
-                    return allAncestorsAbstract(iface.parent)
+                # Make sure to not stick QueryInterface on abstract interfaces.
                 if (not self.descriptor.interface.hasInterfacePrototypeObject() or
-                    not self.descriptor.concrete or
-                    not allAncestorsAbstract(self.descriptor.interface)):
+                    not self.descriptor.concrete):
                     raise TypeError("QueryInterface is only supported on "
-                                    "interfaces that are concrete and all "
-                                    "of whose ancestors are abstract: " +
+                                    "interfaces that are concrete: " +
                                     self.descriptor.name)
                 condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType
                 self.regular.append({
                     "name": 'QueryInterface',
                     "methodInfo": False,
                     "length": 1,
                     "flags": "0",
                     "condition": MemberCondition(func=condition)
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Exceptions.h"
 
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "jsapi.h"
 #include "jsprf.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsPIDOMWindow.h"
--- a/dom/canvas/ImageData.h
+++ b/dom/canvas/ImageData.h
@@ -11,17 +11,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/TypedArray.h"
 #include <stdint.h>
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 
 namespace mozilla {
 namespace dom {
 
 class ImageData final : public nsISupports
 {
   ~ImageData()
   {
new file mode 100644
--- /dev/null
+++ b/dom/events/ConstructibleEventTarget.cpp
@@ -0,0 +1,20 @@
+/* -*- 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 "mozilla/dom/ConstructibleEventTarget.h"
+#include "mozilla/dom/EventTargetBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+ConstructibleEventTarget::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+{
+  return EventTargetBinding::Wrap(cx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/events/ConstructibleEventTarget.h
@@ -0,0 +1,35 @@
+/* -*- 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_ConstructibleEventTarget_h_
+#define mozilla_dom_ConstructibleEventTarget_h_
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "js/RootingAPI.h"
+
+namespace mozilla {
+namespace dom {
+
+class ConstructibleEventTarget : public DOMEventTargetHelper
+{
+public:
+  // Not worrying about isupports and cycle collection here.  This does mean
+  // ConstructibleEventTarget will show up in CC and refcount logs as a
+  // DOMEventTargetHelper, but that's probably OK.
+
+  explicit ConstructibleEventTarget(nsIGlobalObject* aGlobalObject)
+    : DOMEventTargetHelper(aGlobalObject)
+  {
+  }
+
+  virtual JSObject* WrapObject(JSContext* cx,
+			       JS::Handle<JSObject*> aGivenProto) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ConstructibleEventTarget_h_
--- a/dom/events/DOMEventTargetHelper.h
+++ b/dom/events/DOMEventTargetHelper.h
@@ -138,20 +138,17 @@ public:
   nsPIDOMWindowInner* GetWindowIfCurrent() const;
   // Returns the document associated with this event target, if that document is
   // the current document of its browsing context.  Will return null otherwise.
   nsIDocument* GetDocumentIfCurrent() const;
   void BindToOwner(nsIGlobalObject* aOwner);
   void BindToOwner(nsPIDOMWindowInner* aOwner);
   void BindToOwner(DOMEventTargetHelper* aOther);
   virtual void DisconnectFromOwner();
-  nsIGlobalObject* GetParentObject() const
-  {
-    return GetOwnerGlobal();
-  }
+  using EventTarget::GetParentObject;
   virtual nsIGlobalObject* GetOwnerGlobal() const override
   {
     nsCOMPtr<nsIGlobalObject> parentObject = do_QueryReferent(mParentObject);
     return parentObject;
   }
   bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; }
 
   virtual void EventListenerAdded(nsAtom* aType) override;
--- a/dom/events/EventTarget.cpp
+++ b/dom/events/EventTarget.cpp
@@ -2,21 +2,36 @@
 /* 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 "mozilla/EventListenerManager.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/ConstructibleEventTarget.h"
+#include "nsIGlobalObject.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace dom {
 
+/* static */
+already_AddRefed<EventTarget>
+EventTarget::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!global) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+  RefPtr<EventTarget> target = new ConstructibleEventTarget(global);
+  return target.forget();
+}
+
 void
 EventTarget::RemoveEventListener(const nsAString& aType,
                                  EventListener* aListener,
                                  const EventListenerOptionsOrBoolean& aOptions,
                                  ErrorResult& aRv)
 {
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -23,45 +23,53 @@ class EventListenerManager;
 
 namespace dom {
 
 class AddEventListenerOptionsOrBoolean;
 class Event;
 class EventListener;
 class EventListenerOptionsOrBoolean;
 class EventHandlerNonNull;
+class GlobalObject;
 
 template <class T> struct Nullable;
 
 // IID for the dom::EventTarget interface
 #define NS_EVENTTARGET_IID \
 { 0xde651c36, 0x0053, 0x4c67, \
   { 0xb1, 0x3d, 0x67, 0xb9, 0x40, 0xfc, 0x82, 0xe4 } }
 
 class EventTarget : public nsIDOMEventTarget,
                     public nsWrapperCache
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_EVENTTARGET_IID)
 
   // WebIDL API
+  static already_AddRefed<EventTarget> Constructor(const GlobalObject& aGlobal,
+                                                   ErrorResult& aRv);
   using nsIDOMEventTarget::AddEventListener;
   using nsIDOMEventTarget::RemoveEventListener;
   using nsIDOMEventTarget::DispatchEvent;
   virtual void AddEventListener(const nsAString& aType,
                                 EventListener* aCallback,
                                 const AddEventListenerOptionsOrBoolean& aOptions,
                                 const Nullable<bool>& aWantsUntrusted,
                                 ErrorResult& aRv) = 0;
   virtual void RemoveEventListener(const nsAString& aType,
                                    EventListener* aCallback,
                                    const EventListenerOptionsOrBoolean& aOptions,
                                    ErrorResult& aRv);
   bool DispatchEvent(Event& aEvent, CallerType aCallerType, ErrorResult& aRv);
 
+  nsIGlobalObject* GetParentObject() const
+  {
+    return GetOwnerGlobal();
+  }
+
   // Note, this takes the type in onfoo form!
   EventHandlerNonNull* GetEventHandler(const nsAString& aType)
   {
     RefPtr<nsAtom> type = NS_Atomize(aType);
     return GetEventHandler(type, EmptyString());
   }
 
   // Note, this takes the type in onfoo form!
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -40,16 +40,17 @@ EXPORTS.mozilla += [
 ]
 
 EXPORTS.mozilla.dom += [
     'AnimationEvent.h',
     'BeforeUnloadEvent.h',
     'ClipboardEvent.h',
     'CommandEvent.h',
     'CompositionEvent.h',
+    'ConstructibleEventTarget.h',
     'CustomEvent.h',
     'DataTransfer.h',
     'DataTransferItem.h',
     'DataTransferItemList.h',
     'DeviceMotionEvent.h',
     'DragEvent.h',
     'Event.h',
     'EventTarget.h',
@@ -82,16 +83,17 @@ if CONFIG['MOZ_WEBSPEECH']:
 
 UNIFIED_SOURCES += [
     'AnimationEvent.cpp',
     'AsyncEventDispatcher.cpp',
     'BeforeUnloadEvent.cpp',
     'ClipboardEvent.cpp',
     'CommandEvent.cpp',
     'CompositionEvent.cpp',
+    'ConstructibleEventTarget.cpp',
     'ContentEventHandler.cpp',
     'CustomEvent.cpp',
     'DataTransfer.cpp',
     'DataTransferItem.cpp',
     'DataTransferItemList.cpp',
     'DeviceMotionEvent.cpp',
     'DOMEventTargetHelper.cpp',
     'DragEvent.cpp',
--- a/dom/flyweb/FlyWebServerEvents.cpp
+++ b/dom/flyweb/FlyWebServerEvents.cpp
@@ -8,18 +8,16 @@
 #include "mozilla/dom/FlyWebFetchEventBinding.h"
 #include "mozilla/dom/FlyWebPublishedServer.h"
 #include "mozilla/dom/FlyWebServerEvents.h"
 #include "mozilla/dom/FlyWebWebSocketEventBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/Response.h"
 
-#include "js/GCAPI.h"
-
 namespace mozilla {
 namespace dom {
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FlyWebFetchEvent)
 
 NS_IMPL_ADDREF_INHERITED(FlyWebFetchEvent, Event)
 NS_IMPL_RELEASE_INHERITED(FlyWebFetchEvent, Event)
--- a/dom/html/nsIHTMLCollection.h
+++ b/dom/html/nsIHTMLCollection.h
@@ -6,17 +6,17 @@
 
 #ifndef nsIHTMLCollection_h___
 #define nsIHTMLCollection_h___
 
 #include "nsISupports.h"
 #include "nsStringFwd.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsWrapperCache.h"
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 
 class nsINode;
 
 namespace mozilla {
 namespace dom {
 class Element;
 } // namespace dom
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -3,17 +3,16 @@
 /* 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 "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 
 #include "jsapi.h"
-#include "js/GCAPI.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
--- a/dom/media/eme/MediaKeyMessageEvent.cpp
+++ b/dom/media/eme/MediaKeyMessageEvent.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeyMessageEventBinding.h"
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 #include "jsfriendapi.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/dom/TypedArray.h"
 #include "nsContentUtils.h"
 #include "mozilla/dom/MediaKeys.h"
 
--- a/dom/vr/VRDisplayEvent.cpp
+++ b/dom/vr/VRDisplayEvent.cpp
@@ -1,16 +1,16 @@
 /* -*- 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 "VRDisplayEvent.h"
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/webauthn/u2f-hid-rs/src/lib.rs
+++ b/dom/webauthn/u2f-hid-rs/src/lib.rs
@@ -22,17 +22,17 @@ pub mod platform;
 #[cfg(any(target_os = "windows"))]
 #[path = "windows/mod.rs"]
 pub mod platform;
 
 #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
 #[path = "stub/mod.rs"]
 pub mod platform;
 
-#[cfg(not(any(target_os = "macos")))]
+#[cfg(target_os = "windows")]
 mod khmatcher;
 
 #[macro_use]
 extern crate log;
 extern crate rand;
 extern crate libc;
 extern crate boxfnonce;
 extern crate runloop;
--- a/dom/webauthn/u2f-hid-rs/src/linux/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/linux/mod.rs
@@ -1,172 +1,149 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use std::time::Duration;
 use std::thread;
 
 mod device;
-mod devicemap;
 mod hidraw;
 mod monitor;
+mod transaction;
 
 use consts::PARAMETER_SIZE;
-use khmatcher::KeyHandleMatcher;
-use runloop::RunLoop;
+use platform::device::Device;
+use platform::transaction::Transaction;
 use util::{io_err, OnceCallback};
-use u2fprotocol::{u2f_is_keyhandle_valid, u2f_register, u2f_sign};
+use u2fprotocol::{u2f_init_device, u2f_is_keyhandle_valid, u2f_register, u2f_sign};
 
-use self::devicemap::DeviceMap;
-use self::monitor::Monitor;
-
+#[derive(Default)]
 pub struct PlatformManager {
-    // Handle to the thread loop.
-    thread: Option<RunLoop>,
+    transaction: Option<Transaction>,
 }
 
 impl PlatformManager {
     pub fn new() -> Self {
-        Self { thread: None }
+        Default::default()
     }
 
     pub fn register(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<Vec<u8>>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new_with_timeout(
-            move |alive| {
-                let mut devices = DeviceMap::new();
-                let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
-                let mut matches = KeyHandleMatcher::new(&key_handles);
+        let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
+            // Create a new device.
+            let dev = &mut match Device::new(path) {
+                Ok(dev) => dev,
+                _ => return,
+            };
 
-                while alive() && monitor.alive() {
-                    // Add/remove devices.
-                    for event in monitor.events() {
-                        devices.process_event(event);
-                    }
+            // Try initializing it.
+            if !dev.is_u2f() || !u2f_init_device(dev) {
+                return;
+            }
 
-                    // Query newly added devices.
-                    matches.update(devices.iter_mut(), |device, key_handle| {
-                        u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
-                            .unwrap_or(false /* no match on failure */)
-                    });
+            // Iterate the exclude list and see if there are any matches.
+            // Abort the state machine if we found a valid key handle.
+            if key_handles.iter().any(|key_handle| {
+                u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
+                    .unwrap_or(false) /* no match on failure */
+            })
+            {
+                return;
+            }
 
-                    // Iterate all devices that don't match any of the handles
-                    // in the exclusion list and try to register.
-                    for (path, device) in devices.iter_mut() {
-                        if matches.get(path).is_empty() {
-                            if let Ok(bytes) = u2f_register(device, &challenge, &application) {
-                                callback.call(Ok(bytes));
-                                return;
-                            }
-                        }
-                    }
-
-                    // Wait a little before trying again.
-                    thread::sleep(Duration::from_millis(100));
+            while alive() {
+                if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
+                    callback.call(Ok(bytes));
+                    break;
                 }
 
-                callback.call(Err(io_err("aborted or timed out")));
-            },
-            timeout,
-        );
+                // Sleep a bit before trying again.
+                thread::sleep(Duration::from_millis(100));
+            }
+        });
 
-        self.thread = Some(try_or!(
-            thread,
-            |_| cbc.call(Err(io_err("couldn't create runloop")))
-        ));
+        self.transaction = Some(try_or!(transaction, |_| {
+            cbc.call(Err(io_err("couldn't create transaction")))
+        }));
     }
 
     pub fn sign(
         &mut self,
         timeout: u64,
         challenge: Vec<u8>,
         application: Vec<u8>,
         key_handles: Vec<Vec<u8>>,
         callback: OnceCallback<(Vec<u8>, Vec<u8>)>,
     ) {
         // Abort any prior register/sign calls.
         self.cancel();
 
         let cbc = callback.clone();
 
-        let thread = RunLoop::new_with_timeout(
-            move |alive| {
-                let mut devices = DeviceMap::new();
-                let monitor = try_or!(Monitor::new(), |e| callback.call(Err(e)));
-                let mut matches = KeyHandleMatcher::new(&key_handles);
+        let transaction = Transaction::new(timeout, cbc.clone(), move |path, alive| {
+            // Create a new device.
+            let dev = &mut match Device::new(path) {
+                Ok(dev) => dev,
+                _ => return,
+            };
 
-                while alive() && monitor.alive() {
-                    // Add/remove devices.
-                    for event in monitor.events() {
-                        devices.process_event(event);
-                    }
-
-                    // Query newly added devices.
-                    matches.update(devices.iter_mut(), |device, key_handle| {
-                        u2f_is_keyhandle_valid(device, &challenge, &application, key_handle)
-                            .unwrap_or(false /* no match on failure */)
-                    });
-
-                    // Iterate all devices.
-                    for (path, device) in devices.iter_mut() {
-                        let key_handles = matches.get(path);
+            // Try initializing it.
+            if !dev.is_u2f() || !u2f_init_device(dev) {
+                return;
+            }
 
-                        // If the device matches none of the given key handles
-                        // then just make it blink with bogus data.
-                        if key_handles.is_empty() {
-                            let blank = vec![0u8; PARAMETER_SIZE];
-                            if let Ok(_) = u2f_register(device, &blank, &blank) {
-                                callback.call(Err(io_err("invalid key")));
-                                return;
-                            }
-
-                            continue;
-                        }
+            // Find all matching key handles.
+            let key_handles = key_handles
+                .iter()
+                .filter(|key_handle| {
+                    u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
+                        .unwrap_or(false) /* no match on failure */
+                })
+                .collect::<Vec<_>>();
 
-                        // Otherwise, try to sign.
-                        for key_handle in key_handles {
-                            if let Ok(bytes) = u2f_sign(
-                                device,
-                                &challenge,
-                                &application,
-                                key_handle,
-                            )
-                            {
-                                callback.call(Ok((key_handle.to_vec(), bytes)));
-                                return;
-                            }
+            while alive() {
+                // If the device matches none of the given key handles
+                // then just make it blink with bogus data.
+                if key_handles.is_empty() {
+                    let blank = vec![0u8; PARAMETER_SIZE];
+                    if let Ok(_) = u2f_register(dev, &blank, &blank) {
+                        callback.call(Err(io_err("invalid key")));
+                        break;
+                    }
+                } else {
+                    // Otherwise, try to sign.
+                    for key_handle in &key_handles {
+                        if let Ok(bytes) = u2f_sign(dev, &challenge, &application, key_handle) {
+                            callback.call(Ok((key_handle.to_vec(), bytes)));
+                            break;
                         }
                     }
-
-                    // Wait a little before trying again.
-                    thread::sleep(Duration::from_millis(100));
                 }
 
-                callback.call(Err(io_err("aborted or timed out")));
-            },
-            timeout,
-        );
+                // Sleep a bit before trying again.
+                thread::sleep(Duration::from_millis(100));
+            }
+        });
 
-        self.thread = Some(try_or!(
-            thread,
-            |_| cbc.call(Err(io_err("couldn't create runloop")))
-        ));
+        self.transaction = Some(try_or!(transaction, |_| {
+            cbc.call(Err(io_err("couldn't create transaction")))
+        }));
     }
 
     // This blocks.
     pub fn cancel(&mut self) {
-        if let Some(thread) = self.thread.take() {
-            thread.cancel();
+        if let Some(mut transaction) = self.transaction.take() {
+            transaction.cancel();
         }
     }
 }
--- a/dom/webauthn/u2f-hid-rs/src/linux/monitor.rs
+++ b/dom/webauthn/u2f-hid-rs/src/linux/monitor.rs
@@ -1,24 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use libc::{c_int, c_short, c_ulong};
 use libudev;
 use libudev::EventType;
-
-use libc::{c_int, c_short, c_ulong};
-
+use runloop::RunLoop;
+use std::collections::HashMap;
 use std::ffi::OsString;
 use std::io;
 use std::os::unix::io::AsRawFd;
-use std::sync::mpsc::{channel, Receiver, TryIter};
-
-use runloop::RunLoop;
-use util::to_io_err;
+use std::sync::Arc;
 
 const UDEV_SUBSYSTEM: &'static str = "hidraw";
 const POLLIN: c_short = 0x0001;
 const POLL_TIMEOUT: c_int = 100;
 
 fn poll(fds: &mut Vec<::libc::pollfd>) -> io::Result<()> {
     let nfds = fds.len() as c_ulong;
 
@@ -26,95 +23,110 @@ fn poll(fds: &mut Vec<::libc::pollfd>) -
 
     if rv < 0 {
         Err(io::Error::from_raw_os_error(rv))
     } else {
         Ok(())
     }
 }
 
-pub enum Event {
-    Add(OsString),
-    Remove(OsString),
+pub struct Monitor<F>
+where
+    F: Fn(OsString, &Fn() -> bool) + Sync,
+{
+    runloops: HashMap<OsString, RunLoop>,
+    new_device_cb: Arc<F>,
 }
 
-impl Event {
-    fn from_udev(event: libudev::Event) -> Option<Self> {
+impl<F> Monitor<F>
+where
+    F: Fn(OsString, &Fn() -> bool) + Send + Sync + 'static,
+{
+    pub fn new(new_device_cb: F) -> Self {
+        Self {
+            runloops: HashMap::new(),
+            new_device_cb: Arc::new(new_device_cb),
+        }
+    }
+
+    pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
+        let ctx = libudev::Context::new()?;
+
+        let mut enumerator = libudev::Enumerator::new(&ctx)?;
+        enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
+
+        // Iterate all existing devices.
+        for dev in enumerator.scan_devices()? {
+            if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
+                self.add_device(path);
+            }
+        }
+
+        let mut monitor = libudev::Monitor::new(&ctx)?;
+        monitor.match_subsystem(UDEV_SUBSYSTEM)?;
+
+        // Start listening for new devices.
+        let mut socket = monitor.listen()?;
+        let mut fds = vec![
+            ::libc::pollfd {
+                fd: socket.as_raw_fd(),
+                events: POLLIN,
+                revents: 0,
+            },
+        ];
+
+        while alive() {
+            // Wait for new events, break on failure.
+            poll(&mut fds)?;
+
+            if let Some(event) = socket.receive_event() {
+                self.process_event(event);
+            }
+        }
+
+        // Remove all tracked devices.
+        self.remove_all_devices();
+
+        Ok(())
+    }
+
+    fn process_event(&mut self, event: libudev::Event) {
         let path = event.device().devnode().map(
             |dn| dn.to_owned().into_os_string(),
         );
 
         match (event.event_type(), path) {
-            (EventType::Add, Some(path)) => Some(Event::Add(path)),
-            (EventType::Remove, Some(path)) => Some(Event::Remove(path)),
-            _ => None,
+            (EventType::Add, Some(path)) => {
+                self.add_device(path);
+            }
+            (EventType::Remove, Some(path)) => {
+                self.remove_device(path);
+            }
+            _ => { /* ignore other types and failures */ }
+        }
+    }
+
+    fn add_device(&mut self, path: OsString) {
+        let f = self.new_device_cb.clone();
+        let key = path.clone();
+
+        let runloop = RunLoop::new(move |alive| if alive() {
+            f(path, alive);
+        });
+
+        if let Ok(runloop) = runloop {
+            self.runloops.insert(key, runloop);
+        }
+    }
+
+    fn remove_device(&mut self, path: OsString) {
+        if let Some(runloop) = self.runloops.remove(&path) {
+            runloop.cancel();
+        }
+    }
+
+    fn remove_all_devices(&mut self) {
+        while !self.runloops.is_empty() {
+            let path = self.runloops.keys().next().unwrap().clone();
+            self.remove_device(path);
         }
     }
 }
-
-pub struct Monitor {
-    // Receive events from the thread.
-    rx: Receiver<Event>,
-    // Handle to the thread loop.
-    thread: RunLoop,
-}
-
-impl Monitor {
-    pub fn new() -> io::Result<Self> {
-        let (tx, rx) = channel();
-
-        let thread = RunLoop::new(move |alive| -> io::Result<()> {
-            let ctx = libudev::Context::new()?;
-            let mut enumerator = libudev::Enumerator::new(&ctx)?;
-            enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
-
-            // Iterate all existing devices.
-            for dev in enumerator.scan_devices()? {
-                if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
-                    tx.send(Event::Add(path)).map_err(to_io_err)?;
-                }
-            }
-
-            let mut monitor = libudev::Monitor::new(&ctx)?;
-            monitor.match_subsystem(UDEV_SUBSYSTEM)?;
-
-            // Start listening for new devices.
-            let mut socket = monitor.listen()?;
-            let mut fds = vec![
-                ::libc::pollfd {
-                    fd: socket.as_raw_fd(),
-                    events: POLLIN,
-                    revents: 0,
-                },
-            ];
-
-            // Loop until we're stopped by the controlling thread, or fail.
-            while alive() {
-                // Wait for new events, break on failure.
-                poll(&mut fds)?;
-
-                // Send the event over.
-                let udev_event = socket.receive_event();
-                if let Some(event) = udev_event.and_then(Event::from_udev) {
-                    tx.send(event).map_err(to_io_err)?;
-                }
-            }
-
-            Ok(())
-        })?;
-
-        Ok(Self { rx, thread })
-    }
-
-    pub fn events<'a>(&'a self) -> TryIter<'a, Event> {
-        self.rx.try_iter()
-    }
-
-    pub fn alive(&self) -> bool {
-        self.thread.alive()
-    }
-}
-
-impl Drop for Monitor {
-    fn drop(&mut self) {
-        self.thread.cancel();
-    }
-}
new file mode 100644
--- /dev/null
+++ b/dom/webauthn/u2f-hid-rs/src/linux/transaction.rs
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use platform::monitor::Monitor;
+use runloop::RunLoop;
+use std::ffi::OsString;
+use std::io;
+use util::{io_err, OnceCallback};
+
+pub struct Transaction {
+    // Handle to the thread loop.
+    thread: Option<RunLoop>,
+}
+
+impl Transaction {
+    pub fn new<F, T>(timeout: u64, callback: OnceCallback<T>, new_device_cb: F) -> io::Result<Self>
+    where
+        F: Fn(OsString, &Fn() -> bool) + Sync + Send + 'static,
+        T: 'static,
+    {
+        let thread = RunLoop::new_with_timeout(
+            move |alive| {
+                // Create a new device monitor.
+                let mut monitor = Monitor::new(new_device_cb);
+
+                // Start polling for new devices.
+                try_or!(monitor.run(alive), |e| callback.call(Err(e)));
+
+                // Send an error, if the callback wasn't called already.
+                callback.call(Err(io_err("aborted or timed out")));
+            },
+            timeout,
+        )?;
+
+        Ok(Self { thread: Some(thread) })
+    }
+
+    pub fn cancel(&mut self) {
+        // This must never be None.
+        self.thread.take().unwrap().cancel();
+    }
+}
--- a/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
+++ b/dom/webauthn/u2f-hid-rs/src/macos/mod.rs
@@ -45,17 +45,17 @@ impl PlatformManager {
             // Create a new device.
             let dev = &mut Device::new(device_ref, rx);
 
             // Try initializing it.
             if !u2f_init_device(dev) {
                 return;
             }
 
-            // Iterate the exlude list and see if there are any matches.
+            // Iterate the exclude list and see if there are any matches.
             // Abort the state machine if we found a valid key handle.
             if key_handles.iter().any(|key_handle| {
                 u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
                     .unwrap_or(false) /* no match on failure */
             })
             {
                 return;
             }
@@ -102,17 +102,17 @@ impl PlatformManager {
 
             // Find all matching key handles.
             let key_handles = key_handles
                 .iter()
                 .filter(|key_handle| {
                     u2f_is_keyhandle_valid(dev, &challenge, &application, key_handle)
                         .unwrap_or(false) /* no match on failure */
                 })
-                .collect::<Vec<&Vec<u8>>>();
+                .collect::<Vec<_>>();
 
             while alive() {
                 // If the device matches none of the given key handles
                 // then just make it blink with bogus data.
                 if key_handles.is_empty() {
                     let blank = vec![0u8; PARAMETER_SIZE];
                     if let Ok(_) = u2f_register(dev, &blank, &blank) {
                         callback.call(Err(io_err("invalid key")));
--- a/dom/webidl/EventTarget.webidl
+++ b/dom/webidl/EventTarget.webidl
@@ -18,17 +18,18 @@ dictionary EventListenerOptions {
   boolean mozSystemGroup = false;
 };
 
 dictionary AddEventListenerOptions : EventListenerOptions {
   boolean passive = false;
   boolean once = false;
 };
 
-[Exposed=(Window,Worker,WorkerDebugger,System)]
+[Constructor,
+ Exposed=(Window,Worker,WorkerDebugger,System)]
 interface EventTarget {
   /* Passing null for wantsUntrusted means "default behavior", which
      differs in content and chrome.  In content that default boolean
      value is true, while in chrome the default boolean value is
      false. */
   [Throws]
   void addEventListener(DOMString type,
                         EventListener? listener,
--- a/dom/webidl/U2F.webidl
+++ b/dom/webidl/U2F.webidl
@@ -1,18 +1,17 @@
 /* -*- Mode: IDL; 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/.
  *
  * The origin of this IDL file is a combination of the FIDO U2F Raw Message Formats:
- * https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html
- * and the U2F JavaScript API v1.1, not yet published. While v1.1 is not published,
- * v1.0, is located here:
- * https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-javascript-api.html
+ * https://www.fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html
+ * and the U2F JavaScript API v1.1:
+ * https://www.fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-javascript-api-v1.1-id-20160915.html
  */
 
 [NoInterfaceObject]
 interface GlobalU2F {
   [Throws, Pref="security.webauth.u2f"]
   readonly attribute U2F u2f;
 };
 
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -2,17 +2,17 @@
 /* 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 nsXBLMaybeCompiled_h__
 #define nsXBLMaybeCompiled_h__
 
-#include "js/GCAPI.h"
+#include "js/RootingAPI.h"
 
 /*
  * A union containing either a pointer representing uncompiled source or a
  * JSObject* representing the compiled result.  The class is templated on the
  * source object type.
  *
  * The purpose of abstracting this as a separate class is to allow it to be
  * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -41,46 +41,62 @@ bool
 CapturedBufferState::Unrotate::UnrotateBuffer()
 {
   return mBuffer->UnrotateBufferTo(mParameters);
 }
 
 bool
 CapturedBufferState::PrepareBuffer()
 {
-  return (!mBufferCopy || mBufferCopy->CopyBuffer()) &&
-         (!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer());
+  return (!mBufferFinalize || mBufferFinalize->CopyBuffer()) &&
+         (!mBufferUnrotate || mBufferUnrotate->UnrotateBuffer()) &&
+         (!mBufferInitialize || mBufferInitialize->CopyBuffer());
 }
 
 void
 CapturedBufferState::GetTextureClients(nsTArray<RefPtr<TextureClient>>& aTextureClients)
 {
-  if (mBufferCopy) {
-    if (TextureClient* source = mBufferCopy->mSource->GetClient()) {
+  if (mBufferFinalize) {
+    if (TextureClient* source = mBufferFinalize->mSource->GetClient()) {
       aTextureClients.AppendElement(source);
     }
-    if (TextureClient* sourceOnWhite = mBufferCopy->mSource->GetClientOnWhite()) {
+    if (TextureClient* sourceOnWhite = mBufferFinalize->mSource->GetClientOnWhite()) {
       aTextureClients.AppendElement(sourceOnWhite);
     }
-    if (TextureClient* destination = mBufferCopy->mDestination->GetClient()) {
+    if (TextureClient* destination = mBufferFinalize->mDestination->GetClient()) {
       aTextureClients.AppendElement(destination);
     }
-    if (TextureClient* destinationOnWhite = mBufferCopy->mDestination->GetClientOnWhite()) {
+    if (TextureClient* destinationOnWhite = mBufferFinalize->mDestination->GetClientOnWhite()) {
       aTextureClients.AppendElement(destinationOnWhite);
     }
   }
 
   if (mBufferUnrotate) {
     if (TextureClient* client = mBufferUnrotate->mBuffer->GetClient()) {
       aTextureClients.AppendElement(client);
     }
     if (TextureClient* clientOnWhite = mBufferUnrotate->mBuffer->GetClientOnWhite()) {
       aTextureClients.AppendElement(clientOnWhite);
     }
   }
+
+  if (mBufferInitialize) {
+    if (TextureClient* source = mBufferInitialize->mSource->GetClient()) {
+      aTextureClients.AppendElement(source);
+    }
+    if (TextureClient* sourceOnWhite = mBufferInitialize->mSource->GetClientOnWhite()) {
+      aTextureClients.AppendElement(sourceOnWhite);
+    }
+    if (TextureClient* destination = mBufferInitialize->mDestination->GetClient()) {
+      aTextureClients.AppendElement(destination);
+    }
+    if (TextureClient* destinationOnWhite = mBufferInitialize->mDestination->GetClientOnWhite()) {
+      aTextureClients.AppendElement(destinationOnWhite);
+    }
+  }
 }
 
 StaticAutoPtr<PaintThread> PaintThread::sSingleton;
 StaticRefPtr<nsIThread> PaintThread::sThread;
 PlatformThreadId PaintThread::sThreadId;
 
 // RAII make sure we clean up and restore our draw targets
 // when we paint async.
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -97,18 +97,19 @@ public:
    * Prepares the rotated buffers for painting by copying a previous frame
    * into the buffer and/or unrotating the pixels and returns whether the
    * operations were successful. If this fails a new buffer should be created
    * for the frame.
    */
   bool PrepareBuffer();
   void GetTextureClients(nsTArray<RefPtr<TextureClient>>& aTextureClients);
 
-  Maybe<Copy> mBufferCopy;
+  Maybe<Copy> mBufferFinalize;
   Maybe<Unrotate> mBufferUnrotate;
+  Maybe<Copy> mBufferInitialize;
 
 protected:
   ~CapturedBufferState() {}
 };
 
 typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
 
 class CompositorBridgeChild;
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -155,72 +155,84 @@ ContentClient::BeginPaint(PaintedLayer* 
                          !(aLayer->Manager()->AsWebRenderLayerManager());
   bool canDrawRotated = aFlags & PAINT_CAN_DRAW_ROTATED;
   bool asyncPaint = (aFlags & PAINT_ASYNC);
 
   IntRect drawBounds = result.mRegionToDraw.GetBounds();
   OpenMode lockMode = asyncPaint ? OpenMode::OPEN_READ_ASYNC_WRITE
                                  : OpenMode::OPEN_READ_WRITE;
 
+  if (asyncPaint) {
+    result.mBufferState = new CapturedBufferState();
+  }
+
+  if (mBuffer) {
+    if (mBuffer->Lock(lockMode)) {
+      // Do not modify result.mRegionToDraw or result.mContentType after this call.
+      Maybe<CapturedBufferState::Copy> bufferFinalize =
+        FinalizeFrame(result.mRegionToDraw);
+
+      if (asyncPaint) {
+        result.mBufferState->mBufferFinalize = Move(bufferFinalize);
+      } else if (bufferFinalize) {
+        bufferFinalize->CopyBuffer();
+      }
+    } else {
+      result.mRegionToDraw = dest.mNeededRegion;
+      dest.mCanReuseBuffer = false;
+      Clear();
+    }
+  }
+
   if (dest.mCanReuseBuffer) {
     MOZ_ASSERT(mBuffer);
 
     bool canReuseBuffer = false;
 
-    if (mBuffer->Lock(lockMode)) {
-      RefPtr<CapturedBufferState> bufferState = new CapturedBufferState();
-
-      // Do not modify result.mRegionToDraw or result.mContentType after this call.
-      FinalizeFrame(result.mRegionToDraw, bufferState);
-
-      auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
+    auto newParameters = mBuffer->AdjustedParameters(dest.mBufferRect);
+    Maybe<CapturedBufferState::Unrotate> bufferUnrotate = Nothing();
 
-      if ((!canHaveRotation && newParameters.IsRotated()) ||
-          (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
-        bufferState->mBufferUnrotate = Some(CapturedBufferState::Unrotate {
-          newParameters,
-          mBuffer->ShallowCopy(),
-        });
-      }
+    if ((!canHaveRotation && newParameters.IsRotated()) ||
+        (!canDrawRotated && newParameters.RectWrapsBuffer(drawBounds))) {
+      bufferUnrotate = Some(CapturedBufferState::Unrotate {
+        newParameters,
+        mBuffer->ShallowCopy(),
+      });
+    }
 
-      // If we're async painting then return the buffer state to
-      // be dispatched to the paint thread, otherwise do it now
-      if (asyncPaint) {
-        // We cannot do a buffer unrotate if the buffer is already rotated
-        // and we're async painting as that may fail
-        if (!bufferState->mBufferUnrotate ||
-            mBuffer->BufferRotation() == IntPoint(0,0)) {
-          result.mBufferState = bufferState;
+    // If we're async painting then return the buffer state to
+    // be dispatched to the paint thread, otherwise do it now
+    if (asyncPaint) {
+      // We cannot do a buffer unrotate if the buffer is already rotated
+      // and we're async painting as that may fail
+      if (!bufferUnrotate ||
+          mBuffer->BufferRotation() == IntPoint(0,0)) {
+        result.mBufferState->mBufferUnrotate = Move(bufferUnrotate);
 
-          // We can then assume that preparing the buffer will always
-          // succeed and update our parameters unconditionally
-          if (bufferState->mBufferUnrotate) {
-            newParameters.SetUnrotated();
-          }
-          mBuffer->SetParameters(newParameters);
-          canReuseBuffer = true;
+        // We can then assume that preparing the buffer will always
+        // succeed and update our parameters unconditionally
+        if (result.mBufferState->mBufferUnrotate) {
+          newParameters.SetUnrotated();
         }
-      } else {
-        if (bufferState->PrepareBuffer()) {
-          if (bufferState->mBufferUnrotate) {
-            newParameters.SetUnrotated();
-          }
-          mBuffer->SetParameters(newParameters);
-          canReuseBuffer = true;
+        mBuffer->SetParameters(newParameters);
+        canReuseBuffer = true;
+      }
+    } else {
+      if (!bufferUnrotate || bufferUnrotate->UnrotateBuffer()) {
+        if (bufferUnrotate) {
+          newParameters.SetUnrotated();
         }
+        mBuffer->SetParameters(newParameters);
+        canReuseBuffer = true;
       }
     }
 
     if (!canReuseBuffer) {
-      if (mBuffer->IsLocked()) {
-        mBuffer->Unlock();
-      }
       dest.mBufferRect = ComputeBufferRect(dest.mNeededRegion.GetBounds());
       dest.mCanReuseBuffer = false;
-      dest.mMustRemoveFrontBuffer = true;
     }
   }
 
   NS_ASSERTION(!(aFlags & PAINT_WILL_RESAMPLE) || dest.mBufferRect == dest.mNeededRegion.GetBounds(),
                "If we're resampling, we need to validate the entire buffer");
 
   // We never had a buffer, the buffer wasn't big enough, the content changed
   // types, or we failed to unrotate the buffer when requested. In any case,
@@ -238,56 +250,56 @@ ContentClient::BeginPaint(PaintedLayer* 
     if (!newBuffer) {
       if (Factory::ReasonableSurfaceSize(IntSize(dest.mBufferRect.Width(), dest.mBufferRect.Height()))) {
         gfxCriticalNote << "Failed buffer for "
                         << dest.mBufferRect.x << ", "
                         << dest.mBufferRect.y << ", "
                         << dest.mBufferRect.Width() << ", "
                         << dest.mBufferRect.Height();
       }
+      Clear();
       return result;
     }
 
     if (!newBuffer->Lock(lockMode)) {
       gfxCriticalNote << "Failed to lock new back buffer.";
+      Clear();
       return result;
     }
 
     // If we have an existing front buffer, copy it into the new back buffer
-    if (RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer()) {
+    if (mBuffer) {
+      if (mBuffer->IsLocked()) {
+        mBuffer->Unlock();
+      }
+
       nsIntRegion updateRegion = newBuffer->BufferRect();
       updateRegion.Sub(updateRegion, result.mRegionToDraw);
 
       if (!updateRegion.IsEmpty()) {
-        RefPtr<CapturedBufferState> bufferState = new CapturedBufferState();
-
-        bufferState->mBufferCopy = Some(CapturedBufferState::Copy {
-          frontBuffer->ShallowCopy(),
+        auto bufferInitialize = CapturedBufferState::Copy {
+          mBuffer->ShallowCopy(),
           newBuffer->ShallowCopy(),
           updateRegion.GetBounds(),
-        });
+        };
 
         // If we're async painting then return the buffer state to
         // be dispatched to the paint thread, otherwise do it now
         if (asyncPaint) {
-          MOZ_ASSERT(!result.mBufferState);
-          result.mBufferState = bufferState;
+          result.mBufferState->mBufferInitialize = Some(Move(bufferInitialize));
         } else {
-          if (!bufferState->PrepareBuffer()) {
+          if (!bufferInitialize.CopyBuffer()) {
             gfxCriticalNote << "Failed to copy front buffer to back buffer.";
             return result;
           }
         }
       }
-
-      if (dest.mMustRemoveFrontBuffer) {
-        Clear();
-      }
     }
 
+    Clear();
     mBuffer = newBuffer;
   }
 
   NS_ASSERTION(canHaveRotation || mBuffer->BufferRotation() == IntPoint(0,0),
                "Rotation disabled, but we have nonzero rotation?");
 
   nsIntRegion invalidate;
   invalidate.Sub(aLayer->GetValidRegion(), dest.mBufferRect);
@@ -414,27 +426,24 @@ ContentClient::PrepareDrawTargetForPaint
 ContentClient::BufferDecision
 ContentClient::CalculateBufferForPaint(PaintedLayer* aLayer,
                                        uint32_t aFlags)
 {
   gfxContentType layerContentType =
     aLayer->CanUseOpaqueSurface() ? gfxContentType::COLOR :
                                     gfxContentType::COLOR_ALPHA;
 
-  RefPtr<RotatedBuffer> frontBuffer = GetFrontBuffer();
-
   SurfaceMode mode;
   gfxContentType contentType;
   IntRect destBufferRect;
   nsIntRegion neededRegion;
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canReuseBuffer = !!mBuffer;
   bool canKeepBufferContents = true;
-  bool mustRemoveFrontBuffer = false;
 
   while (true) {
     mode = aLayer->GetSurfaceMode();
     neededRegion = aLayer->GetVisibleRegion().ToUnknownRegion();
     canReuseBuffer = canReuseBuffer && ValidBufferSize(mBufferSizePolicy,
                                                        mBuffer->BufferRect().Size(),
                                                        neededRegion.GetBounds().Size());
     contentType = layerContentType;
@@ -446,32 +455,17 @@ ContentClient::CalculateBufferForPaint(P
       } else if (neededRegion.GetBounds().Size() <= mBuffer->BufferRect().Size()) {
         // The buffer's big enough but doesn't contain everything that's
         // going to be visible. We'll move it.
         destBufferRect = IntRect(neededRegion.GetBounds().TopLeft(), mBuffer->BufferRect().Size());
       } else {
         destBufferRect = neededRegion.GetBounds();
       }
     } else {
-      // We won't be reusing the buffer. Compute a new rect.
-      if (frontBuffer) {
-        // We must create a buffer that is the same size as the front buffer,
-        // or else we need to remove the front buffer
-        if (ValidBufferSize(mBufferSizePolicy,
-                            frontBuffer->BufferRect().Size(),
-                            neededRegion.GetBounds().Size())) {
-          destBufferRect = frontBuffer->BufferRect();
-          destBufferRect.MoveTo(neededRegion.GetBounds().TopLeft());
-        } else {
-          destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-          mustRemoveFrontBuffer = true;
-        }
-      } else {
-        destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
-      }
+      destBufferRect = ComputeBufferRect(neededRegion.GetBounds());
     }
 
     if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
 #if defined(MOZ_GFX_OPTIMIZE_MOBILE)
       mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
       if (!aLayer->GetParent() ||
           !aLayer->GetParent()->SupportsComponentAlphaChildren() ||
@@ -501,67 +495,49 @@ ContentClient::CalculateBufferForPaint(P
 
     // If we have an existing buffer, but the content type has changed or we
     // have transitioned into/out of component alpha, then we need to recreate it.
     bool needsComponentAlpha = (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA);
     bool backBufferChangedSurface = mBuffer &&
                                     (contentType != mBuffer->GetContentType() ||
                                      needsComponentAlpha != mBuffer->HaveBufferOnWhite());
     if (canKeepBufferContents && backBufferChangedSurface) {
-      // We cannot reuse the back buffer if the surface type or content type
-      // changed. We may have to also invalidate, but only if the front buffer
-      // also changed.
-      canReuseBuffer = false;
-    }
-    bool frontBufferChangedSurface = frontBuffer &&
-                                     (contentType != frontBuffer->GetContentType() ||
-                                      needsComponentAlpha != frontBuffer->HaveBufferOnWhite());
-    if (canKeepBufferContents && frontBufferChangedSurface) {
       // Restart the decision process; we won't re-enter since we guard on
       // being able to keep the buffer contents.
       canReuseBuffer = false;
       canKeepBufferContents = false;
       validRegion.SetEmpty();
-      continue;
     }
-
     break;
   }
 
   NS_ASSERTION(destBufferRect.Contains(neededRegion.GetBounds()),
                "Destination rect doesn't contain what we need to paint");
 
   BufferDecision dest;
   dest.mNeededRegion = Move(neededRegion);
   dest.mValidRegion = Move(validRegion);
   dest.mBufferRect = destBufferRect;
   dest.mBufferMode = mode;
   dest.mBufferContentType = contentType;
   dest.mCanReuseBuffer = canReuseBuffer;
   dest.mCanKeepBufferContents = canKeepBufferContents;
-  dest.mMustRemoveFrontBuffer = mustRemoveFrontBuffer;
   return dest;
 }
 
 bool
 ContentClient::ValidBufferSize(BufferSizePolicy aPolicy,
                                const gfx::IntSize& aBufferSize,
                                const gfx::IntSize& aVisibleBoundsSize)
 {
   return (aVisibleBoundsSize == aBufferSize ||
           (SizedToVisibleBounds != aPolicy &&
            aVisibleBoundsSize < aBufferSize));
 }
 
-RefPtr<RotatedBuffer>
-ContentClient::GetFrontBuffer() const
-{
-  return mBuffer;
-}
-
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 }
 
 // We pass a null pointer for the ContentClient Forwarder argument, which means
@@ -914,85 +890,96 @@ ContentClientDoubleBuffered::SwapBuffers
 
   mFrontAndBackBufferDiffer = true;
 }
 
 ContentClient::PaintState
 ContentClientDoubleBuffered::BeginPaint(PaintedLayer* aLayer,
                                         uint32_t aFlags)
 {
+  EnsureBackBufferIfFrontBuffer();
+
   mIsNewBuffer = false;
+
   if (!mFrontBuffer || !mBuffer) {
     mFrontAndBackBufferDiffer = false;
   }
 
-  return ContentClient::BeginPaint(aLayer, aFlags);
-}
+  if (mFrontAndBackBufferDiffer) {
+    if (mFrontBuffer->DidSelfCopy()) {
+      // We can't easily draw our front buffer into us, since we're going to be
+      // copying stuff around anyway it's easiest if we just move our situation
+      // to non-rotated while we're at it. If this situation occurs we'll have
+      // hit a self-copy path in PaintThebes before as well anyway.
+      gfx::IntRect backBufferRect = mBuffer->BufferRect();
+      backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
 
-RefPtr<RotatedBuffer>
-ContentClientDoubleBuffered::GetFrontBuffer() const
-{
-  return mFrontBuffer;
+      mBuffer->SetBufferRect(backBufferRect);
+      mBuffer->SetBufferRotation(IntPoint(0,0));
+    } else {
+      mBuffer->SetBufferRect(mFrontBuffer->BufferRect());
+      mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
+    }
+  }
+
+  return ContentClient::BeginPaint(aLayer, aFlags);
 }
 
 // Sync front/back buffers content
 // After executing, the new back buffer has the same (interesting) pixels as
 // the new front buffer, and mValidRegion et al. are correct wrt the new
 // back buffer (i.e. as they were for the old back buffer)
-void
-ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                                           CapturedBufferState* aPrepareState)
+Maybe<CapturedBufferState::Copy>
+ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
 {
   if (!mFrontAndBackBufferDiffer) {
     MOZ_ASSERT(!mFrontBuffer || !mFrontBuffer->DidSelfCopy(),
                "If the front buffer did a self copy then our front and back buffer must be different.");
-    return;
+    return Nothing();
   }
 
   MOZ_ASSERT(mFrontBuffer && mBuffer);
   if (!mFrontBuffer || !mBuffer) {
-    return;
+    return Nothing();
   }
 
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
                   this,
                   mFrontUpdatedRegion.GetBounds().x,
                   mFrontUpdatedRegion.GetBounds().y,
                   mFrontUpdatedRegion.GetBounds().Width(),
                   mFrontUpdatedRegion.GetBounds().Height()));
 
   mFrontAndBackBufferDiffer = false;
 
-  // Move the back buffer rect and rotation to the front buffer rect and rotation
-  // so that we can update the pixels that changed between frames
-  gfx::IntRect backBufferRect = mBuffer->BufferRect();
-  backBufferRect.MoveTo(mFrontBuffer->BufferRect().TopLeft());
-  mBuffer->SetBufferRect(backBufferRect);
-  mBuffer->SetBufferRotation(mFrontBuffer->BufferRotation());
-
-  // Calculate the region to update
   nsIntRegion updateRegion = mFrontUpdatedRegion;
   if (mFrontBuffer->DidSelfCopy()) {
-    // If we did an unrotate operation on the front buffer we might as well
-    // unrotate as well because we will be reading back the whole front buffer
-    mBuffer->SetBufferRotation(IntPoint(0,0));
-
     mFrontBuffer->ClearDidSelfCopy();
     updateRegion = mBuffer->BufferRect();
   }
 
   // No point in sync'ing what we are going to draw over anyway. And if there is
   // nothing to sync at all, there is nothing to do and we can go home early.
   updateRegion.Sub(updateRegion, aRegionToDraw);
   if (updateRegion.IsEmpty()) {
-    return;
+    return Nothing();
   }
 
-  MOZ_ASSERT(!aPrepareState->mBufferCopy);
-  aPrepareState->mBufferCopy = Some(CapturedBufferState::Copy {
+  return Some(CapturedBufferState::Copy {
     mFrontBuffer->ShallowCopy(),
     mBuffer->ShallowCopy(),
     updateRegion.GetBounds(),
   });
 }
 
+void
+ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
+{
+  if (!mBuffer && mFrontBuffer) {
+    mBuffer = CreateBufferInternal(mFrontBuffer->BufferRect(),
+                                   mFrontBuffer->GetFormat(),
+                                   mTextureFlags);
+    MOZ_ASSERT(mBuffer);
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -193,42 +193,40 @@ protected:
   struct BufferDecision {
     nsIntRegion mNeededRegion;
     nsIntRegion mValidRegion;
     gfx::IntRect mBufferRect;
     SurfaceMode mBufferMode;
     gfxContentType mBufferContentType;
     bool mCanReuseBuffer;
     bool mCanKeepBufferContents;
-    bool mMustRemoveFrontBuffer;
   };
 
   /**
    * Decide whether we can keep our current buffer and its contents,
    * and return a struct containing the regions to paint, invalidate,
    * the new buffer rect, surface mode, and content type.
    */
   BufferDecision CalculateBufferForPaint(PaintedLayer* aLayer,
                                          uint32_t aFlags);
 
   static bool ValidBufferSize(BufferSizePolicy aPolicy,
                               const gfx::IntSize& aBufferSize,
                               const gfx::IntSize& aVisibleBoundsSize);
 
-  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const;
-
   /**
    * Any actions that should be performed at the last moment before we begin
    * rendering the next frame. I.e., after we calculate what we will draw,
    * but before we rotate the buffer and possibly create new buffers.
    * aRegionToDraw is the region which is guaranteed to be overwritten when
    * drawing the next frame.
    */
-  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                             CapturedBufferState* aState) {}
+  virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) {
+    return Nothing();
+  }
 
   /**
    * Create a new rotated buffer for the specified content type, buffer rect,
    * and buffer flags.
    */
   virtual RefPtr<RotatedBuffer> CreateBuffer(gfxContentType aType,
                                              const gfx::IntRect& aRect,
                                              uint32_t aFlags) = 0;
@@ -361,27 +359,26 @@ public:
                     TextureDumpMode aCompress=TextureDumpMode::Compress) override;
 
   virtual void Clear() override;
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
   virtual PaintState BeginPaint(PaintedLayer* aLayer, uint32_t aFlags) override;
 
-  virtual RefPtr<RotatedBuffer> GetFrontBuffer() const override;
-
-  virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw,
-                             CapturedBufferState* aState) override;
+  virtual Maybe<CapturedBufferState::Copy> FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
   }
 
 private:
+  void EnsureBackBufferIfFrontBuffer();
+
   RefPtr<RemoteRotatedBuffer> mFrontBuffer;
   nsIntRegion mFrontUpdatedRegion;
   bool mFrontAndBackBufferDiffer;
 };
 
 /**
  * A single buffered ContentClientRemoteBuffer. We have a single
  * TextureClient/Host which we update and then send a message to the
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -480,18 +480,21 @@ WebRenderCommandBuilder::GenerateFallbac
 
   RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
 
   bool snap;
   nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
 
   // Blob images will only draw the visible area of the blob so we don't need to clip
   // them here and can just rely on the webrender clipping.
+  // TODO We also don't clip native themed widget to avoid over-invalidation during scrolling.
+  // it would be better to support a sort of straming/tiling scheme for large ones but the hope
+  // is that we should not have large native themed items.
   nsRect paintBounds = itemBounds;
-  if (useBlobImage) {
+  if (useBlobImage || aItem->MustPaintOnContentSide()) {
     paintBounds = itemBounds;
   } else {
     paintBounds = aItem->GetClippedBounds(aDisplayListBuilder);
   }
 
   // nsDisplayItem::Paint() may refer the variables that come from ComputeVisibility().
   // So we should call ComputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
   // uses mVisibleRegion in Paint() and mVisibleRegion is computed in
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "gfxUtils.h"
+#include "mozilla/Mutex.h"
 #include "mozilla/Range.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/InlineTranslator.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/RecordedEvent.h"
 #include "WebRenderTypes.h"
 #include "webrender_ffi.h"
 
@@ -46,34 +47,37 @@ namespace wr {
 struct FontTemplate {
   const uint8_t *mData;
   size_t mSize;
   uint32_t mIndex;
   const VecU8 *mVec;
   RefPtr<UnscaledFont> mUnscaledFont;
 };
 
+StaticMutex sFontDataTableLock;
 std::unordered_map<FontKey, FontTemplate> sFontDataTable;
 
 extern "C" {
 void
 AddFontData(WrFontKey aKey, const uint8_t *aData, size_t aSize, uint32_t aIndex, const ArcVecU8 *aVec) {
+  StaticMutexAutoLock lock(sFontDataTableLock);
   auto i = sFontDataTable.find(aKey);
   if (i == sFontDataTable.end()) {
     FontTemplate font;
     font.mData = aData;
     font.mSize = aSize;
     font.mIndex = aIndex;
     font.mVec = wr_add_ref_arc(aVec);
     sFontDataTable[aKey] = font;
   }
 }
 
 void
 AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
+  StaticMutexAutoLock lock(sFontDataTableLock);
   auto i = sFontDataTable.find(aKey);
   if (i == sFontDataTable.end()) {
     FontTemplate font;
     font.mData = nullptr;
     font.mSize = 0;
     font.mIndex = 0;
     font.mVec = nullptr;
 #ifdef XP_MACOSX
@@ -86,30 +90,36 @@ AddNativeFontHandle(WrFontKey aKey, void
     font.mUnscaledFont = new UnscaledFontFontconfig(reinterpret_cast<const char*>(aHandle), aIndex);
 #endif
     sFontDataTable[aKey] = font;
   }
 }
 
 void
 DeleteFontData(WrFontKey aKey) {
+  StaticMutexAutoLock lock(sFontDataTableLock);
   auto i = sFontDataTable.find(aKey);
   if (i != sFontDataTable.end()) {
     if (i->second.mVec) {
       wr_dec_ref_arc(i->second.mVec);
     }
     sFontDataTable.erase(i);
   }
 }
 }
 
 RefPtr<UnscaledFont>
 GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
-  MOZ_ASSERT(sFontDataTable.find(key) != sFontDataTable.end());
-  auto &data = sFontDataTable[key];
+  StaticMutexAutoLock lock(sFontDataTableLock);
+  auto i = sFontDataTable.find(key);
+  if (i == sFontDataTable.end()) {
+    gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << key.mHandle;
+    return nullptr;
+  }
+  auto &data = i->second;
   if (data.mUnscaledFont) {
     return data.mUnscaledFont;
   }
   MOZ_ASSERT(data.mData);
   FontType type =
 #ifdef XP_MACOSX
     FontType::MAC;
 #elif defined(XP_WIN)
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -467,24 +467,16 @@ typedef bool
  * Finalize obj, which the garbage collector has determined to be unreachable
  * from other live objects or from GC roots.  Obviously, finalizers must never
  * store a reference to obj.
  */
 typedef void
 (* JSFinalizeOp)(JSFreeOp* fop, JSObject* obj);
 
 /**
- * Finalizes external strings created by JS_NewExternalString. The finalizer
- * can be called off the main thread.
- */
-struct JSStringFinalizer {
-    void (*finalize)(const JSStringFinalizer* fin, char16_t* chars);
-};
-
-/**
  * Check whether v is an instance of obj.  Return false on error or exception,
  * true on success with true in *bp if v is an instance of obj, false in
  * *bp otherwise.
  */
 typedef bool
 (* JSHasInstanceOp)(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp,
                     bool* bp);
 
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -1,25 +1,46 @@
 /* -*- 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/. */
 
+/*
+ * High-level interface to the JS garbage collector.
+ */
+
 #ifndef js_GCAPI_h
 #define js_GCAPI_h
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Vector.h"
 
 #include "js/GCAnnotations.h"
-#include "js/HeapAPI.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 
+struct JSCompartment;
+struct JSContext;
+struct JSFreeOp;
+class JSObject;
+struct JSRuntime;
+class JSString;
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
+class JS_PUBLIC_API(JSTracer);
+
+#ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING
+#pragma GCC diagnostic pop
+#endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING
+
 namespace js {
 namespace gc {
 class GCRuntime;
 } // namespace gc
 namespace gcstats {
 struct Statistics;
 } // namespace gcstats
 } // namespace js
@@ -44,18 +65,282 @@ typedef enum JSGCMode {
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL = 0,
 
     /* Minimize GC triggers and release empty GC chunks right away. */
     GC_SHRINK = 1
 } JSGCInvocationKind;
 
+typedef enum JSGCParamKey {
+    /**
+     * Maximum nominal heap before last ditch GC.
+     *
+     * Soft limit on the number of bytes we are allowed to allocate in the GC
+     * heap. Attempts to allocate gcthings over this limit will return null and
+     * subsequently invoke the standard OOM machinery, independent of available
+     * physical memory.
+     *
+     * Pref: javascript.options.mem.max
+     * Default: 0xffffffff
+     */
+    JSGC_MAX_BYTES          = 0,
+
+    /**
+     * Initial value for the malloc bytes threshold.
+     *
+     * Pref: javascript.options.mem.high_water_mark
+     * Default: TuningDefaults::MaxMallocBytes
+     */
+    JSGC_MAX_MALLOC_BYTES   = 1,
+
+    /**
+     * Maximum size of the generational GC nurseries.
+     *
+     * Pref: javascript.options.mem.nursery.max_kb
+     * Default: JS::DefaultNurseryBytes
+     */
+    JSGC_MAX_NURSERY_BYTES  = 2,
+
+    /** Amount of bytes allocated by the GC. */
+    JSGC_BYTES = 3,
+
+    /** Number of times GC has been invoked. Includes both major and minor GC. */
+    JSGC_NUMBER = 4,
+
+    /**
+     * Select GC mode.
+     *
+     * See: JSGCMode in GCAPI.h
+     * prefs: javascript.options.mem.gc_per_zone and
+     *   javascript.options.mem.gc_incremental.
+     * Default: JSGC_MODE_INCREMENTAL
+     */
+    JSGC_MODE = 6,
+
+    /** Number of cached empty GC chunks. */
+    JSGC_UNUSED_CHUNKS = 7,
+
+    /** Total number of allocated GC chunks. */
+    JSGC_TOTAL_CHUNKS = 8,
+
+    /**
+     * Max milliseconds to spend in an incremental GC slice.
+     *
+     * Pref: javascript.options.mem.gc_incremental_slice_ms
+     * Default: DefaultTimeBudget.
+     */
+    JSGC_SLICE_TIME_BUDGET = 9,
+
+    /**
+     * Maximum size the GC mark stack can grow to.
+     *
+     * Pref: none
+     * Default: MarkStack::DefaultCapacity
+     */
+    JSGC_MARK_STACK_LIMIT = 10,
+
+    /**
+     * GCs less than this far apart in time will be considered 'high-frequency
+     * GCs'.
+     *
+     * See setGCLastBytes in jsgc.cpp.
+     *
+     * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms
+     * Default: HighFrequencyThresholdUsec
+     */
+    JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
+
+    /**
+     * Start of dynamic heap growth.
+     *
+     * Pref: javascript.options.mem.gc_high_frequency_low_limit_mb
+     * Default: HighFrequencyLowLimitBytes
+     */
+    JSGC_HIGH_FREQUENCY_LOW_LIMIT = 12,
+
+    /**
+     * End of dynamic heap growth.
+     *
+     * Pref: javascript.options.mem.gc_high_frequency_high_limit_mb
+     * Default: HighFrequencyHighLimitBytes
+     */
+    JSGC_HIGH_FREQUENCY_HIGH_LIMIT = 13,
+
+    /**
+     * Upper bound of heap growth.
+     *
+     * Pref: javascript.options.mem.gc_high_frequency_heap_growth_max
+     * Default: HighFrequencyHeapGrowthMax
+     */
+    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX = 14,
+
+    /**
+     * Lower bound of heap growth.
+     *
+     * Pref: javascript.options.mem.gc_high_frequency_heap_growth_min
+     * Default: HighFrequencyHeapGrowthMin
+     */
+    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN = 15,
+
+    /**
+     * Heap growth for low frequency GCs.
+     *
+     * Pref: javascript.options.mem.gc_low_frequency_heap_growth
+     * Default: LowFrequencyHeapGrowth
+     */
+    JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
+
+    /**
+     * If false, the heap growth factor is fixed at 3. If true, it is determined
+     * based on whether GCs are high- or low- frequency.
+     *
+     * Pref: javascript.options.mem.gc_dynamic_heap_growth
+     * Default: DynamicHeapGrowthEnabled
+     */
+    JSGC_DYNAMIC_HEAP_GROWTH = 17,
+
+    /**
+     * If true, high-frequency GCs will use a longer mark slice.
+     *
+     * Pref: javascript.options.mem.gc_dynamic_mark_slice
+     * Default: DynamicMarkSliceEnabled
+     */
+    JSGC_DYNAMIC_MARK_SLICE = 18,
+
+    /**
+     * Lower limit after which we limit the heap growth.
+     *
+     * The base value used to compute zone->threshold.gcTriggerBytes(). When
+     * usage.gcBytes() surpasses threshold.gcTriggerBytes() for a zone, the
+     * zone may be scheduled for a GC, depending on the exact circumstances.
+     *
+     * Pref: javascript.options.mem.gc_allocation_threshold_mb
+     * Default GCZoneAllocThresholdBase
+     */
+    JSGC_ALLOCATION_THRESHOLD = 19,
+
+    /**
+     * We try to keep at least this many unused chunks in the free chunk pool at
+     * all times, even after a shrinking GC.
+     *
+     * Pref: javascript.options.mem.gc_min_empty_chunk_count
+     * Default: MinEmptyChunkCount
+     */
+    JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
+
+    /**
+     * We never keep more than this many unused chunks in the free chunk
+     * pool.
+     *
+     * Pref: javascript.options.mem.gc_min_empty_chunk_count
+     * Default: MinEmptyChunkCount
+     */
+    JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
+
+    /**
+     * Whether compacting GC is enabled.
+     *
+     * Pref: javascript.options.mem.gc_compacting
+     * Default: CompactingEnabled
+     */
+    JSGC_COMPACTING_ENABLED = 23,
+
+    /**
+     * If true, painting can trigger IGC slices.
+     *
+     * Pref: javascript.options.mem.gc_refresh_frame_slices_enabled
+     * Default: RefreshFrameSlicesEnabled
+     */
+    JSGC_REFRESH_FRAME_SLICES_ENABLED = 24,
+
+    /**
+     * Factor for triggering a GC based on JSGC_ALLOCATION_THRESHOLD
+     *
+     * Default: ZoneAllocThresholdFactorDefault
+     * Pref: None
+     */
+    JSGC_ALLOCATION_THRESHOLD_FACTOR = 25,
+
+    /**
+     * Factor for triggering a GC based on JSGC_ALLOCATION_THRESHOLD.
+     * Used if another GC (in different zones) is already running.
+     *
+     * Default: ZoneAllocThresholdFactorAvoidInterruptDefault
+     * Pref: None
+     */
+    JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT = 26,
+} JSGCParamKey;
+
+/*
+ * Generic trace operation that calls JS::TraceEdge on each traceable thing's
+ * location reachable from data.
+ */
+typedef void
+(* JSTraceDataOp)(JSTracer* trc, void* data);
+
+typedef enum JSGCStatus {
+    JSGC_BEGIN,
+    JSGC_END
+} JSGCStatus;
+
+typedef void
+(* JSGCCallback)(JSContext* cx, JSGCStatus status, void* data);
+
+typedef void
+(* JSObjectsTenuredCallback)(JSContext* cx, void* data);
+
+typedef enum JSFinalizeStatus {
+    /**
+     * Called when preparing to sweep a group of zones, before anything has been
+     * swept.  The collector will not yield to the mutator before calling the
+     * callback with JSFINALIZE_GROUP_START status.
+     */
+    JSFINALIZE_GROUP_PREPARE,
+
+    /**
+     * Called after preparing to sweep a group of zones. Weak references to
+     * unmarked things have been removed at this point, but no GC things have
+     * been swept. The collector may yield to the mutator after this point.
+     */
+    JSFINALIZE_GROUP_START,
+
+    /**
+     * Called after sweeping a group of zones. All dead GC things have been
+     * swept at this point.
+     */
+    JSFINALIZE_GROUP_END,
+
+    /**
+     * Called at the end of collection when everything has been swept.
+     */
+    JSFINALIZE_COLLECTION_END
+} JSFinalizeStatus;
+
+typedef void
+(* JSFinalizeCallback)(JSFreeOp* fop, JSFinalizeStatus status, void* data);
+
+typedef void
+(* JSWeakPointerZonesCallback)(JSContext* cx, void* data);
+
+typedef void
+(* JSWeakPointerCompartmentCallback)(JSContext* cx, JSCompartment* comp, void* data);
+
+/**
+ * Finalizes external strings created by JS_NewExternalString. The finalizer
+ * can be called off the main thread.
+ */
+struct JSStringFinalizer {
+    void (*finalize)(const JSStringFinalizer* fin, char16_t* chars);
+};
+
 namespace JS {
 
+struct Zone;
+
 #define GCREASONS(D)                            \
     /* Reasons internal to the JS engine */     \
     D(API)                                      \
     D(EAGER_ALLOC_TRIGGER)                      \
     D(DESTROY_RUNTIME)                          \
     D(ROOTS_REMOVED)                            \
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
@@ -437,39 +722,16 @@ IsIncrementalGCInProgress(JSContext* cx)
 
 /**
  * Returns true while an incremental GC is ongoing, both when actively
  * collecting and between slices.
  */
 extern JS_PUBLIC_API(bool)
 IsIncrementalGCInProgress(JSRuntime* rt);
 
-/*
- * Returns true when writes to GC thing pointers (and reads from weak pointers)
- * must call an incremental barrier. This is generally only true when running
- * mutator code in-between GC slices. At other times, the barrier may be elided
- * for performance.
- */
-extern JS_PUBLIC_API(bool)
-IsIncrementalBarrierNeeded(JSContext* cx);
-
-/*
- * Notify the GC that a reference to a JSObject is about to be overwritten.
- * This method must be called if IsIncrementalBarrierNeeded.
- */
-extern JS_PUBLIC_API(void)
-IncrementalPreWriteBarrier(JSObject* obj);
-
-/*
- * Notify the GC that a weak reference to a GC thing has been read.
- * This method must be called if IsIncrementalBarrierNeeded.
- */
-extern JS_PUBLIC_API(void)
-IncrementalReadBarrier(GCCellPtr thing);
-
 /**
  * Returns true if the most recent GC ran incrementally.
  */
 extern JS_PUBLIC_API(bool)
 WasIncrementalGC(JSRuntime* rt);
 
 /*
  * Generational GC:
@@ -608,105 +870,181 @@ class JS_PUBLIC_API(AutoCheckCannotGC) :
 #else
 class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoRequireNoGC
 {
   public:
     explicit AutoCheckCannotGC(JSContext* cx = nullptr) {}
 } JS_HAZ_GC_INVALIDATED;
 #endif
 
-/**
- * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
- * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
- * if anything was unmarked.
- */
-extern JS_FRIEND_API(bool)
-UnmarkGrayGCThingRecursively(GCCellPtr thing);
-
-} /* namespace JS */
-
-namespace js {
-namespace gc {
-
-static MOZ_ALWAYS_INLINE void
-ExposeGCThingToActiveJS(JS::GCCellPtr thing)
-{
-    // GC things residing in the nursery cannot be gray: they have no mark bits.
-    // All live objects in the nursery are moved to tenured at the beginning of
-    // each GC slice, so the gray marker never sees nursery things.
-    if (IsInsideNursery(thing.asCell()))
-        return;
-
-    // There's nothing to do for permanent GC things that might be owned by
-    // another runtime.
-    if (thing.mayBeOwnedByOtherRuntime())
-        return;
-
-    if (IsIncrementalBarrierNeededOnTenuredGCThing(thing))
-        JS::IncrementalReadBarrier(thing);
-    else if (js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()))
-        JS::UnmarkGrayGCThingRecursively(thing);
-
-    MOZ_ASSERT(!js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()));
-}
-
-template <typename T>
-extern JS_PUBLIC_API(bool)
-EdgeNeedsSweepUnbarrieredSlow(T* thingp);
-
-static MOZ_ALWAYS_INLINE bool
-EdgeNeedsSweepUnbarriered(JSObject** objp)
-{
-    // This function does not handle updating nursery pointers. Raw JSObject
-    // pointers should be updated separately or replaced with
-    // JS::Heap<JSObject*> which handles this automatically.
-    MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
-    if (IsInsideNursery(reinterpret_cast<Cell*>(*objp)))
-        return false;
-
-    auto zone = JS::shadow::Zone::asShadowZone(detail::GetGCThingZone(uintptr_t(*objp)));
-    if (!zone->isGCSweepingOrCompacting())
-        return false;
-
-    return EdgeNeedsSweepUnbarrieredSlow(objp);
-}
-
-} /* namespace gc */
-} /* namespace js */
-
-namespace JS {
-
-/*
- * This should be called when an object that is marked gray is exposed to the JS
- * engine (by handing it to running JS code or writing it into live JS
- * data). During incremental GC, since the gray bits haven't been computed yet,
- * we conservatively mark the object black.
- */
-static MOZ_ALWAYS_INLINE void
-ExposeObjectToActiveJS(JSObject* obj)
-{
-    MOZ_ASSERT(obj);
-    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj));
-    js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
-}
-
-static MOZ_ALWAYS_INLINE void
-ExposeScriptToActiveJS(JSScript* script)
-{
-    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script));
-    js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
-}
-
 /*
  * Internal to Firefox.
  */
 extern JS_FRIEND_API(void)
 NotifyGCRootsRemoved(JSContext* cx);
 
 /*
  * Internal to Firefox.
  */
 extern JS_FRIEND_API(void)
 NotifyDidPaint(JSContext* cx);
 
 } /* namespace JS */
 
+/**
+ * Register externally maintained GC roots.
+ *
+ * traceOp: the trace operation. For each root the implementation should call
+ *          JS::TraceEdge whenever the root contains a traceable thing.
+ * data:    the data argument to pass to each invocation of traceOp.
+ */
+extern JS_PUBLIC_API(bool)
+JS_AddExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data);
+
+/** Undo a call to JS_AddExtraGCRootsTracer. */
+extern JS_PUBLIC_API(void)
+JS_RemoveExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data);
+
+extern JS_PUBLIC_API(void)
+JS_GC(JSContext* cx);
+
+extern JS_PUBLIC_API(void)
+JS_MaybeGC(JSContext* cx);
+
+extern JS_PUBLIC_API(void)
+JS_SetGCCallback(JSContext* cx, JSGCCallback cb, void* data);
+
+extern JS_PUBLIC_API(void)
+JS_SetObjectsTenuredCallback(JSContext* cx, JSObjectsTenuredCallback cb,
+                             void* data);
+
+extern JS_PUBLIC_API(bool)
+JS_AddFinalizeCallback(JSContext* cx, JSFinalizeCallback cb, void* data);
+
+extern JS_PUBLIC_API(void)
+JS_RemoveFinalizeCallback(JSContext* cx, JSFinalizeCallback cb);
+
+/*
+ * Weak pointers and garbage collection
+ *
+ * Weak pointers are by their nature not marked as part of garbage collection,
+ * but they may need to be updated in two cases after a GC:
+ *
+ *  1) Their referent was found not to be live and is about to be finalized
+ *  2) Their referent has been moved by a compacting GC
+ *
+ * To handle this, any part of the system that maintain weak pointers to
+ * JavaScript GC things must register a callback with
+ * JS_(Add,Remove)WeakPointer{ZoneGroup,Compartment}Callback(). This callback
+ * must then call JS_UpdateWeakPointerAfterGC() on all weak pointers it knows
+ * about.
+ *
+ * Since sweeping is incremental, we have several callbacks to avoid repeatedly
+ * having to visit all embedder structures. The WeakPointerZonesCallback is
+ * called once for each strongly connected group of zones, whereas the
+ * WeakPointerCompartmentCallback is called once for each compartment that is
+ * visited while sweeping. Structures that cannot contain references in more
+ * than one compartment should sweep the relevant per-compartment structures
+ * using the latter callback to minimizer per-slice overhead.
+ *
+ * The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
+ * referent is about to be finalized the pointer will be set to null. If the
+ * referent has been moved then the pointer will be updated to point to the new
+ * location.
+ *
+ * Callers of this method are responsible for updating any state that is
+ * dependent on the object's address. For example, if the object's address is
+ * used as a key in a hashtable, then the object must be removed and
+ * re-inserted with the correct hash.
+ */
+
+extern JS_PUBLIC_API(bool)
+JS_AddWeakPointerZonesCallback(JSContext* cx, JSWeakPointerZonesCallback cb, void* data);
+
+extern JS_PUBLIC_API(void)
+JS_RemoveWeakPointerZonesCallback(JSContext* cx, JSWeakPointerZonesCallback cb);
+
+extern JS_PUBLIC_API(bool)
+JS_AddWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb,
+                                     void* data);
+
+extern JS_PUBLIC_API(void)
+JS_RemoveWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb);
+
+namespace JS {
+template <typename T> class Heap;
+}
+
+extern JS_PUBLIC_API(void)
+JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject*>* objp);
+
+extern JS_PUBLIC_API(void)
+JS_UpdateWeakPointerAfterGCUnbarriered(JSObject** objp);
+
+extern JS_PUBLIC_API(void)
+JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value);
+
+extern JS_PUBLIC_API(void)
+JS_ResetGCParameter(JSContext* cx, JSGCParamKey key);
+
+extern JS_PUBLIC_API(uint32_t)
+JS_GetGCParameter(JSContext* cx, JSGCParamKey key);
+
+extern JS_PUBLIC_API(void)
+JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem);
+
+/**
+ * Create a new JSString whose chars member refers to external memory, i.e.,
+ * memory requiring application-specific finalization.
+ */
+extern JS_PUBLIC_API(JSString*)
+JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length,
+                     const JSStringFinalizer* fin);
+
+/**
+ * Create a new JSString whose chars member may refer to external memory.
+ * If a new external string is allocated, |*allocatedExternal| is set to true.
+ * Otherwise the returned string is either not an external string or an
+ * external string allocated by a previous call and |*allocatedExternal| is set
+ * to false. If |*allocatedExternal| is false, |fin| won't be called.
+ */
+extern JS_PUBLIC_API(JSString*)
+JS_NewMaybeExternalString(JSContext* cx, const char16_t* chars, size_t length,
+                          const JSStringFinalizer* fin, bool* allocatedExternal);
+
+/**
+ * Return whether 'str' was created with JS_NewExternalString or
+ * JS_NewExternalStringWithClosure.
+ */
+extern JS_PUBLIC_API(bool)
+JS_IsExternalString(JSString* str);
+
+/**
+ * Return the 'fin' arg passed to JS_NewExternalString.
+ */
+extern JS_PUBLIC_API(const JSStringFinalizer*)
+JS_GetExternalStringFinalizer(JSString* str);
+
+namespace JS {
+
+extern JS_PUBLIC_API(bool)
+IsIdleGCTaskNeeded(JSRuntime* rt);
+
+extern JS_PUBLIC_API(void)
+RunIdleTimeGCTask(JSRuntime* rt);
+
+} // namespace JS
+
+namespace js {
+namespace gc {
+
+/**
+ * Create an object providing access to the garbage collector's internal notion
+ * of the current state of memory (both GC heap memory and GCthing-controlled
+ * malloc memory.
+ */
+extern JS_PUBLIC_API(JSObject*)
+NewMemoryInfoObject(JSContext* cx);
+
+} /* namespace gc */
+} /* namespace js */
+
 #endif /* js_GCAPI_h */
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -103,17 +103,29 @@ AssertGCThingHasType(js::gc::Cell* cell,
 #endif
 
 MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
 
 } /* namespace gc */
 } /* namespace js */
 
 namespace JS {
-struct Zone;
+
+/*
+ * This list enumerates the different types of conceptual stacks we have in
+ * SpiderMonkey. In reality, they all share the C stack, but we allow different
+ * stack limits depending on the type of code running.
+ */
+enum StackKind
+{
+    StackForSystemCode,      // C++, such as the GC, running on behalf of the VM.
+    StackForTrustedScript,   // Script running with trusted principals.
+    StackForUntrustedScript, // Script running with untrusted principals.
+    StackKindCount
+};
 
 /*
  * Default size for the generational nursery in bytes.
  * This is the initial nursery size, when running in the browser this is
  * updated by JS_SetGCParameter().
  */
 const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
 
@@ -462,17 +474,48 @@ GCThingIsMarkedGray(GCCellPtr thing)
     if (thing.mayBeOwnedByOtherRuntime())
         return false;
     return js::gc::detail::CellIsMarkedGrayIfKnown(thing.asCell());
 }
 
 extern JS_PUBLIC_API(JS::TraceKind)
 GCThingTraceKind(void* thing);
 
-} /* namespace JS */
+/*
+ * Returns true when writes to GC thing pointers (and reads from weak pointers)
+ * must call an incremental barrier. This is generally only true when running
+ * mutator code in-between GC slices. At other times, the barrier may be elided
+ * for performance.
+ */
+extern JS_PUBLIC_API(bool)
+IsIncrementalBarrierNeeded(JSContext* cx);
+
+/*
+ * Notify the GC that a reference to a JSObject is about to be overwritten.
+ * This method must be called if IsIncrementalBarrierNeeded.
+ */
+extern JS_PUBLIC_API(void)
+IncrementalPreWriteBarrier(JSObject* obj);
+
+/*
+ * Notify the GC that a weak reference to a GC thing has been read.
+ * This method must be called if IsIncrementalBarrierNeeded.
+ */
+extern JS_PUBLIC_API(void)
+IncrementalReadBarrier(GCCellPtr thing);
+
+/**
+ * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
+ * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
+ * if anything was unmarked.
+ */
+extern JS_FRIEND_API(bool)
+UnmarkGrayGCThingRecursively(GCCellPtr thing);
+
+} // namespace JS
 
 namespace js {
 namespace gc {
 
 static MOZ_ALWAYS_INLINE bool
 IsIncrementalBarrierNeededOnTenuredGCThing(const JS::GCCellPtr thing)
 {
     MOZ_ASSERT(thing);
@@ -482,20 +525,80 @@ IsIncrementalBarrierNeededOnTenuredGCThi
     // called while we are tracing the heap, e.g. during memory reporting
     // (see bug 1313318).
     MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
 
     JS::Zone* zone = JS::GetTenuredGCThingZone(thing);
     return JS::shadow::Zone::asShadowZone(zone)->needsIncrementalBarrier();
 }
 
-/**
- * Create an object providing access to the garbage collector's internal notion
- * of the current state of memory (both GC heap memory and GCthing-controlled
- * malloc memory.
+static MOZ_ALWAYS_INLINE void
+ExposeGCThingToActiveJS(JS::GCCellPtr thing)
+{
+    // GC things residing in the nursery cannot be gray: they have no mark bits.
+    // All live objects in the nursery are moved to tenured at the beginning of
+    // each GC slice, so the gray marker never sees nursery things.
+    if (IsInsideNursery(thing.asCell()))
+        return;
+
+    // There's nothing to do for permanent GC things that might be owned by
+    // another runtime.
+    if (thing.mayBeOwnedByOtherRuntime())
+        return;
+
+    if (IsIncrementalBarrierNeededOnTenuredGCThing(thing))
+        JS::IncrementalReadBarrier(thing);
+    else if (js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()))
+        JS::UnmarkGrayGCThingRecursively(thing);
+
+    MOZ_ASSERT(!js::gc::detail::TenuredCellIsMarkedGray(thing.asCell()));
+}
+
+template <typename T>
+extern JS_PUBLIC_API(bool)
+EdgeNeedsSweepUnbarrieredSlow(T* thingp);
+
+static MOZ_ALWAYS_INLINE bool
+EdgeNeedsSweepUnbarriered(JSObject** objp)
+{
+    // This function does not handle updating nursery pointers. Raw JSObject
+    // pointers should be updated separately or replaced with
+    // JS::Heap<JSObject*> which handles this automatically.
+    MOZ_ASSERT(!JS::CurrentThreadIsHeapMinorCollecting());
+    if (IsInsideNursery(reinterpret_cast<Cell*>(*objp)))
+        return false;
+
+    auto zone = JS::shadow::Zone::asShadowZone(detail::GetGCThingZone(uintptr_t(*objp)));
+    if (!zone->isGCSweepingOrCompacting())
+        return false;
+
+    return EdgeNeedsSweepUnbarrieredSlow(objp);
+}
+
+} // namespace gc
+} // namesapce js
+
+namespace JS {
+
+/*
+ * This should be called when an object that is marked gray is exposed to the JS
+ * engine (by handing it to running JS code or writing it into live JS
+ * data). During incremental GC, since the gray bits haven't been computed yet,
+ * we conservatively mark the object black.
  */
-extern JS_PUBLIC_API(JSObject*)
-NewMemoryInfoObject(JSContext* cx);
+static MOZ_ALWAYS_INLINE void
+ExposeObjectToActiveJS(JSObject* obj)
+{
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&obj));
+    js::gc::ExposeGCThingToActiveJS(GCCellPtr(obj));
+}
 
-} /* namespace gc */
-} /* namespace js */
+static MOZ_ALWAYS_INLINE void
+ExposeScriptToActiveJS(JSScript* script)
+{
+    MOZ_ASSERT(!js::gc::EdgeNeedsSweepUnbarrieredSlow(&script));
+    js::gc::ExposeGCThingToActiveJS(GCCellPtr(script));
+}
+
+} /* namespace JS */
 
 #endif /* js_HeapAPI_h */
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -14,17 +14,16 @@
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #include <type_traits>
 
 #include "jspubtd.h"
 
 #include "js/GCAnnotations.h"
-#include "js/GCAPI.h"
 #include "js/GCPolicyAPI.h"
 #include "js/HeapAPI.h"
 #include "js/TypeDecls.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 
 /*
  * Moving GC Stack Rooting
@@ -766,16 +765,134 @@ class alignas(8) DispatchWrapper
         wrapper->tracer(trc, &wrapper->storage, name);
     }
 };
 
 } /* namespace js */
 
 namespace JS {
 
+class JS_PUBLIC_API(AutoGCRooter);
+
+// Our instantiations of Rooted<void*> and PersistentRooted<void*> require an
+// instantiation of MapTypeToRootKind.
+template <>
+struct MapTypeToRootKind<void*> {
+    static const RootKind kind = RootKind::Traceable;
+};
+
+using RootedListHeads = mozilla::EnumeratedArray<RootKind, RootKind::Limit,
+                                                 Rooted<void*>*>;
+
+// Superclass of JSContext which can be used for rooting data in use by the
+// current thread but that does not provide all the functions of a JSContext.
+class RootingContext
+{
+    // Stack GC roots for Rooted GC heap pointers.
+    RootedListHeads stackRoots_;
+    template <typename T> friend class JS::Rooted;
+
+    // Stack GC roots for AutoFooRooter classes.
+    JS::AutoGCRooter* autoGCRooters_;
+    friend class JS::AutoGCRooter;
+
+  public:
+    RootingContext();
+
+    void traceStackRoots(JSTracer* trc);
+    void checkNoGCRooters();
+
+  protected:
+    // The remaining members in this class should only be accessed through
+    // JSContext pointers. They are unrelated to rooting and are in place so
+    // that inlined API functions can directly access the data.
+
+    /* The current compartment. */
+    JSCompartment*      compartment_;
+
+    /* The current zone. */
+    JS::Zone*           zone_;
+
+  public:
+    /* Limit pointer for checking native stack consumption. */
+    uintptr_t nativeStackLimit[StackKindCount];
+
+    static const RootingContext* get(const JSContext* cx) {
+        return reinterpret_cast<const RootingContext*>(cx);
+    }
+
+    static RootingContext* get(JSContext* cx) {
+        return reinterpret_cast<RootingContext*>(cx);
+    }
+
+    friend JSCompartment* js::GetContextCompartment(const JSContext* cx);
+    friend JS::Zone* js::GetContextZone(const JSContext* cx);
+};
+
+class JS_PUBLIC_API(AutoGCRooter)
+{
+  public:
+    AutoGCRooter(JSContext* cx, ptrdiff_t tag)
+      : AutoGCRooter(JS::RootingContext::get(cx), tag)
+    {}
+    AutoGCRooter(JS::RootingContext* cx, ptrdiff_t tag)
+      : down(cx->autoGCRooters_),
+        tag_(tag),
+        stackTop(&cx->autoGCRooters_)
+    {
+        MOZ_ASSERT(this != *stackTop);
+        *stackTop = this;
+    }
+
+    ~AutoGCRooter() {
+        MOZ_ASSERT(this == *stackTop);
+        *stackTop = down;
+    }
+
+    /* Implemented in gc/RootMarking.cpp. */
+    inline void trace(JSTracer* trc);
+    static void traceAll(const js::CooperatingContext& target, JSTracer* trc);
+    static void traceAllWrappers(const js::CooperatingContext& target, JSTracer* trc);
+
+  protected:
+    AutoGCRooter * const down;
+
+    /*
+     * Discriminates actual subclass of this being used.  If non-negative, the
+     * subclass roots an array of values of the length stored in this field.
+     * If negative, meaning is indicated by the corresponding value in the enum
+     * below.  Any other negative value indicates some deeper problem such as
+     * memory corruption.
+     */
+    ptrdiff_t tag_;
+
+    enum {
+        VALARRAY =     -2, /* js::AutoValueArray */
+        PARSER =       -3, /* js::frontend::Parser */
+        VALVECTOR =   -10, /* js::AutoValueVector */
+        IDVECTOR =    -11, /* js::AutoIdVector */
+        OBJVECTOR =   -14, /* js::AutoObjectVector */
+        IONMASM =     -19, /* js::jit::MacroAssembler */
+        WRAPVECTOR =  -20, /* js::AutoWrapperVector */
+        WRAPPER =     -21, /* js::AutoWrapperRooter */
+        CUSTOM =      -26  /* js::CustomAutoRooter */
+    };
+
+    static ptrdiff_t GetTag(const Value& value) { return VALVECTOR; }
+    static ptrdiff_t GetTag(const jsid& id) { return IDVECTOR; }
+    static ptrdiff_t GetTag(JSObject* obj) { return OBJVECTOR; }
+
+  private:
+    AutoGCRooter ** const stackTop;
+
+    /* No copy or assignment semantics. */
+    AutoGCRooter(AutoGCRooter& ida) = delete;
+    void operator=(AutoGCRooter& ida) = delete;
+};
+
 namespace detail {
 
 /*
  * For pointer types, the TraceKind for tracing is based on the list it is
  * in (selected via MapTypeToRootKind), so no additional storage is
  * required here. Non-pointer types, however, share the same list, so the
  * function to call for tracing is stored adjacent to the struct. Since C++
  * cannot templatize on storage class, this is implemented via the wrapper
@@ -869,16 +986,38 @@ class MOZ_RAII Rooted : public js::Roote
 
     Rooted(const Rooted&) = delete;
 } JS_HAZ_ROOTED;
 
 } /* namespace JS */
 
 namespace js {
 
+/*
+ * Inlinable accessors for JSContext.
+ *
+ * - These must not be available on the more restricted superclasses of
+ *   JSContext, so we can't simply define them on RootingContext.
+ *
+ * - They're perfectly ordinary JSContext functionality, so ought to be
+ *   usable without resorting to jsfriendapi.h, and when JSContext is an
+ *   incomplete type.
+ */
+inline JSCompartment*
+GetContextCompartment(const JSContext* cx)
+{
+    return JS::RootingContext::get(cx)->compartment_;
+}
+
+inline JS::Zone*
+GetContextZone(const JSContext* cx)
+{
+    return JS::RootingContext::get(cx)->zone_;
+}
+
 /**
  * Augment the generic Rooted<T> interface when T = JSObject* with
  * class-querying and downcasting operations.
  *
  * Given a Rooted<JSObject*> obj, one can view
  *   Handle<StringObject*> h = obj.as<StringObject*>();
  * as an optimization of
  *   Rooted<StringObject*> rooted(cx, &obj->as<StringObject*>());
--- a/js/src/builtin/Intl.h
+++ b/js/src/builtin/Intl.h
@@ -10,17 +10,16 @@
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jsalloc.h"
 #include "NamespaceImports.h"
 
 #include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
-#include "js/GCAPI.h"
 #include "js/GCHashTable.h"
 #include "vm/NativeObject.h"
 
 class JSLinearString;
 
 /*
  * The Intl module specified by standard ECMA-402,
  * ECMAScript Internationalization API Specification.
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -21,17 +21,16 @@
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
-#include "js/GCAPI.h"
 #include "js/Value.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
--- a/js/src/gc/ArenaList.h
+++ b/js/src/gc/ArenaList.h
@@ -7,17 +7,16 @@
 /*
  * GC-internal definitions of ArenaList and associated heap data structures.
  */
 
 #ifndef gc_ArenaList_h
 #define gc_ArenaList_h
 
 #include "gc/AllocKind.h"
-#include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "threading/ProtectedData.h"
 
 namespace JS {
 
 struct Zone;
 
 } /* namespace JS */
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -10,18 +10,16 @@
 
 #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 {
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -6,17 +6,16 @@
 
 #ifndef gc_GCRuntime_h
 #define gc_GCRuntime_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumSet.h"
 #include "mozilla/Maybe.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"
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -20,17 +20,16 @@
 #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"
 
 struct JSRuntime;
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -8,17 +8,16 @@
 #include "gc/Nursery-inl.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Move.h"
 #include "mozilla/Unused.h"
 
 #include "jscompartment.h"
-#include "jsfriendapi.h"
 #include "jsutil.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Memory.h"
 #include "jit/JitFrames.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #if defined(DEBUG)
@@ -546,18 +545,21 @@ js::Nursery::renderProfileJSON(JSONPrint
     json.beginObject();
 
     json.property("status", "complete");
 
     json.property("reason", JS::gcreason::ExplainReason(previousGC.reason));
     json.property("bytes_tenured", previousGC.tenuredBytes);
     json.property("bytes_used", previousGC.nurseryUsedBytes);
     json.property("cur_capacity", previousGC.nurseryCapacity);
-    json.property("new_capacity", spaceToEnd());
-    json.property("lazy_capacity", allocatedChunkCount() * ChunkSize);
+    const size_t newCapacity = spaceToEnd(maxChunkCount());
+    if (newCapacity != previousGC.nurseryCapacity)
+        json.property("new_capacity", newCapacity);
+    if (previousGC.nurseryLazyCapacity != previousGC.nurseryCapacity)
+        json.property("lazy_capacity", previousGC.nurseryLazyCapacity);
     if (!timeInChunkAlloc_.IsZero())
         json.property("chunk_alloc_us", timeInChunkAlloc_, json.MICROSECONDS);
 
     json.beginObjectProperty("phase_times");
 
 #define EXTRACT_NAME(name, text) #name,
     static const char* names[] = {
 FOR_EACH_NURSERY_PROFILE_TIME(EXTRACT_NAME)
@@ -670,17 +672,18 @@ js::Nursery::collect(JS::gcreason::Reaso
     JS::AutoSuppressGCAnalysis nogc;
 
     TenureCountCache tenureCounts;
     previousGC.reason = JS::gcreason::NO_REASON;
     if (!isEmpty()) {
         doCollection(reason, tenureCounts);
     } else {
         previousGC.nurseryUsedBytes = 0;
-        previousGC.nurseryCapacity = spaceToEnd();
+        previousGC.nurseryCapacity = spaceToEnd(maxChunkCount());
+        previousGC.nurseryLazyCapacity = spaceToEnd(allocatedChunkCount());
         previousGC.tenuredBytes = 0;
     }
 
     // Resize the nursery.
     maybeResizeNursery(reason);
 
     // If we are promoting the nursery, or exhausted the store buffer with
     // pointers to nursery things, which will force a collection well before
@@ -759,17 +762,17 @@ js::Nursery::doCollection(JS::gcreason::
 {
     JSRuntime* rt = runtime();
     AutoTraceSession session(rt, JS::HeapState::MinorCollecting);
     AutoSetThreadIsPerformingGC performingGC;
     AutoStopVerifyingBarriers av(rt, false);
     AutoDisableProxyCheck disableStrictProxyChecking;
     mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;
 
-    const size_t initialNurseryCapacity = spaceToEnd();
+    const size_t initialNurseryCapacity = spaceToEnd(maxChunkCount());
     const size_t initialNurseryUsedBytes = initialNurseryCapacity - freeSpace();
 
     // Move objects pointed to by roots from the nursery to the major heap.
     TenuringTracer mover(rt, this);
 
     // Mark the store buffer. This must happen first.
     StoreBuffer& sb = runtime()->gc.storeBuffer();
 
@@ -858,16 +861,17 @@ js::Nursery::doCollection(JS::gcreason::
 #ifdef JS_GC_ZEAL
     if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
         CheckHashTablesAfterMovingGC(rt);
 #endif
     endProfile(ProfileKey::CheckHashTables);
 
     previousGC.reason = reason;
     previousGC.nurseryCapacity = initialNurseryCapacity;
+    previousGC.nurseryLazyCapacity = spaceToEnd(allocatedChunkCount());
     previousGC.nurseryUsedBytes = initialNurseryUsedBytes;
     previousGC.tenuredBytes = mover.tenuredSize;
 }
 
 void
 js::Nursery::FreeMallocedBuffersTask::transferBuffersToFree(MallocedBuffersSet& buffersToFree,
                                                             const AutoLockHelperThreadState& lock)
 {
@@ -961,19 +965,19 @@ js::Nursery::clear()
         setCurrentChunk(0);
     }
 
     /* Set current start position for isEmpty checks. */
     setStartPosition();
 }
 
 size_t
-js::Nursery::spaceToEnd() const
+js::Nursery::spaceToEnd(unsigned chunkCount) const
 {
-    unsigned lastChunk = maxChunkCount() - 1;
+    unsigned lastChunk = chunkCount - 1;
 
     MOZ_ASSERT(lastChunk >= currentStartChunk_);
     MOZ_ASSERT(currentStartPosition_ - chunk(currentStartChunk_).start() <= NurseryChunkUsableSize);
 
     size_t bytes = (chunk(currentStartChunk_).end() - currentStartPosition_) +
                    ((lastChunk - currentStartChunk_) * NurseryChunkUsableSize);
 
     MOZ_ASSERT(bytes <= maxChunkCount() * NurseryChunkUsableSize);
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -248,17 +248,20 @@ class Nursery
         size_t total = 0;
         for (MallocedBuffersSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront())
             total += mallocSizeOf(r.front());
         total += mallocedBuffers.sizeOfExcludingThis(mallocSizeOf);
         return total;
     }
 
     // The number of bytes from the start position to the end of the nursery.
-    size_t spaceToEnd() const;
+    // pass maxChunkCount(), allocatedChunkCount() or chunkCountLimit()
+    // to calculate the nursery size, current lazy-allocated size or nursery
+    // limit respectively.
+    size_t spaceToEnd(unsigned chunkCount) const;
 
     // Free space remaining, not counting chunk trailers.
     MOZ_ALWAYS_INLINE size_t freeSpace() const {
         MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize);
         return (currentEnd_ - position_) +
                (maxChunkCount() - currentChunk_ - 1) * NurseryChunkUsableSize;
     }
 
@@ -372,16 +375,17 @@ class Nursery
 
     /*
      * This data is initialised only if the nursery is enabled and after at
      * least one call to Nursery::collect()
      */
     struct {
         JS::gcreason::Reason reason;
         size_t nurseryCapacity;
+        size_t nurseryLazyCapacity;
         size_t nurseryUsedBytes;
         size_t tenuredBytes;
     } previousGC;
 
     /*
      * Calculate the promotion rate of the most recent minor GC.
      * The valid_for_tenuring parameter is used to return whether this
      * promotion rate is accurate enough (the nursery was full enough) to be
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -588,74 +588,91 @@ Statistics::renderJsonMessage(uint64_t t
     json.endObject();
 
     return UniqueChars(printer.release());
 }
 
 void
 Statistics::formatJsonDescription(uint64_t timestamp, JSONPrinter& json) const
 {
+    // If you change JSON properties here, please update:
+    // Telemetry ping code: toolkit/components/telemetry/GCTelemetry.jsm
+    // Telemetry documentation: toolkit/components/telemetry/docs/data/main-ping.rst
+    // Telemetry tests: toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
+    // Perf.html: https://github.com/devtools-html/perf.html
+
     json.property("timestamp", timestamp);
 
     TimeDuration total, longest;
     gcDuration(&total, &longest);
     json.property("max_pause", longest, JSONPrinter::MILLISECONDS);
     json.property("total_time", total, JSONPrinter::MILLISECONDS);
-
+    // We might be able to omit reason if perf.html was able to retrive it
+    // from the first slice.  But it doesn't do this yet.
     json.property("reason", ExplainReason(slices_[0].reason));
     json.property("zones_collected", zoneStats.collectedZoneCount);
     json.property("total_zones", zoneStats.zoneCount);
     json.property("total_compartments", zoneStats.compartmentCount);
-    json.property("minor_gcs", counts[STAT_MINOR_GC]);
-    json.property("store_buffer_overflows", counts[STAT_STOREBUFFER_OVERFLOW]);
+    json.property("minor_gcs", getCount(STAT_MINOR_GC));
+    uint32_t storebufferOverflows = getCount(STAT_STOREBUFFER_OVERFLOW);
+    if (storebufferOverflows)
+        json.property("store_buffer_overflows", storebufferOverflows);
     json.property("slices", slices_.length());
 
     const double mmu20 = computeMMU(TimeDuration::FromMilliseconds(20));
     const double mmu50 = computeMMU(TimeDuration::FromMilliseconds(50));
     json.property("mmu_20ms", int(mmu20 * 100));
     json.property("mmu_50ms", int(mmu50 * 100));
 
     TimeDuration sccTotal, sccLongest;
     sccDurations(&sccTotal, &sccLongest);
     json.property("scc_sweep_total", sccTotal, JSONPrinter::MILLISECONDS);
     json.property("scc_sweep_max_pause", sccLongest, JSONPrinter::MILLISECONDS);
 
-    json.property("nonincremental_reason", ExplainAbortReason(nonincrementalReason_));
-    json.property("allocated", uint64_t(preBytes)/1024/1024);
+    if (nonincrementalReason_ != AbortReason::None)
+        json.property("nonincremental_reason", ExplainAbortReason(nonincrementalReason_));
     json.property("allocated_bytes", preBytes);
-    json.property("added_chunks", getCount(STAT_NEW_CHUNK));
-    json.property("removed_chunks", getCount(STAT_DESTROY_CHUNK));
+    uint32_t addedChunks = getCount(STAT_NEW_CHUNK);
+    if (addedChunks)
+        json.property("added_chunks", addedChunks);
+    uint32_t removedChunks = getCount(STAT_DESTROY_CHUNK);
+    if (removedChunks)
+        json.property("removed_chunks", removedChunks);
     json.property("major_gc_number", startingMajorGCNumber);
     json.property("minor_gc_number", startingMinorGCNumber);
     json.property("slice_number", startingSliceNumber);
 }
 
 void
 Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice, JSONPrinter& json) const
 {
-    TimeDuration when = slice.start - slices_[0].start;
+    // If you change JSON properties here, please update:
+    // Telemetry ping code: toolkit/components/telemetry/GCTelemetry.jsm
+    // Telemetry documentation: toolkit/components/telemetry/docs/data/main-ping.rst
+    // Telemetry tests: toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
+    // Perf.html: https://github.com/devtools-html/perf.html
     char budgetDescription[200];
     slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
     TimeStamp originTime = TimeStamp::ProcessCreation();
 
     json.property("slice", i);
     json.property("pause", slice.duration(), JSONPrinter::MILLISECONDS);
-    json.property("when", when, JSONPrinter::MILLISECONDS);
     json.property("reason", ExplainReason(slice.reason));
     json.property("initial_state", gc::StateName(slice.initialState));
     json.property("final_state", gc::StateName(slice.finalState));
     json.property("budget", budgetDescription);
     json.property("major_gc_number", startingMajorGCNumber);
     if (thresholdTriggered) {
         json.floatProperty("trigger_amount", triggerAmount, 0);
         json.floatProperty("trigger_threshold", triggerThreshold, 0);
     }
-    json.property("page_faults", int64_t(slice.endFaults - slice.startFaults));
+    int64_t numFaults = slice.endFaults - slice.startFaults;
+    if (numFaults != 0)
+        json.property("page_faults", numFaults);
     json.property("start_timestamp", slice.start - originTime, JSONPrinter::SECONDS);
-    json.property("end_timestamp", slice.end - originTime, JSONPrinter::SECONDS);
 }
 
 void
 Statistics::formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes, JSONPrinter& json) const
 {
     for (auto phase : AllPhases()) {
         TimeDuration ownTime = phaseTimes[phase];
         if (!ownTime.IsZero())
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -14,17 +14,16 @@
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsalloc.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 {
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -3,17 +3,16 @@
  * 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/Tracer.h"
 
 #include "mozilla/DebugOnly.h"
 
-#include "jsapi.h"
 #include "jsfun.h"
 #include "jsprf.h"
 #include "jsscript.h"
 #include "jsutil.h"
 #include "NamespaceImports.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Marking.h"
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Sprintf.h"
 
 #include "jscntxt.h"
 #include "jsprf.h"
 
 #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;
--- a/js/src/jit-test/tests/coverage/simple.js
+++ b/js/src/jit-test/tests/coverage/simple.js
@@ -96,73 +96,88 @@ checkLcov(function () { //FN:$,top-level
   }
   f();              //DA:$,1
   //FNF:2
   //FNH:2
   //LF:2
   //LH:2
 });
 
+checkLcov(function () { ','.split(','); //FN:$,top-level //FNDA:1,% //DA:$,1
+  //FNF:1
+  //FNH:1
+  //LF:1
+  //LH:1
+});
+
+checkLcov(function () { function f() { ','.split(','); } //FN:$,top-level //FNDA:1,% //FN:$,f //FNDA:1,f //DA:$,1
+  f(); //DA:$,1
+  //FNF:2
+  //FNH:2
+  //LF:2
+  //LH:2
+});
+
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  if (l.length == 3)      //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,-
+  if (l.length == 3)      //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0
     l.push('');           //DA:$,0
   l.pop();                //DA:$,1
   //FNF:1
   //FNH:1
   //LF:4
   //LH:3
   //BRF:2
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
     l.push('');           //DA:$,1
   l.pop();                //DA:$,1
   //FNF:1
   //FNH:1
   //LF:4
   //LH:4
   //BRF:2
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  if (l.length == 3)      //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,-
+  if (l.length == 3)      //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0
     l.push('');           //DA:$,0
   else
     l.pop();              //DA:$,1
   //FNF:1
   //FNH:1
   //LF:4
   //LH:3
   //BRF:2
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
     l.push('');           //DA:$,1
   else
     l.pop();              //DA:$,0
   //FNF:1
   //FNH:1
   //LF:4
   //LH:3
   //BRF:2
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+  if (l.length == 2)      //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
     l.push('');           //DA:$,1
   else {
     if (l.length == 1)    //DA:$,0 //BRDA:$,1,0,- //BRDA:$,1,1,-
       l.pop();            //DA:$,0
   }
   //FNF:1
   //FNH:1
   //LF:5
@@ -188,17 +203,17 @@ checkLcov(function () { //FN:$,top-level
   f(5);           //DA:$,1
   //FNF:2
   //FNH:2
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   try {                     //DA:$,1
     var l = ",".split(','); //DA:$,1
-    if (l.length == 2) {    //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+    if (l.length == 2) {    //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
       l.push('');           //DA:$,1
       throw l;              //DA:$,1
     }
     l.pop();                //DA:$,0
   } catch (x) {             //DA:$,1
     x.pop();                //DA:$,1
   }
   //FNF:1
@@ -208,17 +223,17 @@ checkLcov(function () { //FN:$,top-level
   //BRF:2
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(',');   //DA:$,1
   try {                     //DA:$,1
     try {                   //DA:$,1
-      if (l.length == 2) {  //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+      if (l.length == 2) {  //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
         l.push('');         //DA:$,1
         throw l;            //DA:$,1
       }
       l.pop();              //DA:$,0
     } finally {             //DA:$,1
       l.pop();              //DA:$,1
     }
   } catch (x) {             //DA:$,1
@@ -248,17 +263,17 @@ checkLcov(function () { //FN:$,top-level
   //LH:5
   //BRF:0
   //BRH:0
 });
 
 // Test TableSwitch opcode
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0 //BRDA:$,0,4,0
     case 0:
       l.push('0');        //DA:$,0
       break;
     case 1:
       l.push('1');        //DA:$,0
       break;
     case 2:
       l.push('2');        //DA:$,1
@@ -273,17 +288,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:4
   //BRF:5
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0 //BRDA:$,0,4,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
     case 3:
       l.push('3');        //DA:$,1
@@ -295,17 +310,17 @@ checkLcov(function () { //FN:$,top-level
   //LH:5
   //BRF:5
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
                           // Branches are ordered, and starting at 0
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,- //BRDA:$,0,2,- //BRDA:$,0,3,- //BRDA:$,0,4,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0 //BRDA:$,0,2,0 //BRDA:$,0,3,0 //BRDA:$,0,4,0
     case 5:
       l.push('5');        //DA:$,0
     case 4:
       l.push('4');        //DA:$,0
     case 3:
       l.push('3');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
@@ -316,34 +331,34 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:4
   //BRF:5
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,- //BRDA:$,0,2,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0 //BRDA:$,0,2,0
     case 2:
       l.push('2');        //DA:$,1
     case 5:
       l.push('5');        //DA:$,1
   }
   l.pop();                //DA:$,1
   //FNF:1
   //FNH:1
   //LF:5
   //LH:5
   //BRF:3
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1
     case 3:
       l.push('1');        //DA:$,0
     case 5:
       l.push('5');        //DA:$,0
   }
   l.pop();                //DA:$,1
   //FNF:1
   //FNH:1
@@ -356,33 +371,33 @@ checkLcov(function () { //FN:$,top-level
 // Unfortunately the differences between switch implementations leaks in the
 // code coverage reports.
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   function f(a) {         //FN:$,f //FNDA:2,%
     return a;             //DA:$,2
   }
   var l = ",".split(','); //DA:$,1
   switch (l.length) {     //DA:$,1
-    case f(-42):          //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1
+    case f(-42):          //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1
       l.push('1');        //DA:$,0
-    case f(51):           //DA:$,1 //BRDA:$,1,0,- //BRDA:$,1,1,1
+    case f(51):           //DA:$,1 //BRDA:$,1,0,0 //BRDA:$,1,1,1
       l.push('5');        //DA:$,0
   }
   l.pop();                //DA:$,1
   //FNF:2
   //FNH:2
   //LF:8
   //LH:6
   //BRF:4
   //BRH:2
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1 //BRDA:$,0,2,- //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1 //BRDA:$,0,2,0 //BRDA:$,0,3,0
     case 0:
     case 1:
       l.push('0');        //DA:$,0
       l.push('1');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
     case 3:
       l.push('3');        //DA:$,1
@@ -393,17 +408,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     case 2:
     case 3:
       l.push('2');        //DA:$,1
       l.push('3');        //DA:$,1
@@ -414,17 +429,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
     default:
       l.push('1');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
     case 3:
@@ -436,17 +451,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
     case 2:
       l.push('2');        //DA:$,1
     case 3:
@@ -458,17 +473,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0 //BRDA:$,0,4,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
       l.push('default');  //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
@@ -481,17 +496,17 @@ checkLcov(function () { //FN:$,top-level
   //LF:8
   //LH:5
   //BRF:5
   //BRH:1
 });
 
 checkLcov(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,- //BRDA:$,0,3,1
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,0 //BRDA:$,0,3,1
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
       l.push('2');        //DA:$,1
     case 3:
       l.push('3');        //DA:$,1
@@ -500,10 +515,29 @@ checkLcov(function () { //FN:$,top-level
   //FNF:1
   //FNH:1
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
+checkLcov(function () { //FN:$,top-level //FNDA:1,%
+  var l = ','.split(','); //DA:$,1
+  if (l.length === 45) {  //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0
+    switch (l[0]) {       //DA:$,0 //BRDA:$,1,0,- //BRDA:$,1,1,-
+      case ',':
+        l.push('0');      //DA:$,0
+      default:
+        l.push('1');      //DA:$,0
+    }
+  }
+  l.pop();                //DA:$,1
+  //FNF:1
+  //FNH:1
+  //LF:6
+  //LH:3
+  //BRF:4
+  //BRH:1
+});
+
 // If you add a test case here, do the same in
 // jit-test/tests/debug/Script-getOffsetsCoverage-01.js
--- a/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-01.js
+++ b/js/src/jit-test/tests/debug/Script-getOffsetsCoverage-01.js
@@ -149,16 +149,23 @@ checkGetOffsetsCoverage(function () { //
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level
   function f() {    //FN:$,f
     ",".split(','); //DA:$,1
   }
   f();              //DA:$,1
 });
 
+checkGetOffsetsCoverage(function () { ','.split(','); //FN:$,top-level //DA:$,1
+});
+
+checkGetOffsetsCoverage(function () { function f() { ','.split(','); } //FN:$,top-level //FN:$,f //DA:$,1
+  f(); //DA:$,1
+});
+
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
   if (l.length == 3)      //DA:$,1
     l.push('');           //DA:$,0
   l.pop();                //DA:$,1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
@@ -338,17 +345,17 @@ checkGetOffsetsCoverage(function () { //
       l.push('5');        //DA:$,0
   }
   l.pop();                //DA:$,1
 });
 
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,1 //BRDA:$,0,2,- //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,1 //BRDA:$,0,2,0 //BRDA:$,0,3,0
     case 0:
     case 1:
       l.push('0');        //DA:$,0
       l.push('1');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
     case 3:
       l.push('3');        //DA:$,1
@@ -359,17 +366,17 @@ checkGetOffsetsCoverage(function () { //
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     case 2:
     case 3:
       l.push('2');        //DA:$,1
       l.push('3');        //DA:$,1
@@ -380,17 +387,17 @@ checkGetOffsetsCoverage(function () { //
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
     default:
       l.push('1');        //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
     case 3:
@@ -402,17 +409,17 @@ checkGetOffsetsCoverage(function () { //
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
     case 2:
       l.push('2');        //DA:$,1
     case 3:
@@ -424,17 +431,17 @@ checkGetOffsetsCoverage(function () { //
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,1 //BRDA:$,0,3,- //BRDA:$,0,4,-
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,1 //BRDA:$,0,3,0 //BRDA:$,0,4,0
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
       l.push('default');  //DA:$,0
     case 2:
       l.push('2');        //DA:$,1
@@ -447,17 +454,17 @@ checkGetOffsetsCoverage(function () { //
   //LF:8
   //LH:5
   //BRF:5
   //BRH:1
 });
 
 checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
   var l = ",".split(','); //DA:$,1
-  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,- //BRDA:$,0,1,- //BRDA:$,0,2,- //BRDA:$,0,3,1
+  switch (l.length) {     //DA:$,1 //BRDA:$,0,0,0 //BRDA:$,0,1,0 //BRDA:$,0,2,0 //BRDA:$,0,3,1
     case 0:
       l.push('0');        //DA:$,0
     case 1:
       l.push('1');        //DA:$,0
     default:
       l.push('2');        //DA:$,1
     case 3:
       l.push('3');        //DA:$,1
@@ -466,10 +473,29 @@ checkGetOffsetsCoverage(function () { //
   //FNF:1
   //FNH:1
   //LF:7
   //LH:5
   //BRF:4
   //BRH:1
 });
 
+checkGetOffsetsCoverage(function () { //FN:$,top-level //FNDA:1,%
+  var l = ','.split(','); //DA:$,1
+  if (l.length === 45) {  //DA:$,1 //BRDA:$,0,0,1 //BRDA:$,0,1,0
+    switch (l[0]) {       //DA:$,0 //BRDA:$,1,0,- //BRDA:$,1,1,-
+      case ',':
+        l.push('0');      //DA:$,0
+      default:
+        l.push('1');      //DA:$,0
+    }
+  }
+  l.pop();                //DA:$,1
+  //FNF:1
+  //FNH:1
+  //LF:6
+  //LH:3
+  //BRF:4
+  //BRH:1
+});
+
 // If you add a test case here, do the same in
 // jit-test/tests/coverage/simple.js
--- a/js/src/jit-test/tests/ion/recover-arrays.js
+++ b/js/src/jit-test/tests/ion/recover-arrays.js
@@ -270,18 +270,17 @@ function build(l) { var arr = []; for (v
 var uceFault_arrayAlloc3 = eval(uneval(uceFault).replace('uceFault', 'uceFault_arrayAlloc3'));
 function arrayAlloc3(i) {
     var a = [0,1,2,3];
     if (uceFault_arrayAlloc3(i) || uceFault_arrayAlloc3(i)) {
         assertEq(a[0], 0);
         assertEq(a[3], 3);
         return a.length;
     }
-    // TODO: Does not support NewArrayCopyOnWrite yet.
-    assertRecoveredOnBailout(a, false);
+    assertRecoveredOnBailout(a, true);
     return 0;
 };
 
 // Prevent compilation of the top-level
 eval(uneval(resumeHere));
 
 for (var i = 0; i < 100; i++) {
     array0Length(i);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/recover-cow-arrays.js
@@ -0,0 +1,218 @@
+// Ion eager fails the test below because we have not yet created any
+// template object in baseline before running the content of the top-level
+// function.
+if (getJitCompilerOptions()["ion.warmup.trigger"] <= 100)
+    setJitCompilerOption("ion.warmup.trigger", 100);
+
+// This test checks that we are able to remove the getelem & setelem with scalar
+// replacement, so we should not force inline caches, as this would skip the
+// generation of getelem & setelem instructions.
+if (getJitCompilerOptions()["ion.forceinlineCaches"])
+    setJitCompilerOption("ion.forceinlineCaches", 0);
+
+// This function is used to force a bailout when it is inlined, and to recover
+// the frame of the function in which this function is inlined.
+var resumeHere = function (i) { if (i >= 99) bailout(); };
+
+// This function is used to cause an invalidation after having removed a branch
+// after DCE.  This is made to check if we correctly recover an array
+// allocation.
+var uceFault = function (i) {
+    if (i > 98)
+        uceFault = function (i) { return true; };
+    return false;
+};
+
+// This function is used to ensure that we do escape the array, and thus prevent
+// any escape analysis.
+var global_arr;
+function escape(arr) { global_arr = arr; }
+
+// Check Array length defined by the literal.
+function array0Length(i) {
+    var a = [];
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array0LengthBail(i) {
+    var a = [];
+    resumeHere(i);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array1Length(i) {
+    var a = [1];
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array1LengthBail(i) {
+    var a = [1];
+    resumeHere(i);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2Length(i) {
+    var a = [1, 2];
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2LengthBail(i) {
+    var a = [1, 2];
+    resumeHere(i);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+
+// Check Array content
+function array1Content(i) {
+    var a = [42];
+    assertEq(a[0], 42);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+function array1ContentBail0(i) {
+    var a = [42];
+    resumeHere(i);
+    assertEq(a[0], 42);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+function array1ContentBail1(i) {
+    var a = [42];
+    assertEq(a[0], 42);
+    resumeHere(i);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2Content(i) {
+    var a = [1, 2];
+    assertEq(a[0], 1);
+    assertEq(a[1], 2);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2ContentBail0(i) {
+    var a = [1, 2];
+    resumeHere(i);
+    assertEq(a[0], 1);
+    assertEq(a[1], 2);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2ContentBail1(i) {
+    var a = [1, 2];
+    assertEq(a[0], 1);
+    resumeHere(i);
+    assertEq(a[1], 2);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+function array2ContentBail2(i) {
+    var a = [1, 2];
+    assertEq(a[0], 1);
+    assertEq(a[1], 2);
+    resumeHere(i);
+    assertRecoveredOnBailout(a, true);
+    return a.length;
+}
+
+// We don't handle COW array writes
+function arrayWrite1(i) {
+    var a = [1, 2];
+    a[0] = 42;
+    assertEq(a[0], 42);
+    assertEq(a[1], 2);
+    assertRecoveredOnBailout(a, false);
+    return a.length;
+}
+
+function arrayWrite2(i) {
+    var a = [1, 2];
+    a.length = 1;
+    assertEq(a[0], 1);
+    assertEq(a[1], undefined);
+    assertRecoveredOnBailout(a, false);
+    return a.length;
+}
+
+// Check escape analysis in case of holes.
+function arrayHole0(i) {
+    var a = [1,,3];
+    assertEq(a[0], 1);
+    assertEq(a[1], undefined);
+    assertEq(a[2], 3);
+    // need to check for holes.
+    assertRecoveredOnBailout(a, false);
+    return a.length;
+}
+
+// Same test as the previous one, but the Array.prototype is changed to return
+// "100" when we request for the element "1".
+function arrayHole1(i) {
+    var a = [1,,3];
+    assertEq(a[0], 1);
+    assertEq(a[1], 100);
+    assertEq(a[2], 3);
+    // need to check for holes.
+    assertRecoveredOnBailout(a, false);
+    return a.length;
+}
+
+function build(l) { var arr = []; for (var i = 0; i < l; i++) arr.push(i); return arr }
+var uceFault_arrayAlloc3 = eval(uneval(uceFault).replace('uceFault', 'uceFault_arrayAlloc3'));
+function arrayAlloc(i) {
+    var a = [0,1,2,3];
+    if (uceFault_arrayAlloc3(i) || uceFault_arrayAlloc3(i)) {
+        assertEq(a[0], 0);
+        assertEq(a[3], 3);
+        return a.length;
+    }
+    assertRecoveredOnBailout(a, true);
+    return 0;
+};
+
+// Prevent compilation of the top-level
+eval(uneval(resumeHere));
+
+for (var i = 0; i < 100; i++) {
+    array0Length(i);
+    array0LengthBail(i);
+    array1Length(i);
+    array1LengthBail(i);
+    array2Length(i);
+    array2LengthBail(i);
+    array1Content(i);
+    array1ContentBail0(i);
+    array1ContentBail1(i);
+    array2Content(i);
+    array2ContentBail0(i);
+    array2ContentBail1(i);
+    array2ContentBail2(i);
+    arrayWrite1(i);
+    arrayWrite2(i);
+    arrayHole0(i);
+    arrayAlloc(i);
+}
+
+// If arr[1] is not defined, then we fallback on the prototype which instead of
+// returning undefined, returns "0".
+Object.defineProperty(Array.prototype, 1, {
+  value: 100,
+  configurable: true,
+  enumerable: true,
+  writable: true
+});
+
+for (var i = 0; i < 100; i++) {
+    arrayHole1(i);
+}
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -41,17 +41,16 @@
 #endif
 #include "jit/arm/Simulator-arm.h"
 #if defined(JS_CODEGEN_ARM64)
 #include "jit/arm64/vixl/Cpu-vixl.h"
 #endif
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "jit/ProcessExecutableMemory.h"
-#include "js/GCAPI.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
 #if defined(__sparc__)
 #ifdef __linux__  // bugzilla 502369
 static void sync_instruction_memory(caddr_t v, u_int len)
 {
     caddr_t end = v + len;
@@ -71,16 +70,18 @@ extern  "C" void sync_instruction_memory
      (!defined(JS_SIMULATOR_MIPS32) && !defined(JS_SIMULATOR_MIPS64))
 #include <sys/cachectl.h>
 #endif
 
 #if defined(JS_CODEGEN_ARM) && defined(XP_IOS)
 #include <libkern/OSCacheControl.h>
 #endif
 
+struct JSRuntime;
+
 namespace JS {
     struct CodeSizes;
 } // namespace JS
 
 namespace js {
 namespace jit {
 
 enum CodeKind { ION_CODE = 0, BASELINE_CODE, REGEXP_CODE, OTHER_CODE };
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6117,18 +6117,22 @@ IonBuilder::jsop_newarray_copyonwrite()
 
     // The baseline compiler should have ensured the template object has a type
     // with the copy on write flag set already. During the arguments usage
     // analysis the baseline compiler hasn't run yet, however, though in this
     // case the template object's type doesn't matter.
     MOZ_ASSERT_IF(info().analysisMode() != Analysis_ArgumentsUsage,
                   templateObject->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
 
+
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    current->add(templateConst);
+
     MNewArrayCopyOnWrite* ins =
-        MNewArrayCopyOnWrite::New(alloc(), constraints(), templateObject,
+        MNewArrayCopyOnWrite::New(alloc(), constraints(), templateConst,
                                   templateObject->group()->initialHeap(constraints()));
 
     current->add(ins);
     current->push(ins);
 
     return Ok();
 }
 
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -9,17 +9,16 @@
 
 #include "mozilla/HashFunctions.h"
 
 #include <algorithm>
 
 #include "jsfriendapi.h"
 #include "jstypes.h"
 
-#include "js/GCAPI.h"
 #include "js/Value.h"
 #include "vm/String.h"
 
 namespace js {
 namespace jit {
 
 typedef uint32_t RecoverOffset;
 typedef uint32_t SnapshotOffset;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -5084,17 +5084,17 @@ MObjectState::initFromTemplateObject(Tem
     // uninitialized-lexical magic value of call objects.
     if (templateObject->is<UnboxedPlainObject>()) {
         UnboxedPlainObject& unboxedObject = templateObject->as<UnboxedPlainObject>();
         const UnboxedLayout& layout = unboxedObject.layoutDontCheckGeneration();
         const UnboxedLayout::PropertyVector& properties = layout.properties();
 
         for (size_t i = 0; i < properties.length(); i++) {
             Value val = unboxedObject.getValue(properties[i], /* maybeUninitialized = */ true);
-            MDefinition *def = undefinedVal;
+            MDefinition* def = undefinedVal;
             if (!val.isUndefined()) {
                 MConstant* ins = val.isObject() ?
                     MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
                     MConstant::New(alloc, val);
                 block()->insertBefore(this, ins);
                 def = ins;
             }
             initSlot(i, def);
@@ -5150,40 +5150,70 @@ MObjectState::Copy(TempAllocator& alloc,
 }
 
 MArrayState::MArrayState(MDefinition* arr)
   : MVariadicInstruction(classOpcode)
 {
     // This instruction is only used as a summary for bailout paths.
     setResultType(MIRType::Object);
     setRecoveredOnBailout();
-    numElements_ = arr->toNewArray()->length();
+    numElements_ = arr->isNewArray() ? arr->toNewArray()->length()
+                   : arr->toNewArrayCopyOnWrite()->length();
 }
 
 bool
 MArrayState::init(TempAllocator& alloc, MDefinition* obj, MDefinition* len)
 {
     if (!MVariadicInstruction::init(alloc, numElements() + 2))
         return false;
     // +1, for the Array object.
     initOperand(0, obj);
     // +1, for the length value of the array.
     initOperand(1, len);
     return true;
 }
 
+bool
+MArrayState::initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal)
+{
+    if (!array()->isNewArrayCopyOnWrite()) {
+        for (size_t i = 0; i < numElements(); i++)
+            initElement(i, undefinedVal);
+
+        return true;
+    }
+
+    ArrayObject* obj = array()->toNewArrayCopyOnWrite()->templateObject();
+    MOZ_ASSERT(obj->length() == numElements());
+
+    // Initialize all the elements of the object state with the value contained in
+    // the template object.
+    for (size_t i = 0; i < numElements(); i++) {
+        Value val = obj->getDenseElement(i);
+        MDefinition* def = undefinedVal;
+        if (!val.isUndefined()) {
+            MConstant* ins = val.isObject() ?
+                MConstant::NewConstraintlessObject(alloc, &val.toObject()) :
+                MConstant::New(alloc, val);
+            block()->insertBefore(this, ins);
+            def = ins;
+        }
+        initElement(i, def);
+    }
+
+    return true;
+}
+
 MArrayState*
 MArrayState::New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
                  MDefinition* initLength)
 {
     MArrayState* res = new(alloc) MArrayState(arr);
     if (!res || !res->init(alloc, arr, initLength))
         return nullptr;
-    for (size_t i = 0; i < res->numElements(); i++)
-        res->initElement(i, undefinedVal);
     return res;
 }
 
 MArrayState*
 MArrayState::Copy(TempAllocator& alloc, MArrayState* state)
 {
     MDefinition* arr = state->array();
     MDefinition* len = state->initializedLength();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -3366,50 +3366,55 @@ class MNewArray
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         // The template object can safely be used in the recover instruction
         // because it can never be mutated by any other function execution.
         return templateObject() != nullptr;
     }
 };
 
-class MNewArrayCopyOnWrite : public MNullaryInstruction
-{
-    CompilerGCPointer<ArrayObject*> templateObject_;
+class MNewArrayCopyOnWrite
+  : public MUnaryInstruction,
+    public NoTypePolicy::Data
+{
     gc::InitialHeap initialHeap_;
 
     MNewArrayCopyOnWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
-                         ArrayObject* templateObject, gc::InitialHeap initialHeap)
-      : MNullaryInstruction(classOpcode),
-        templateObject_(templateObject),
+                         MConstant* templateConst, gc::InitialHeap initialHeap)
+      : MUnaryInstruction(classOpcode, templateConst),
         initialHeap_(initialHeap)
     {
-        MOZ_ASSERT(!templateObject->isSingleton());
+        MOZ_ASSERT(!templateObject()->isSingleton());
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
     }
 
   public:
     INSTRUCTION_HEADER(NewArrayCopyOnWrite)
     TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
+    uint32_t length() const {
+      return templateObject()->length();
+    }
+
     ArrayObject* templateObject() const {
-        return templateObject_;
+        return &getOperand(0)->toConstant()->toObject().as<ArrayObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
     virtual AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 
-    bool appendRoots(MRootList& roots) const override {
-        return roots.append(templateObject_);
+    MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
+    bool canRecoverOnBailout() const override {
+        return true;
     }
 };
 
 class MNewArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
@@ -3997,21 +4002,23 @@ class MArrayState
   public:
     INSTRUCTION_HEADER(ArrayState)
     NAMED_OPERANDS((0, array), (1, initializedLength))
 
     static MArrayState* New(TempAllocator& alloc, MDefinition* arr, MDefinition* undefinedVal,
                             MDefinition* initLength);
     static MArrayState* Copy(TempAllocator& alloc, MArrayState* state);
 
+    // Initialize values from CopyOnWrite arrays.
+    MOZ_MUST_USE bool initFromTemplateObject(TempAllocator& alloc, MDefinition* undefinedVal);
+
     void setInitializedLength(MDefinition* def) {
         replaceOperand(1, def);
     }
 
-
     size_t numElements() const {
         return numElements_;
     }
 
     MDefinition* getElement(uint32_t index) const {
         return getOperand(index + 2);
     }
     void setElement(uint32_t index, MDefinition* def) {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -16,17 +16,16 @@
 #include "jit/AtomicOp.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "js/Conversions.h"
-#include "js/GCAPI.h"
 #include "vm/TraceLogging.h"
 
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Interpreter-inl.h"
 
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -1397,16 +1397,45 @@ RNewArray::recover(JSContext* cx, Snapsh
         return false;
 
     result.setObject(*resultObject);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MNewArrayCopyOnWrite::writeRecoverData(CompactBufferWriter& writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_NewArrayCopyOnWrite));
+    writer.writeByte(initialHeap());
+    return true;
+}
+
+RNewArrayCopyOnWrite::RNewArrayCopyOnWrite(CompactBufferReader& reader)
+{
+    initialHeap_ = gc::InitialHeap(reader.readByte());
+}
+
+bool
+RNewArrayCopyOnWrite::recover(JSContext* cx, SnapshotIterator& iter) const
+{
+    RootedArrayObject templateObject(cx, &iter.read().toObject().as<ArrayObject>());
+    RootedValue result(cx);
+
+    ArrayObject* resultObject = NewDenseCopyOnWriteArray(cx, templateObject, initialHeap_);
+    if (!resultObject)
+        return false;
+
+    result.setObject(*resultObject);
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MNewIterator::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_NewIterator));
     writer.writeByte(type_);
     return true;
 }
 
@@ -1727,26 +1756,36 @@ RArrayState::RArrayState(CompactBufferRe
 
 bool
 RArrayState::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedValue result(cx);
     ArrayObject* object = &iter.read().toObject().as<ArrayObject>();
     uint32_t initLength = iter.read().toInt32();
 
-    object->setDenseInitializedLength(initLength);
-    for (size_t index = 0; index < numElements(); index++) {
-        Value val = iter.read();
+    if (!object->denseElementsAreCopyOnWrite()) {
+        object->setDenseInitializedLength(initLength);
+        for (size_t index = 0; index < numElements(); index++) {
+            Value val = iter.read();
+
+            if (index >= initLength) {
+                MOZ_ASSERT(val.isUndefined());
+                continue;
+            }
 
-        if (index >= initLength) {
-            MOZ_ASSERT(val.isUndefined());
-            continue;
+            object->initDenseElement(index, val);
         }
+    } else {
+        MOZ_ASSERT(object->getDenseInitializedLength() == numElements());
+        MOZ_ASSERT(initLength == numElements());
 
-        object->initDenseElement(index, val);
+        for (size_t index = 0; index < numElements(); index++) {
+            Value val = iter.read();
+            MOZ_RELEASE_ASSERT(object->getDenseElement(index) == val);
+        }
     }
 
     result.setObject(*object);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -98,16 +98,17 @@ namespace jit {
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
     _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewTypedArray)                            \
     _(NewArray)                                 \
+    _(NewArrayCopyOnWrite)                      \
     _(NewIterator)                              \
     _(NewDerivedTypedObject)                    \
     _(NewCallObject)                            \
     _(CreateThisWithTemplate)                   \
     _(Lambda)                                   \
     _(LambdaArrow)                              \
     _(SimdBox)                                  \
     _(ObjectState)                              \
@@ -604,16 +605,27 @@ class RNewArray final : public RInstruct
     uint32_t count_;
 
   public:
     RINSTRUCTION_HEADER_NUM_OP_(NewArray, 1)
 
     MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
 };
 
+class RNewArrayCopyOnWrite final : public RInstruction
+{
+  private:
+    gc::InitialHeap initialHeap_;
+
+  public:
+    RINSTRUCTION_HEADER_NUM_OP_(NewArrayCopyOnWrite, 1)
+
+    MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
+};
+
 class RNewIterator final : public RInstruction
 {
   private:
     uint8_t type_;
 
   public:
     RINSTRUCTION_HEADER_NUM_OP_(NewIterator, 1)
 
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -870,17 +870,17 @@ IndexOf(MDefinition* ins, int32_t* res)
         return false;
     *res = indexDefConst->toInt32();
     return true;
 }
 
 // Returns False if the elements is not escaped and if it is optimizable by
 // ScalarReplacementOfArray.
 static bool
-IsElementEscaped(MElements* def, uint32_t arraySize)
+IsElementEscaped(MElements* def, uint32_t arraySize, bool copyOnWrite)
 {
     JitSpewDef(JitSpew_Escape, "Check elements\n", def);
     JitSpewIndent spewIndent(JitSpew_Escape);
 
     for (MUseIterator i(def->usesBegin()); i != def->usesEnd(); i++) {
         // The MIRType::Elements cannot be captured in a resume point as
         // it does not represent a value allocation.
         MDefinition* access = (*i)->consumer()->toDefinition();
@@ -914,16 +914,21 @@ IsElementEscaped(MElements* def, uint32_
                 return true;
             }
             break;
           }
 
           case MDefinition::Opcode::StoreElement: {
             MOZ_ASSERT(access->toStoreElement()->elements() == def);
 
+            if (copyOnWrite) {
+                JitSpewDef(JitSpew_Escape, "write to COW\n", access);
+                return true;
+            }
+
             // If we need hole checks, then the array cannot be escaped
             // as the array might refer to the prototype chain to look
             // for properties, thus it might do additional side-effects
             // which are not reflected by the alias set, is we are
             // bailing on holes.
             if (access->toStoreElement()->needsHoleCheck()) {
                 JitSpewDef(JitSpew_Escape,
                            "has a store element with a hole check\n", access);
@@ -946,16 +951,21 @@ IsElementEscaped(MElements* def, uint32_
             if (access->toStoreElement()->value()->type() == MIRType::MagicHole) {
                 JitSpewDef(JitSpew_Escape, "has a store element with an magic-hole constant\n", access);
                 return true;
             }
             break;
           }
 
           case MDefinition::Opcode::SetInitializedLength:
+            if (copyOnWrite) {
+                JitSpewDef(JitSpew_Escape, "write to COW\n", access);
+                return true;
+            }
+
             MOZ_ASSERT(access->toSetInitializedLength()->elements() == def);
             break;
 
           case MDefinition::Opcode::InitializedLength:
             MOZ_ASSERT(access->toInitializedLength()->elements() == def);
             break;
 
           case MDefinition::Opcode::ArrayLength:
@@ -966,35 +976,46 @@ IsElementEscaped(MElements* def, uint32_
             JitSpewDef(JitSpew_Escape, "is escaped by\n", access);
             return true;
         }
     }
     JitSpew(JitSpew_Escape, "Elements is not escaped");
     return false;
 }
 
+static inline bool
+IsOptimizableArrayInstruction(MInstruction* ins)
+{
+    return ins->isNewArray() || ins->isNewArrayCopyOnWrite();
+}
+
 // Returns False if the array is not escaped and if it is optimizable by
 // ScalarReplacementOfArray.
 //
 // For the moment, this code is dumb as it only supports arrays which are not
 // changing length, with only access with known constants.
 static bool
 IsArrayEscaped(MInstruction* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType::Object);
-    MOZ_ASSERT(ins->isNewArray());
-    uint32_t length = ins->toNewArray()->length();
+    MOZ_ASSERT(IsOptimizableArrayInstruction(ins));
 
     JitSpewDef(JitSpew_Escape, "Check array\n", ins);
     JitSpewIndent spewIndent(JitSpew_Escape);
 
-    JSObject* obj = ins->toNewArray()->templateObject();
-    if (!obj) {
-        JitSpew(JitSpew_Escape, "No template object defined.");
-        return true;
+    uint32_t length;
+    if (ins->isNewArray()) {
+        if (!ins->toNewArray()->templateObject()) {
+            JitSpew(JitSpew_Escape, "No template object defined.");
+            return true;
+        }
+
+        length = ins->toNewArray()->length();
+    } else {
+        length = ins->toNewArrayCopyOnWrite()->templateObject()->length();
     }
 
     if (length >= 16) {
         JitSpew(JitSpew_Escape, "Array has too many elements");
         return true;
     }
 
     // Check if the object is escaped. If the object is not the first argument
@@ -1011,17 +1032,17 @@ IsArrayEscaped(MInstruction* ins)
             continue;
         }
 
         MDefinition* def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Opcode::Elements: {
             MElements *elem = def->toElements();
             MOZ_ASSERT(elem->object() == ins);
-            if (IsElementEscaped(elem, length)) {
+            if (IsElementEscaped(elem, length, ins->isNewArrayCopyOnWrite())) {
                 JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", elem);
                 return true;
             }
 
             break;
           }
 
           // This instruction is a no-op used to verify that scalar replacement
@@ -1121,27 +1142,33 @@ ArrayMemoryView::startingBlock()
     return startBlock_;
 }
 
 bool
 ArrayMemoryView::initStartingState(BlockState** pState)
 {
     // Uninitialized elements have an "undefined" value.
     undefinedVal_ = MConstant::New(alloc_, UndefinedValue());
-    MConstant* initLength = MConstant::New(alloc_, Int32Value(0));
+    MConstant* initLength = MConstant::New(alloc_, Int32Value(arr_->isNewArrayCopyOnWrite()
+                                                              ? arr_->toNewArrayCopyOnWrite()->length()
+                                                              : 0));
     arr_->block()->insertBefore(arr_, undefinedVal_);
     arr_->block()->insertBefore(arr_, initLength);
 
     // Create a new block state and insert at it at the location of the new array.
     BlockState* state = BlockState::New(alloc_, arr_, undefinedVal_, initLength);
     if (!state)
         return false;
 
     startBlock_->insertAfter(arr_, state);
 
+    // Initialize the elements of the array state.
+    if (!state->initFromTemplateObject(alloc_, undefinedVal_))
+        return false;
+
     // Hold out of resume point until it is visited.
     state->setInWorklist();
 
     *pState = state;
     return true;
 }
 
 void
@@ -1396,17 +1423,17 @@ ScalarReplacement(MIRGenerator* mir, MIR
                 ObjectMemoryView view(graph.alloc(), *ins);
                 if (!replaceObject.run(view))
                     return false;
                 view.assertSuccess();
                 addedPhi = true;
                 continue;
             }
 
-            if (ins->isNewArray() && !IsArrayEscaped(*ins)) {
+            if (IsOptimizableArrayInstruction(*ins) && !IsArrayEscaped(*ins)) {
                 ArrayMemoryView view(graph.alloc(), *ins);
                 if (!replaceArray.run(view))
                     return false;
                 view.assertSuccess();
                 addedPhi = true;
                 continue;
             }
         }
--- a/js/src/jsapi-tests/testGCHooks.cpp
+++ b/js/src/jsapi-tests/testGCHooks.cpp
@@ -1,17 +1,15 @@
 /* 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 "mozilla/ArrayUtils.h"
 #include "mozilla/UniquePtr.h"
 
-#include "js/GCAPI.h"
-
 #include "jsapi-tests/tests.h"
 
 static unsigned gSliceCallbackCount = 0;
 
 static void
 NonIncrementalGCSliceCallback(JSContext* cx, JS::GCProgress progress, const JS::GCDescription& desc)
 {
     using namespace JS;
--- a/js/src/jsapi-tests/testIntTypesABI.cpp
+++ b/js/src/jsapi-tests/testIntTypesABI.cpp
@@ -14,17 +14,16 @@
 #include "jstypes.h"
 
 #include "js/CallArgs.h"
 #include "js/CallNonGenericMethod.h"
 #include "js/CharacterEncoding.h"
 #include "js/Class.h"
 #include "js/Date.h"
 #include "js/Debug.h"
-#include "js/GCAPI.h"
 #include "js/HashTable.h"
 #include "js/HeapAPI.h"
 #include "js/Id.h"
 /* LegacyIntTypes.h is deliberately exempted from this requirement */
 #include "js/MemoryMetrics.h"
 #include "js/ProfilingStack.h"
 #include "js/RefCounted.h"
 #include "js/RequiredDefines.h"
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -544,63 +544,16 @@ struct JSFreeOp {
         return runtime_;
     }
 };
 
 /* Callbacks and their arguments. */
 
 /************************************************************************/
 
-typedef enum JSGCStatus {
-    JSGC_BEGIN,
-    JSGC_END
-} JSGCStatus;
-
-typedef void
-(* JSGCCallback)(JSContext* cx, JSGCStatus status, void* data);
-
-typedef void
-(* JSObjectsTenuredCallback)(JSContext* cx, void* data);
-
-typedef enum JSFinalizeStatus {
-    /**
-     * Called when preparing to sweep a group of zones, before anything has been
-     * swept.  The collector will not yield to the mutator before calling the
-     * callback with JSFINALIZE_GROUP_START status.
-     */
-    JSFINALIZE_GROUP_PREPARE,
-
-    /**
-     * Called after preparing to sweep a group of zones. Weak references to
-     * unmarked things have been removed at this point, but no GC things have
-     * been swept. The collector may yield to the mutator after this point.
-     */
-    JSFINALIZE_GROUP_START,
-
-    /**
-     * Called after sweeping a group of zones. All dead GC things have been
-     * swept at this point.
-     */
-    JSFINALIZE_GROUP_END,
-
-    /**
-     * Called at the end of collection when everything has been swept.
-     */
-    JSFINALIZE_COLLECTION_END
-} JSFinalizeStatus;
-
-typedef void
-(* JSFinalizeCallback)(JSFreeOp* fop, JSFinalizeStatus status, void* data);
-
-typedef void
-(* JSWeakPointerZonesCallback)(JSContext* cx, void* data);
-
-typedef void
-(* JSWeakPointerCompartmentCallback)(JSContext* cx, JSCompartment* comp, void* data);
-
 typedef bool
 (* JSInterruptCallback)(JSContext* cx);
 
 typedef JSObject*
 (* JSGetIncumbentGlobalCallback)(JSContext* cx);
 
 typedef bool
 (* JSEnqueuePromiseJobCallback)(JSContext* cx, JS::HandleObject job,
@@ -1724,359 +1677,16 @@ JS_freeop(JSFreeOp* fop, void* p);
 
 extern JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext* cx, size_t nbytes);
 
 extern JS_PUBLIC_API(char*)
 JS_strdup(JSContext* cx, const char* s);
 
 /**
- * Register externally maintained GC roots.
- *
- * traceOp: the trace operation. For each root the implementation should call
- *          JS::TraceEdge whenever the root contains a traceable thing.
- * data:    the data argument to pass to each invocation of traceOp.
- */
-extern JS_PUBLIC_API(bool)
-JS_AddExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data);
-
-/** Undo a call to JS_AddExtraGCRootsTracer. */
-extern JS_PUBLIC_API(void)
-JS_RemoveExtraGCRootsTracer(JSContext* cx, JSTraceDataOp traceOp, void* data);
-
-/*
- * Garbage collector API.
- */
-namespace JS {
-
-extern JS_PUBLIC_API(bool)
-IsIdleGCTaskNeeded(JSRuntime* rt);
-
-extern JS_PUBLIC_API(void)
-RunIdleTimeGCTask(JSRuntime* rt);
-
-} // namespace JS
-
-extern JS_PUBLIC_API(void)
-JS_GC(JSContext* cx);
-
-extern JS_PUBLIC_API(void)
-JS_MaybeGC(JSContext* cx);
-
-extern JS_PUBLIC_API(void)
-JS_SetGCCallback(JSContext* cx, JSGCCallback cb, void* data);
-
-extern JS_PUBLIC_API(void)
-JS_SetObjectsTenuredCallback(JSContext* cx, JSObjectsTenuredCallback cb,
-                             void* data);
-
-extern JS_PUBLIC_API(bool)
-JS_AddFinalizeCallback(JSContext* cx, JSFinalizeCallback cb, void* data);
-
-extern JS_PUBLIC_API(void)
-JS_RemoveFinalizeCallback(JSContext* cx, JSFinalizeCallback cb);
-
-/*
- * Weak pointers and garbage collection
- *
- * Weak pointers are by their nature not marked as part of garbage collection,
- * but they may need to be updated in two cases after a GC:
- *
- *  1) Their referent was found not to be live and is about to be finalized
- *  2) Their referent has been moved by a compacting GC
- *
- * To handle this, any part of the system that maintain weak pointers to
- * JavaScript GC things must register a callback with
- * JS_(Add,Remove)WeakPointer{ZoneGroup,Compartment}Callback(). This callback
- * must then call JS_UpdateWeakPointerAfterGC() on all weak pointers it knows
- * about.
- *
- * Since sweeping is incremental, we have several callbacks to avoid repeatedly
- * having to visit all embedder structures. The WeakPointerZonesCallback is
- * called once for each strongly connected group of zones, whereas the
- * WeakPointerCompartmentCallback is called once for each compartment that is
- * visited while sweeping. Structures that cannot contain references in more
- * than one compartment should sweep the relevant per-compartment structures
- * using the latter callback to minimizer per-slice overhead.
- *
- * The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
- * referent is about to be finalized the pointer will be set to null. If the
- * referent has been moved then the pointer will be updated to point to the new
- * location.
- *
- * Callers of this method are responsible for updating any state that is
- * dependent on the object's address. For example, if the object's address is
- * used as a key in a hashtable, then the object must be removed and
- * re-inserted with the correct hash.
- */
-
-extern JS_PUBLIC_API(bool)
-JS_AddWeakPointerZonesCallback(JSContext* cx, JSWeakPointerZonesCallback cb, void* data);
-
-extern JS_PUBLIC_API(void)
-JS_RemoveWeakPointerZonesCallback(JSContext* cx, JSWeakPointerZonesCallback cb);
-
-extern JS_PUBLIC_API(bool)
-JS_AddWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb,
-                                     void* data);
-
-extern JS_PUBLIC_API(void)
-JS_RemoveWeakPointerCompartmentCallback(JSContext* cx, JSWeakPointerCompartmentCallback cb);
-
-extern JS_PUBLIC_API(void)
-JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject*>* objp);
-
-extern JS_PUBLIC_API(void)
-JS_UpdateWeakPointerAfterGCUnbarriered(JSObject** objp);
-
-typedef enum JSGCParamKey {
-    /**
-     * Maximum nominal heap before last ditch GC.
-     *
-     * Soft limit on the number of bytes we are allowed to allocate in the GC
-     * heap. Attempts to allocate gcthings over this limit will return null and
-     * subsequently invoke the standard OOM machinery, independent of available
-     * physical memory.
-     *
-     * Pref: javascript.options.mem.max
-     * Default: 0xffffffff
-     */
-    JSGC_MAX_BYTES          = 0,
-
-    /**
-     * Initial value for the malloc bytes threshold.
-     *
-     * Pref: javascript.options.mem.high_water_mark
-     * Default: TuningDefaults::MaxMallocBytes
-     */
-    JSGC_MAX_MALLOC_BYTES   = 1,
-
-    /**
-     * Maximum size of the generational GC nurseries.
-     *
-     * Pref: javascript.options.mem.nursery.max_kb
-     * Default: JS::DefaultNurseryBytes
-     */
-    JSGC_MAX_NURSERY_BYTES  = 2,
-
-    /** Amount of bytes allocated by the GC. */
-    JSGC_BYTES = 3,
-
-    /** Number of times GC has been invoked. Includes both major and minor GC. */
-    JSGC_NUMBER = 4,
-
-    /**
-     * Select GC mode.
-     *
-     * See: JSGCMode in GCAPI.h
-     * prefs: javascript.options.mem.gc_per_zone and
-     *   javascript.options.mem.gc_incremental.
-     * Default: JSGC_MODE_INCREMENTAL
-     */
-    JSGC_MODE = 6,
-
-    /** Number of cached empty GC chunks. */
-    JSGC_UNUSED_CHUNKS = 7,
-
-    /** Total number of allocated GC chunks. */
-    JSGC_TOTAL_CHUNKS = 8,
-
-    /**
-     * Max milliseconds to spend in an incremental GC slice.
-     *
-     * Pref: javascript.options.mem.gc_incremental_slice_ms
-     * Default: DefaultTimeBudget.
-     */
-    JSGC_SLICE_TIME_BUDGET = 9,
-
-    /**
-     * Maximum size the GC mark stack can grow to.
-     *
-     * Pref: none
-     * Default: MarkStack::DefaultCapacity
-     */
-    JSGC_MARK_STACK_LIMIT = 10,
-
-    /**
-     * GCs less than this far apart in time will be considered 'high-frequency
-     * GCs'.
-     *
-     * See setGCLastBytes in jsgc.cpp.
-     *
-     * Pref: javascript.options.mem.gc_high_frequency_time_limit_ms
-     * Default: HighFrequencyThresholdUsec
-     */
-    JSGC_HIGH_FREQUENCY_TIME_LIMIT = 11,
-
-    /**
-     * Start of dynamic heap growth.
-     *
-     * Pref: javascript.options.mem.gc_high_frequency_low_limit_mb
-     * Default: HighFrequencyLowLimitBytes
-     */
-    JSGC_HIGH_FREQUENCY_LOW_LIMIT = 12,
-
-    /**
-     * End of dynamic heap growth.
-     *
-     * Pref: javascript.options.mem.gc_high_frequency_high_limit_mb
-     * Default: HighFrequencyHighLimitBytes
-     */
-    JSGC_HIGH_FREQUENCY_HIGH_LIMIT = 13,
-
-    /**
-     * Upper bound of heap growth.
-     *
-     * Pref: javascript.options.mem.gc_high_frequency_heap_growth_max
-     * Default: HighFrequencyHeapGrowthMax
-     */
-    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX = 14,
-
-    /**
-     * Lower bound of heap growth.
-     *
-     * Pref: javascript.options.mem.gc_high_frequency_heap_growth_min
-     * Default: HighFrequencyHeapGrowthMin
-     */
-    JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN = 15,
-
-    /**
-     * Heap growth for low frequency GCs.
-     *
-     * Pref: javascript.options.mem.gc_low_frequency_heap_growth
-     * Default: LowFrequencyHeapGrowth
-     */
-    JSGC_LOW_FREQUENCY_HEAP_GROWTH = 16,
-
-    /**
-     * If false, the heap growth factor is fixed at 3. If true, it is determined
-     * based on whether GCs are high- or low- frequency.
-     *
-     * Pref: javascript.options.mem.gc_dynamic_heap_growth
-     * Default: DynamicHeapGrowthEnabled
-     */
-    JSGC_DYNAMIC_HEAP_GROWTH = 17,
-
-    /**
-     * If true, high-frequency GCs will use a longer mark slice.
-     *
-     * Pref: javascript.options.mem.gc_dynamic_mark_slice
-     * Default: DynamicMarkSliceEnabled
-     */
-    JSGC_DYNAMIC_MARK_SLICE = 18,
-
-    /**
-     * Lower limit after which we limit the heap growth.
-     *
-     * The base value used to compute zone->threshold.gcTriggerBytes(). When
-     * usage.gcBytes() surpasses threshold.gcTriggerBytes() for a zone, the
-     * zone may be scheduled for a GC, depending on the exact circumstances.
-     *
-     * Pref: javascript.options.mem.gc_allocation_threshold_mb
-     * Default GCZoneAllocThresholdBase
-     */
-    JSGC_ALLOCATION_THRESHOLD = 19,
-
-    /**
-     * We try to keep at least this many unused chunks in the free chunk pool at
-     * all times, even after a shrinking GC.
-     *
-     * Pref: javascript.options.mem.gc_min_empty_chunk_count
-     * Default: MinEmptyChunkCount
-     */
-    JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
-
-    /**
-     * We never keep more than this many unused chunks in the free chunk
-     * pool.
-     *
-     * Pref: javascript.options.mem.gc_min_empty_chunk_count
-     * Default: MinEmptyChunkCount
-     */
-    JSGC_MAX_EMPTY_CHUNK_COUNT = 22,
-
-    /**
-     * Whether compacting GC is enabled.
-     *
-     * Pref: javascript.options.mem.gc_compacting
-     * Default: CompactingEnabled
-     */
-    JSGC_COMPACTING_ENABLED = 23,
-
-    /**
-     * If true, painting can trigger IGC slices.
-     *
-     * Pref: javascript.options.mem.gc_refresh_frame_slices_enabled
-     * Default: RefreshFrameSlicesEnabled
-     */
-    JSGC_REFRESH_FRAME_SLICES_ENABLED = 24,
-
-    /**
-     * Factor for triggering a GC based on JSGC_ALLOCATION_THRESHOLD
-     *
-     * Default: ZoneAllocThresholdFactorDefault
-     * Pref: None
-     */
-    JSGC_ALLOCATION_THRESHOLD_FACTOR = 25,
-
-    /**
-     * Factor for triggering a GC based on JSGC_ALLOCATION_THRESHOLD.
-     * Used if another GC (in different zones) is already running.
-     *
-     * Default: ZoneAllocThresholdFactorAvoidInterruptDefault
-     * Pref: None
-     */
-    JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT = 26,
-} JSGCParamKey;
-
-extern JS_PUBLIC_API(void)
-JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value);
-
-extern JS_PUBLIC_API(void)
-JS_ResetGCParameter(JSContext* cx, JSGCParamKey key);
-
-extern JS_PUBLIC_API(uint32_t)
-JS_GetGCParameter(JSContext* cx, JSGCParamKey key);
-
-extern JS_PUBLIC_API(void)
-JS_SetGCParametersBasedOnAvailableMemory(JSContext* cx, uint32_t availMem);
-
-/**
- * Create a new JSString whose chars member refers to external memory, i.e.,
- * memory requiring application-specific finalization.
- */
-extern JS_PUBLIC_API(JSString*)
-JS_NewExternalString(JSContext* cx, const char16_t* chars, size_t length,
-                     const JSStringFinalizer* fin);
-
-/**
- * Create a new JSString whose chars member may refer to external memory.
- * If a new external string is allocated, |*allocatedExternal| is set to true.
- * Otherwise the returned string is either not an external string or an
- * external string allocated by a previous call and |*allocatedExternal| is set
- * to false. If |*allocatedExternal| is false, |fin| won't be called.
- */
-extern JS_PUBLIC_API(JSString*)
-JS_NewMaybeExternalString(JSContext* cx, const char16_t* chars, size_t length,
-                          const JSStringFinalizer* fin, bool* allocatedExternal);
-
-/**
- * Return whether 'str' was created with JS_NewExternalString or
- * JS_NewExternalStringWithClosure.
- */
-extern JS_PUBLIC_API(bool)
-JS_IsExternalString(JSString* str);
-
-/**
- * Return the 'fin' arg passed to JS_NewExternalString.
- */
-extern JS_PUBLIC_API(const JSStringFinalizer*)
-JS_GetExternalStringFinalizer(JSString* str);
-
-/**
  * Set the size of the native stack that should not be exceed. To disable
  * stack size checking pass 0.
  *
  * SpiderMonkey allows for a distinction between system code (such as GCs, which
  * may incidentally be triggered by script but are not strictly performed on
  * behalf of such script), trusted script (as determined by JS_SetTrustedPrincipals),
  * and untrusted script. Each kind of code may have a different stack quota,
  * allowing embedders to keep higher-priority machinery running in the face of
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -66,43 +66,18 @@ class AtomStateEntry
     bool needsSweep() {
         JSAtom* atom = asPtrUnbarriered();
         return gc::IsAboutToBeFinalizedUnbarriered(&atom);
     }
 };
 
 struct AtomHasher
 {
-    struct Lookup
-    {
-        union {
-            const JS::Latin1Char* latin1Chars;
-            const char16_t* twoByteChars;
-        };
-        bool isLatin1;
-        size_t length;
-        const JSAtom* atom; /* Optional. */
-        JS::AutoCheckCannotGC nogc;
-
-        HashNumber hash;
-
-        MOZ_ALWAYS_INLINE Lookup(const char16_t* chars, size_t length)
-          : twoByteChars(chars), isLatin1(false), length(length), atom(nullptr)
-        {
-            hash = mozilla::HashString(chars, length);
-        }
-        MOZ_ALWAYS_INLINE Lookup(const JS::Latin1Char* chars, size_t length)
-          : latin1Chars(chars), isLatin1(true), length(length), atom(nullptr)
-        {
-            hash = mozilla::HashString(chars, length);
-        }
-        inline explicit Lookup(const JSAtom* atom);
-    };
-
-    static HashNumber hash(const Lookup& l) { return l.hash; }
+    struct Lookup;
+    static inline HashNumber hash(const Lookup& l);
     static MOZ_ALWAYS_INLINE bool match(const AtomStateEntry& entry, const Lookup& lookup);
     static void rekey(AtomStateEntry& k, const AtomStateEntry& newKey) { k = newKey; }
 };
 
 using AtomSet = JS::GCHashSet<AtomStateEntry, AtomHasher, SystemAllocPolicy>;
 
 // This class is a wrapper for AtomSet that is used to ensure the AtomSet is
 // not modified. It should only expose read-only methods from AtomSet.
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -30,16 +30,48 @@ inline JSAtom*
 js::AtomStateEntry::asPtrUnbarriered() const
 {
     MOZ_ASSERT(bits != 0);
     return reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
 }
 
 namespace js {
 
+struct AtomHasher::Lookup
+{
+    union {
+        const JS::Latin1Char* latin1Chars;
+        const char16_t* twoByteChars;
+    };
+    bool isLatin1;
+    size_t length;
+    const JSAtom* atom; /* Optional. */
+    JS::AutoCheckCannotGC nogc;
+
+    HashNumber hash;
+
+    MOZ_ALWAYS_INLINE Lookup(const char16_t* chars, size_t length)
+      : twoByteChars(chars), isLatin1(false), length(length), atom(nullptr)
+        {
+            hash = mozilla::HashString(chars, length);
+        }
+    MOZ_ALWAYS_INLINE Lookup(const JS::Latin1Char* chars, size_t length)
+      : latin1Chars(chars), isLatin1(true), length(length), atom(nullptr)
+        {
+            hash = mozilla::HashString(chars, length);
+        }
+    inline explicit Lookup(const JSAtom* atom);
+};
+
+inline HashNumber
+AtomHasher::hash(const Lookup& l)
+{
+    return l.hash;
+}
+
 inline jsid
 AtomToId(JSAtom* atom)
 {
     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
 
     uint32_t index;
     if (atom->isIndex(&index) && index <= JSID_INT_MAX)
         return INT_TO_JSID(int32_t(index));
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -15,17 +15,16 @@
  * values, called slots.  The map/slot pointer pair is GC'ed, while the map
  * is reference counted and the slot vector is malloc'ed.
  */
 
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "js/Conversions.h"
-#include "js/GCAPI.h"
 #include "js/GCVector.h"
 #include "js/HeapAPI.h"
 #include "vm/Printer.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Xdr.h"
 
 namespace JS {
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -102,23 +102,16 @@ class JS_PUBLIC_API(JSTracer);
 class JSFlatString;
 
 typedef bool                    (*JSInitCallback)(void);
 
 template<typename T> struct JSConstScalarSpec;
 typedef JSConstScalarSpec<double> JSConstDoubleSpec;
 typedef JSConstScalarSpec<int32_t> JSConstIntegerSpec;
 
-/*
- * Generic trace operation that calls JS::TraceEdge on each traceable thing's
- * location reachable from data.
- */
-typedef void
-(* JSTraceDataOp)(JSTracer* trc, void* data);
-
 namespace js {
 namespace gc {
 class AutoTraceSession;
 class StoreBuffer;
 } // namespace gc
 
 class CooperatingContext;
 
@@ -203,177 +196,18 @@ class MOZ_STACK_CLASS JS_PUBLIC_API(Auto
     ~AutoEnterCycleCollection();
 #else
   public:
     explicit AutoEnterCycleCollection(JSRuntime* rt) {}
     ~AutoEnterCycleCollection() {}
 #endif
 };
 
-class RootingContext;
-
-// Our instantiations of Rooted<void*> and PersistentRooted<void*> require an
-// instantiation of MapTypeToRootKind.
-template <>
-struct MapTypeToRootKind<void*> {
-    static const RootKind kind = RootKind::Traceable;
-};
-
-using RootedListHeads = mozilla::EnumeratedArray<RootKind, RootKind::Limit,
-                                                 Rooted<void*>*>;
-
-/*
- * This list enumerates the different types of conceptual stacks we have in
- * SpiderMonkey. In reality, they all share the C stack, but we allow different
- * stack limits depending on the type of code running.
- */
-enum StackKind
-{
-    StackForSystemCode,      // C++, such as the GC, running on behalf of the VM.
-    StackForTrustedScript,   // Script running with trusted principals.
-    StackForUntrustedScript, // Script running with untrusted principals.
-    StackKindCount
-};
-
-class JS_PUBLIC_API(AutoGCRooter);
-
-// Superclass of JSContext which can be used for rooting data in use by the
-// current thread but that does not provide all the functions of a JSContext.
-class RootingContext
-{
-    // Stack GC roots for Rooted GC heap pointers.
-    RootedListHeads stackRoots_;
-    template <typename T> friend class JS::Rooted;
-
-    // Stack GC roots for AutoFooRooter classes.
-    JS::AutoGCRooter* autoGCRooters_;
-    friend class JS::AutoGCRooter;
-
-  public:
-    RootingContext();
-
-    void traceStackRoots(JSTracer* trc);
-    void checkNoGCRooters();
-
-  protected:
-    // The remaining members in this class should only be accessed through
-    // JSContext pointers. They are unrelated to rooting and are in place so
-    // that inlined API functions can directly access the data.
-
-    /* The current compartment. */
-    JSCompartment*      compartment_;
-
-    /* The current zone. */
-    JS::Zone*           zone_;
-
-  public:
-    /* Limit pointer for checking native stack consumption. */
-    uintptr_t nativeStackLimit[StackKindCount];
-
-    static const RootingContext* get(const JSContext* cx) {
-        return reinterpret_cast<const RootingContext*>(cx);
-    }
-
-    static RootingContext* get(JSContext* cx) {
-        return reinterpret_cast<RootingContext*>(cx);
-    }
-
-    friend JSCompartment* js::GetContextCompartment(const JSContext* cx);
-    friend JS::Zone* js::GetContextZone(const JSContext* cx);
-};
-
-class JS_PUBLIC_API(AutoGCRooter)
-{
-  public:
-    AutoGCRooter(JSContext* cx, ptrdiff_t tag)
-      : AutoGCRooter(JS::RootingContext::get(cx), tag)
-    {}
-    AutoGCRooter(JS::RootingContext* cx, ptrdiff_t tag)
-      : down(cx->autoGCRooters_),
-        tag_(tag),
-        stackTop(&cx->autoGCRooters_)
-    {
-        MOZ_ASSERT(this != *stackTop);
-        *stackTop = this;
-    }
-
-    ~AutoGCRooter() {
-        MOZ_ASSERT(this == *stackTop);
-        *stackTop = down;
-    }
-
-    /* Implemented in gc/RootMarking.cpp. */
-    inline void trace(JSTracer* trc);
-    static void traceAll(const js::CooperatingContext& target, JSTracer* trc);
-    static void traceAllWrappers(const js::CooperatingContext& target, JSTracer* trc);
-
-  protected:
-    AutoGCRooter * const down;
-
-    /*
-     * Discriminates actual subclass of this being used.  If non-negative, the
-     * subclass roots an array of values of the length stored in this field.
-     * If negative, meaning is indicated by the corresponding value in the enum
-     * below.  Any other negative value indicates some deeper problem such as
-     * memory corruption.
-     */
-    ptrdiff_t tag_;
-
-    enum {
-        VALARRAY =     -2, /* js::AutoValueArray */
-        PARSER =       -3, /* js::frontend::Parser */
-        VALVECTOR =   -10, /* js::AutoValueVector */
-        IDVECTOR =    -11, /* js::AutoIdVector */
-        OBJVECTOR =   -14, /* js::AutoObjectVector */
-        IONMASM =     -19, /* js::jit::MacroAssembler */
-        WRAPVECTOR =  -20, /* js::AutoWrapperVector */
-        WRAPPER =     -21, /* js::AutoWrapperRooter */
-        CUSTOM =      -26  /* js::CustomAutoRooter */
-    };
-
-    static ptrdiff_t GetTag(const Value& value) { return VALVECTOR; }
-    static ptrdiff_t GetTag(const jsid& id) { return IDVECTOR; }
-    static ptrdiff_t GetTag(JSObject* obj) { return OBJVECTOR; }
-
-  private:
-    AutoGCRooter ** const stackTop;
-
-    /* No copy or assignment semantics. */
-    AutoGCRooter(AutoGCRooter& ida) = delete;
-    void operator=(AutoGCRooter& ida) = delete;
-};
-
 } /* namespace JS */
 
-namespace js {
-
-/*
- * Inlinable accessors for JSContext.
- *
- * - These must not be available on the more restricted superclasses of
- *   JSContext, so we can't simply define them on RootingContext.
- *
- * - They're perfectly ordinary JSContext functionality, so ought to be
- *   usable without resorting to jsfriendapi.h, and when JSContext is an
- *   incomplete type.
- */
-inline JSCompartment*
-GetContextCompartment(const JSContext* cx)
-{
-    return JS::RootingContext::get(cx)->compartment_;
-}
-
-inline JS::Zone*
-GetContextZone(const JSContext* cx)
-{
-    return JS::RootingContext::get(cx)->zone_;
-}
-
-} /* namespace js */
-
 MOZ_BEGIN_EXTERN_C
 
 // Defined in NSPR prio.h.
 typedef struct PRFileDesc PRFileDesc;
 
 MOZ_END_EXTERN_C
 
 #endif /* jspubtd_h */
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -9,17 +9,16 @@
 #include <string.h>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 #include "jswrapper.h"
 
-#include "js/GCAPI.h"
 #include "vm/GlobalObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 
 WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -70,17 +70,16 @@
 #include "frontend/Parser.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/OptimizationTracking.h"
 #include "js/Debug.h"
-#include "js/GCAPI.h"
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/StructuredClone.h"
 #include "js/SweepingAPI.h"
 #include "js/TrackedOptimizationInfo.h"
 #include "perf/jsperf.h"
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/CodeCoverage.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Move.h"
 
 #include <stdio.h>
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #endif
@@ -67,35 +68,36 @@ LCovSource::LCovSource(LifoAlloc* alloc,
   : name_(name),
     outFN_(alloc),
     outFNDA_(alloc),
     numFunctionsFound_(0),
     numFunctionsHit_(0),
     outBRDA_(alloc),
     numBranchesFound_(0),
     numBranchesHit_(0),
-    outDA_(alloc),
     numLinesInstrumented_(0),
     numLinesHit_(0),
+    maxLineHit_(0),
     hasTopLevelScript_(false)
 {
 }
 
 LCovSource::LCovSource(LCovSource&& src)
   : name_(src.name_),
     outFN_(src.outFN_),
     outFNDA_(src.outFNDA_),
     numFunctionsFound_(src.numFunctionsFound_),
     numFunctionsHit_(src.numFunctionsHit_),
     outBRDA_(src.outBRDA_),
     numBranchesFound_(src.numBranchesFound_),
     numBranchesHit_(src.numBranchesHit_),
-    outDA_(src.outDA_),
+    linesHit_(Move(src.linesHit_)),
     numLinesInstrumented_(src.numLinesInstrumented_),
     numLinesHit_(src.numLinesHit_),
+    maxLineHit_(src.maxLineHit_),
     hasTopLevelScript_(src.hasTopLevelScript_)
 {
     src.name_ = nullptr;
 }
 
 LCovSource::~LCovSource()
 {
     js_delete(name_);
@@ -114,17 +116,23 @@ LCovSource::exportInto(GenericPrinter& o
     outFNDA_.exportInto(out);
     out.printf("FNF:%zu\n", numFunctionsFound_);
     out.printf("FNH:%zu\n", numFunctionsHit_);
 
     outBRDA_.exportInto(out);
     out.printf("BRF:%zu\n", numBranchesFound_);
     out.printf("BRH:%zu\n", numBranchesHit_);
 
-    outDA_.exportInto(out);
+    if (linesHit_.initialized()) {
+        for (size_t lineno = 1; lineno <= maxLineHit_; ++lineno) {
+            if (auto p = linesHit_.lookup(lineno))
+                out.printf("DA:%zu,%" PRIu64 "\n", lineno, p->value());
+        }
+    }
+
     out.printf("LF:%zu\n", numLinesInstrumented_);
     out.printf("LH:%zu\n", numLinesHit_);
 
     out.put("end_of_record\n");
 }
 
 bool
 LCovSource::writeScriptName(LSprinter& out, JSScript* script)
@@ -134,16 +142,19 @@ LCovSource::writeScriptName(LSprinter& o
         return EscapedStringPrinter(out, fun->displayAtom(), 0);
     out.printf("top-level");
     return true;
 }
 
 bool
 LCovSource::writeScript(JSScript* script)
 {
+    if (!linesHit_.initialized() && !linesHit_.init())
+        return false;
+
     numFunctionsFound_++;
     outFN_.printf("FN:%zu,", script->lineno());
     if (!writeScriptName(outFN_, script))
         return false;
     outFN_.put("\n", 1);
 
     uint64_t hits = 0;
     ScriptCounts* sc = nullptr;
@@ -165,16 +176,17 @@ LCovSource::writeScript(JSScript* script
     jssrcnote* sn = script->notes();
     if (!SN_IS_TERMINATOR(sn))
         snpc += SN_DELTA(sn);
 
     size_t lineno = script->lineno();
     jsbytecode* end = script->codeEnd();
     size_t branchId = 0;
     size_t tableswitchExitOffset = 0;
+    bool firstLineHasBeenWritten = false;
     for (jsbytecode* pc = script->code(); pc != end; pc = GetNextPc(pc)) {
         MOZ_ASSERT(script->code() <= pc && pc < end);
         JSOp op = JSOp(*pc);
         bool jump = IsJumpOpcode(op) || op == JSOP_TABLESWITCH;
         bool fallsthrough = BytecodeFallsThrough(op) && op != JSOP_GOSUB;
 
         // If the current script & pc has a hit-count report, then update the
         // current number of hits.
@@ -183,33 +195,44 @@ LCovSource::writeScript(JSScript* script
             if (counts)
                 hits = counts->numExec();
         }
 
         // If we have additional source notes, walk all the source notes of the
         // current pc.
         if (snpc <= pc) {
             size_t oldLine = lineno;
+            // Without this check, we'll never write the script's first line
+            if (lineno == script->lineno() && !firstLineHasBeenWritten)
+                oldLine = 0;
             while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
                 SrcNoteType type = SN_TYPE(sn);
                 if (type == SRC_SETLINE)
                     lineno = size_t(GetSrcNoteOffset(sn, 0));
                 else if (type == SRC_NEWLINE)
                     lineno++;
                 else if (type == SRC_TABLESWITCH)
                     tableswitchExitOffset = GetSrcNoteOffset(sn, 0);
 
                 sn = SN_NEXT(sn);
                 snpc += SN_DELTA(sn);
             }
 
             if (oldLine != lineno && fallsthrough) {
-                outDA_.printf("DA:%zu,%" PRIu64 "\n", lineno, hits);
+                auto p = linesHit_.lookupForAdd(lineno);
+                if (!p) {
+                    if (!linesHit_.add(p, lineno, hits))
+                        return false;
+                } else {
+                    p->value() += hits;
+                }
 
                 // Count the number of lines instrumented & hit.
+                firstLineHasBeenWritten = true;
+                maxLineHit_ = std::max(lineno, maxLineHit_);
                 numLinesInstrumented_++;
                 if (hits)
                     numLinesHit_++;
             }
         }
 
         // If the current instruction has thrown, then decrement the hit counts
         // with the number of throws.
@@ -227,23 +250,23 @@ LCovSource::writeScript(JSScript* script
             if (sc) {
                 const PCCounts* counts = sc->maybeGetPCCounts(script->pcToOffset(fallthroughTarget));
                 if (counts)
                     fallthroughHits = counts->numExec();
             }
 
             uint64_t taken = hits - fallthroughHits;
             outBRDA_.printf("BRDA:%zu,%zu,0,", lineno, branchId);
-            if (taken)
+            if (hits)
                 outBRDA_.printf("%" PRIu64 "\n", taken);
             else
                 outBRDA_.put("-\n", 2);
 
             outBRDA_.printf("BRDA:%zu,%zu,1,", lineno, branchId);
-            if (fallthroughHits)
+            if (hits)
                 outBRDA_.printf("%" PRIu64 "\n", fallthroughHits);
             else
                 outBRDA_.put("-\n", 2);
 
             // Count the number of branches, and the number of branches hit.
             numBranchesFound_ += 2;
             if (hits)
                 numBranchesHit_ += !!taken + !!fallthroughHits;
@@ -331,17 +354,17 @@ LCovSource::writeScript(JSScript* script
                                 fallsThroughHits = script->getHitCount(endpc);
                         }
 
                         caseHits -= fallsThroughHits;
                     }
 
                     outBRDA_.printf("BRDA:%zu,%zu,%zu,",
                                     lineno, branchId, caseId);
-                    if (caseHits)
+                    if (hits)
                         outBRDA_.printf("%" PRIu64 "\n", caseHits);
                     else
                         outBRDA_.put("-\n", 2);
 
                     numBranchesFound_++;
                     numBranchesHit_ += !!caseHits;
                     defaultHits -= caseHits;
                     caseId++;
@@ -396,35 +419,34 @@ LCovSource::writeScript(JSScript* script
                         defaultHits = counts->numExec();
                 }
                 defaultHits -= fallsThroughHits;
             }
 
             if (defaultHasOwnClause) {
                 outBRDA_.printf("BRDA:%zu,%zu,%zu,",
                                 lineno, branchId, caseId);
-                if (defaultHits)
+                if (hits)
                     outBRDA_.printf("%" PRIu64 "\n", defaultHits);
                 else
                     outBRDA_.put("-\n", 2);
                 numBranchesFound_++;
                 numBranchesHit_ += !!defaultHits;
             }
 
             // Increment the branch identifier, and go to the next instruction.
             branchId++;
             tableswitchExitOffset = 0;
         }
     }
 
     // Report any new OOM.
     if (outFN_.hadOutOfMemory() ||
         outFNDA_.hadOutOfMemory() ||
-        outBRDA_.hadOutOfMemory() ||
-        outDA_.hadOutOfMemory())
+        outBRDA_.hadOutOfMemory())
     {
         return false;
     }
 
     // If this script is the top-level script, then record it such that we can
     // assume that the code coverage report is complete, as this script has
     // references on all inner scripts.
     if (script->isTopLevel())
--- a/js/src/vm/CodeCoverage.h
+++ b/js/src/vm/CodeCoverage.h
@@ -6,16 +6,18 @@
 
 #ifndef vm_CodeCoverage_h
 #define vm_CodeCoverage_h
 
 #include "mozilla/Vector.h"
 
 #include "ds/LifoAlloc.h"
 
+#include "js/HashTable.h"
+
 #include "vm/Printer.h"
 
 struct JSCompartment;
 class JSScript;
 class JSObject;
 
 namespace js {
 
@@ -65,20 +67,24 @@ class LCovSource
     size_t numFunctionsFound_;
     size_t numFunctionsHit_;
 
     // LifoAlloc string which hold branches statistics.
     LSprinter outBRDA_;
     size_t numBranchesFound_;
     size_t numBranchesHit_;
 
-    // LifoAlloc string which hold lines statistics.
-    LSprinter outDA_;
+    // Holds lines statistics. When processing a line hit count, the hit count
+    // is added to any hit count already in the hash map so that we handle
+    // lines that belong to more than one JSScript or function in the same
+    // source file.
+    HashMap<size_t, uint64_t, DefaultHasher<size_t>, SystemAllocPolicy> linesHit_;
     size_t numLinesInstrumented_;
     size_t numLinesHit_;
+    size_t maxLineHit_;
 
     // Status flags.
     bool hasTopLevelScript_ : 1;
 };
 
 class LCovCompartment
 {
   public:
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -22,17 +22,16 @@
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/Parser.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "js/Date.h"
-#include "js/GCAPI.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/Vector.h"
 #include "proxy/ScriptedProxyHandler.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/AsyncFunction.h"
 #include "vm/AsyncIteration.h"
 #include "vm/DebuggerMemory.h"
 #include "vm/GeckoProfiler.h"
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/PodOperations.h"
 
 #include "jscntxt.h"
 
 #include "gc/Marking.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitCompartment.h"
-#include "js/GCAPI.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
 
 #include "jit/JSJitFrameIter-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -9,17 +9,16 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include "gc/Marking.h"
-#include "js/GCAPI.h"
 #include "js/UbiNode.h"
 #include "vm/GeckoProfiler.h"
 
 #include "jscntxtinlines.h"
 #include "jscompartmentinlines.h"
 
 #include "vm/GeckoProfiler-inl.h"
 
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -14,17 +14,16 @@
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsstr.h"
 
 #include "gc/Barrier.h"
 #include "gc/Heap.h"
 #include "gc/Rooting.h"
 #include "js/CharacterEncoding.h"
-#include "js/GCAPI.h"
 #include "js/RootingAPI.h"
 
 #include "vm/Printer.h"
 
 class JSDependentString;
 class JSExtensibleString;
 class JSExternalString;
 class JSInlineString;
--- a/layout/reftests/css-invalid/select/reftest.list
+++ b/layout/reftests/css-invalid/select/reftest.list
@@ -2,11 +2,11 @@ needs-focus == select-valid.html select-
 fuzzy-if(skiaContent,1,3) needs-focus == select-invalid.html select-ref.html
 fuzzy-if(skiaContent,2,6) needs-focus == select-disabled.html select-disabled-ref.html
 fuzzy-if(skiaContent,2,6) needs-focus == select-dyn-disabled.html select-disabled-ref.html
 fuzzy-if(skiaContent,1,3) needs-focus == select-dyn-not-disabled.html select-ref.html
 needs-focus == select-required-invalid.html select-required-ref.html
 needs-focus == select-required-valid.html select-required-ref.html
 needs-focus == select-required-multiple-still-valid.html select-required-multiple-ref.html
 fuzzy-if(skiaContent,1,250) needs-focus == select-required-multiple-valid.html select-required-multiple-ref.html
-fuzzy-if(skiaContent&&!Android,1,3) needs-focus == select-disabled-fieldset-1.html select-fieldset-ref.html
+fuzzy-if(skiaContent&&!Android,1,3) skip-if(Android) needs-focus == select-disabled-fieldset-1.html select-fieldset-ref.html
 fuzzy-if(skiaContent&&!Android,2,3) needs-focus == select-disabled-fieldset-2.html select-fieldset-ref.html
 fuzzy-if(skiaContent,2,5) needs-focus == select-fieldset-legend.html select-fieldset-legend-ref.html
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -4142,17 +4142,17 @@ public class BrowserApp extends GeckoApp
         showTabQueuePromptIfApplicable(intent);
 
         // GeckoApp will wrap this unsafe external intent in a SafeIntent.
         super.onNewIntent(externalIntent);
 
         if (AppConstants.MOZ_ANDROID_BEAM && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
             final GeckoBundle data = new GeckoBundle(2);
             data.putString("uri", intent.getDataString());
-            data.putInt("flags", LOAD_NEW_TAB);
+            data.putString("flags", "OPEN_NEWTAB");
             getAppEventDispatcher().dispatch("Tab:OpenUri", data);
         }
 
         // Only solicit feedback when the app has been launched from the icon shortcut.
         if (GuestSession.NOTIFICATION_INTENT.equals(action)) {
             GuestSession.onNotificationIntentReceived(this);
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -156,20 +156,16 @@ public abstract class GeckoApp extends G
     // Delay before running one-time "cleanup" tasks that may be needed
     // after a version upgrade.
     private static final int CLEANUP_DEFERRAL_SECONDS = 15;
 
     // Length of time in ms during which crashes are classified as startup crashes
     // for crash loop detection purposes.
     private static final int STARTUP_PHASE_DURATION_MS = 30 * 1000;
 
-    protected static final int LOAD_DEFAULT = 0;
-    protected static final int LOAD_NEW_TAB = 1;
-    protected static final int LOAD_SWITCH_TAB = 2;
-
     private static boolean sAlreadyLoaded;
 
     protected RelativeLayout mRootLayout;
     protected RelativeLayout mMainLayout;
 
     protected RelativeLayout mGeckoLayout;
     private OrientationEventListener mCameraOrientationEventListener;
     protected MenuPanel mMenuPanel;
@@ -1792,22 +1788,22 @@ public abstract class GeckoApp extends G
                         flags |= Tabs.LOADURL_FIRST_AFTER_ACTIVITY_UNHIDDEN;
                     }
                     Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
                 }
             });
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             final GeckoBundle data = new GeckoBundle(2);
             data.putString("uri", uri);
-            data.putInt("flags", LOAD_SWITCH_TAB);
+            data.putString("flags", "OPEN_SWITCHTAB");
             getAppEventDispatcher().dispatch("Tab:OpenUri", data);
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             final GeckoBundle data = new GeckoBundle(2);
             data.putString("uri", uri);
-            data.putInt("flags", LOAD_NEW_TAB);
+            data.putString("flags", "OPEN_NEWTAB");
             getAppEventDispatcher().dispatch("Tab:OpenUri", data);
         } else if (NotificationHelper.HELPER_BROADCAST_ACTION.equals(action)) {
             NotificationHelper.getInstance(getApplicationContext()).handleNotificationIntent(intent);
         } else if (ACTION_LAUNCH_SETTINGS.equals(action)) {
             // Check if launched from data reporting notification.
             Intent settingsIntent = new Intent(GeckoApp.this, GeckoPreferences.class);
             // Copy extras.
             settingsIntent.putExtras(intent.getUnsafe());
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1926,17 +1926,18 @@ var BrowserApp = {
           }
           this.loadURI(url, browser, params);
         }
         break;
       }
 
       case "Tab:OpenUri":
         window.browserDOMWindow.openURI(Services.io.newURI(data.uri),
-                                        /* opener */ null, data.flags,
+                                        /* opener */ null,
+                                        Ci.nsIBrowserDOMWindow[data.flags],
                                         Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
                                         /* triggeringPrincipal */ null);
         break;
 
       case "Tab:Selected":
         this._handleTabSelected(this.getTabForId(data.id));
         break;
 
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -1426,17 +1426,17 @@ public:
     BufferWriter(nsIInputStream* aInputStream,
                  void* aBuffer, int64_t aCount)
         : Runnable("BufferWriterRunnable")
         , mMonitor("BufferWriter.mMonitor")
         , mInputStream(aInputStream)
         , mBuffer(aBuffer)
         , mCount(aCount)
         , mWrittenData(0)
-        , mBufferType(mBuffer ? eExternal : eInternal)
+        , mBufferType(aBuffer ? eExternal : eInternal)
         , mAsyncResult(NS_OK)
         , mBufferSize(0)
     {
         MOZ_ASSERT(aInputStream);
         MOZ_ASSERT(aCount == -1 || aCount > 0);
         MOZ_ASSERT_IF(mBuffer, aCount > 0);
     }
 
@@ -1472,18 +1472,22 @@ public:
         return mWrittenData;
     }
 
     void*
     StealBuffer()
     {
         MOZ_ASSERT(mBufferType == eInternal);
 
+        MonitorAutoLock lock(mMonitor);
+
         void* buffer = mBuffer;
+
         mBuffer = nullptr;
+        mBufferSize = 0;
 
         return buffer;
     }
 
 private:
     ~BufferWriter()
     {
         if (mBuffer && mBufferType == eInternal) {
@@ -1546,105 +1550,106 @@ private:
         mTaskQueue = new TaskQueue(target.forget());
 
         MonitorAutoLock lock(mMonitor);
 
         nsCOMPtr<nsIRunnable> runnable = this;
         nsresult rv = mTaskQueue->Dispatch(runnable.forget());
         NS_ENSURE_SUCCESS(rv, rv);
 
-        lock.Wait();
+        rv = lock.Wait();
+        NS_ENSURE_SUCCESS(rv, rv);
 
         return mAsyncResult;
     }
 
     // This method runs on the I/O Thread when the owning thread is blocked by
     // the mMonitor. It is called multiple times until mCount is greater than 0
     // or until there is something to read in the stream.
     NS_IMETHOD
     Run() override
     {
         MOZ_ASSERT(mAsyncInputStream);
         MOZ_ASSERT(!mInputStream);
 
+        MonitorAutoLock lock(mMonitor);
+
         if (mCount == 0) {
-            OperationCompleted(NS_OK);
+            OperationCompleted(lock, NS_OK);
             return NS_OK;
         }
 
         if (mCount == -1 && !MaybeExpandBufferSize()) {
-            OperationCompleted(NS_ERROR_OUT_OF_MEMORY);
+            OperationCompleted(lock, NS_ERROR_OUT_OF_MEMORY);
             return NS_OK;
         }
 
         uint64_t offset = mWrittenData;
         uint64_t length = mCount == -1 ? BUFFER_SIZE : mCount;
 
         // Let's try to read it directly.
         uint32_t writtenData;
         nsresult rv = mAsyncInputStream->ReadSegments(NS_CopySegmentToBuffer,
                                                      static_cast<char*>(mBuffer) + offset,
                                                      length, &writtenData);
 
         // Operation completed.
         if (NS_SUCCEEDED(rv) && writtenData == 0) {
-            OperationCompleted(NS_OK);
+            OperationCompleted(lock, NS_OK);
             return NS_OK;
         }
 
         // If we succeeded, let's try to read again.
         if (NS_SUCCEEDED(rv)) {
             mWrittenData += writtenData;
             if (mCount != -1) {
                 MOZ_ASSERT(mCount >= writtenData);
                 mCount -= writtenData;
             }
 
             nsCOMPtr<nsIRunnable> runnable = this;
             rv = mTaskQueue->Dispatch(runnable.forget());
             if (NS_WARN_IF(NS_FAILED(rv))) {
-                OperationCompleted(rv);
+                OperationCompleted(lock, rv);
             }
         
             return NS_OK;
         }
 
         // Async wait...
         if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
             rv = mAsyncInputStream->AsyncWait(this, 0, length, mTaskQueue);
             if (NS_WARN_IF(NS_FAILED(rv))) {
-                OperationCompleted(rv);
+                OperationCompleted(lock, rv);
             }
             return NS_OK;
         }
 
         // Error.
-        OperationCompleted(rv);
+        OperationCompleted(lock, rv);
         return NS_OK;
     }
 
     NS_IMETHOD
     OnInputStreamReady(nsIAsyncInputStream* aStream) override
     {
         MOZ_ASSERT(aStream == mAsyncInputStream);
         // The stream is ready, let's read it again.
         return Run();
     }
 
     // This function is called from the I/O thread and it will unblock the
     // owning thread.
     void
-    OperationCompleted(nsresult aRv)
+    OperationCompleted(MonitorAutoLock& aLock, nsresult aRv)
     {
-        MonitorAutoLock lock(mMonitor);
-
         mAsyncResult = aRv;
 
         // This will unlock the owning thread.
-        lock.Notify();
+        aLock.Notify();
     }
 
     bool
     MaybeExpandBufferSize()
     {
         MOZ_ASSERT(mCount == -1);
 
         if (mBufferSize >= mWrittenData + BUFFER_SIZE) {
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -54,16 +54,18 @@ from ..frontend.data import (
     HostSimpleProgram,
     HostSources,
     InstallationTarget,
     JARManifest,
     JavaJarData,
     Library,
     Linkable,
     LocalInclude,
+    LocalizedFiles,
+    LocalizedPreprocessedFiles,
     ObjdirFiles,
     ObjdirPreprocessedFiles,
     PerSourceFlag,
     Program,
     RustLibrary,
     HostRustLibrary,
     RustProgram,
     RustTest,
@@ -640,16 +642,22 @@ class RecursiveMakeBackend(CommonBackend
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, ObjdirFiles):
             self._process_objdir_files(obj, obj.files, backend_file)
 
         elif isinstance(obj, ObjdirPreprocessedFiles):
             self._process_final_target_pp_files(obj, obj.files, backend_file, 'OBJDIR_PP_FILES')
 
+        elif isinstance(obj, LocalizedFiles):
+            self._process_localized_files(obj, obj.files, backend_file)
+
+        elif isinstance(obj, LocalizedPreprocessedFiles):
+            self._process_localized_pp_files(obj, obj.files, backend_file)
+
         elif isinstance(obj, FinalTargetFiles):
             self._process_final_target_files(obj, obj.files, backend_file)
 
         elif isinstance(obj, FinalTargetPreprocessedFiles):
             self._process_final_target_pp_files(obj, obj.files, backend_file, 'DIST_FILES')
 
         elif isinstance(obj, AndroidResDirs):
             # Order matters.
@@ -1448,16 +1456,64 @@ class RecursiveMakeBackend(CommonBackend
             for f in files:
                 backend_file.write('%s += %s\n' % (
                     var, self._pretty_path(f, backend_file)))
             backend_file.write('%s_PATH := $(DEPTH)/%s\n'
                                % (var, mozpath.join(obj.install_target, path)))
             backend_file.write('%s_TARGET := misc\n' % var)
             backend_file.write('PP_TARGETS += %s\n' % var)
 
+    def _write_localized_files_files(self, files, name, backend_file):
+        for f in files:
+            # The emitter asserts that all files start with `en-US/`
+            e, f = f.split('en-US/')
+            assert(not e)
+            if '*' in f:
+                # We can't use MERGE_FILE for wildcards because it takes
+                # only the first match internally. This is only used
+                # in one place in the tree currently so we'll hardcode
+                # that specific behavior for now.
+                backend_file.write('%s += $(wildcard $(LOCALE_SRCDIR)/%s)\n' % (name, f))
+            else:
+                backend_file.write('%s += $(call MERGE_FILE,%s)\n' % (name, f))
+
+    def _process_localized_files(self, obj, files, backend_file):
+        target = obj.install_target
+        path = mozpath.basedir(target, ('dist/bin', ))
+        if not path:
+            raise Exception('Cannot install localized files to ' + target)
+        for i, (path, files) in enumerate(files.walk()):
+            name = 'LOCALIZED_FILES_%d' % i
+            self._no_skip['libs'].add(backend_file.relobjdir)
+            self._write_localized_files_files(files, name + '_FILES', backend_file)
+            # Use FINAL_TARGET here because some l10n repack rules set
+            # XPI_NAME to generate langpacks.
+            backend_file.write('%s_DEST = $(FINAL_TARGET)/%s\n' % (name, path))
+            backend_file.write('%s_TARGET := libs\n' % name)
+            backend_file.write('INSTALL_TARGETS += %s\n' % name)
+
+    def _process_localized_pp_files(self, obj, files, backend_file):
+        target = obj.install_target
+        path = mozpath.basedir(target, ('dist/bin', ))
+        if not path:
+            raise Exception('Cannot install localized files to ' + target)
+        for i, (path, files) in enumerate(files.walk()):
+            name = 'LOCALIZED_PP_FILES_%d' % i
+            self._no_skip['libs'].add(backend_file.relobjdir)
+            self._write_localized_files_files(files, name, backend_file)
+            # Use FINAL_TARGET here because some l10n repack rules set
+            # XPI_NAME to generate langpacks.
+            backend_file.write('%s_PATH = $(FINAL_TARGET)/%s\n' % (name, path))
+            backend_file.write('%s_TARGET := libs\n' % name)
+            # Localized files will have different content in different
+            # localizations, and some preprocessed files may not have
+            # any preprocessor directives.
+            backend_file.write('%s_FLAGS := --silence-missing-directive-warnings\n' % name)
+            backend_file.write('PP_TARGETS += %s\n' % name)
+
     def _process_objdir_files(self, obj, files, backend_file):
         # We can't use an install manifest for the root of the objdir, since it
         # would delete all the other files that get put there by the build
         # system.
         for i, (path, files) in enumerate(files.walk()):
             self._no_skip['misc'].add(backend_file.relobjdir)
             for f in files:
                 backend_file.write('OBJDIR_%d_FILES += %s\n' % (
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1410,16 +1410,63 @@ VARIABLES = {
            FINAL_TARGET_FILES += ['foo.png']
            FINAL_TARGET_FILES.images['do-not-use'] += ['bar.svg']
         """),
 
     'FINAL_TARGET_PP_FILES': (ContextDerivedTypedHierarchicalStringList(Path), list,
         """Like ``FINAL_TARGET_FILES``, with preprocessing.
         """),
 
+    'LOCALIZED_FILES': (ContextDerivedTypedHierarchicalStringList(Path), list,
+        """List of locale-dependent files to be installed into the application
+        directory.
+
+        This functions similarly to ``FINAL_TARGET_FILES``, but the files are
+        sourced from the locale directory and will vary per localization.
+        For an en-US build, this is functionally equivalent to
+        ``FINAL_TARGET_FILES``. For a build with ``--enable-ui-locale``,
+        the file will be taken from ``$LOCALE_SRCDIR``, with the leading
+        ``en-US`` removed. For a l10n repack of an en-US build, the file
+        will be taken from the first location where it exists from:
+        * the merged locale directory if it exists
+        * ``$LOCALE_SRCDIR`` with the leading ``en-US`` removed
+        * the in-tree en-US location
+
+        Paths specified here must be relative to the source directory and must
+        include a leading ``en-US``. Wildcards are allowed, and will be
+        expanded at the time of locale packaging to match files in the
+        locale directory.
+
+        Files that are missing from a locale will typically have the en-US
+        version used, but for wildcard expansions only files from the
+        locale directory will be used, even if that means no files will
+        be copied.
+
+        Example::
+
+           LOCALIZED_FILES.foo += [
+             'en-US/foo.js',
+             'en-US/things/*.ini',
+           ]
+
+        If this was placed in ``toolkit/locales/moz.build``, it would copy
+        ``toolkit/locales/en-US/foo.js`` and
+        ``toolkit/locales/en-US/things/*.ini`` to ``$(DIST)/bin/foo`` in an
+        en-US build, and in a build of a different locale (or a repack),
+        it would copy ``$(LOCALE_SRCDIR)/toolkit/foo.js`` and
+        ``$(LOCALE_SRCDIR)/toolkit/things/*.ini``.
+        """),
+
+    'LOCALIZED_PP_FILES': (ContextDerivedTypedHierarchicalStringList(Path), list,
+        """Like ``LOCALIZED_FILES``, with preprocessing.
+
+        Note that the ``AB_CD`` define is available and expands to the current
+        locale being packaged, as with preprocessed entries in jar manifests.
+        """),
+
     'OBJDIR_FILES': (ContextDerivedTypedHierarchicalStringList(Path), list,
         """List of files to be installed anywhere in the objdir. Use sparingly.
 
         ``OBJDIR_FILES`` is similar to FINAL_TARGET_FILES, but it allows copying
         anywhere in the object directory. This is intended for various one-off
         cases, not for general use. If you wish to add entries to OBJDIR_FILES,
         please consult a build peer.
         """),
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -975,16 +975,29 @@ class FinalTargetPreprocessedFiles(Conte
     FINAL_TARGET_PP_FILES.
     """
     __slots__ = ('files')
 
     def __init__(self, sandbox, files):
         ContextDerived.__init__(self, sandbox)
         self.files = files
 
+class LocalizedFiles(FinalTargetFiles):
+    """Sandbox container object for LOCALIZED_FILES, which is a
+    HierarchicalStringList.
+    """
+    pass
+
+
+class LocalizedPreprocessedFiles(FinalTargetPreprocessedFiles):
+    """Sandbox container object for LOCALIZED_PP_FILES, which is a
+    HierarchicalStringList.
+    """
+    pass
+
 
 class ObjdirFiles(FinalTargetFiles):
     """Sandbox container object for OBJDIR_FILES, which is a
     HierarchicalStringList.
     """
     @property
     def install_target(self):
         return ''
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -52,16 +52,18 @@ from .data import (
     HostSimpleProgram,
     HostSources,
     InstallationTarget,
     IPDLFile,
     JARManifest,
     Library,
     Linkable,
     LocalInclude,
+    LocalizedFiles,
+    LocalizedPreprocessedFiles,
     ObjdirFiles,
     ObjdirPreprocessedFiles,
     PerSourceFlag,
     PreprocessedTestWebIDLFile,
     PreprocessedWebIDLFile,
     Program,
     RustLibrary,
     HostRustLibrary,
@@ -1074,16 +1076,18 @@ class TreeMetadataEmitter(LoggingMixin):
         generated_files.update(['%s%s' % (k, self.config.substs.get('BIN_SUFFIX', '')) for k in self._binaries.keys()])
 
         components = []
         for var, cls in (
             ('BRANDING_FILES', BrandingFiles),
             ('EXPORTS', Exports),
             ('FINAL_TARGET_FILES', FinalTargetFiles),
             ('FINAL_TARGET_PP_FILES', FinalTargetPreprocessedFiles),
+            ('LOCALIZED_FILES', LocalizedFiles),
+            ('LOCALIZED_PP_FILES', LocalizedPreprocessedFiles),
             ('OBJDIR_FILES', ObjdirFiles),
             ('OBJDIR_PP_FILES', ObjdirPreprocessedFiles),
             ('TEST_HARNESS_FILES', TestHarnessFiles),
         ):
             all_files = context.get(var)
             if not all_files:
                 continue
             if dist_install is False and var != 'TEST_HARNESS_FILES':
@@ -1098,23 +1102,29 @@ class TreeMetadataEmitter(LoggingMixin):
                         'Cannot install files to the root of TEST_HARNESS_FILES', context)
                 if base == 'components':
                     components.extend(files)
                 if base == 'defaults/pref':
                     has_prefs = True
                 if mozpath.split(base)[0] == 'res':
                     has_resources = True
                 for f in files:
-                    if ((var == 'FINAL_TARGET_PP_FILES' or
-                         var == 'OBJDIR_PP_FILES') and
+                    if (var in ('FINAL_TARGET_PP_FILES',
+                                'OBJDIR_PP_FILES',
+                                'LOCALIZED_FILES',
+                                'LOCALIZED_PP_FILES') and
                         not isinstance(f, SourcePath)):
                         raise SandboxValidationError(
                                 ('Only source directory paths allowed in ' +
                                  '%s: %s')
                                 % (var, f,), context)
+                    if var.startswith('LOCALIZED_') and not f.startswith('en-US/'):
+                        raise SandboxValidationError(
+                                '%s paths must start with `en-US/`: %s'
+                                % (var, f,), context)
                     if not isinstance(f, ObjDirPath):
                         path = f.full_path
                         if '*' not in path and not os.path.exists(path):
                             raise SandboxValidationError(
                                 'File listed in %s does not exist: %s'
                                 % (var, path), context)
                     else:
                         # TODO: Bug 1254682 - The '/' check is to allow
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/localized-files/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+LOCALIZED_FILES += [
+    'en-US/abc/*.abc',
+    'en-US/bar.ini',
+    'en-US/foo.js',
+]
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/localized-pp-files/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+LOCALIZED_PP_FILES += [
+    'en-US/bar.ini',
+    'en-US/foo.js',
+]
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -839,16 +839,54 @@ class TestRecursiveMakeBackend(BackendTe
             'DIST_FILES_0_PATH := $(DEPTH)/dist/bin/',
             'DIST_FILES_0_TARGET := misc',
             'PP_TARGETS += DIST_FILES_0',
         ]
 
         found = [str for str in lines if 'DIST_FILES' in str]
         self.assertEqual(found, expected)
 
+    def test_localized_files(self):
+        """Test that LOCALIZED_FILES is written to backend.mk correctly."""
+        env = self._consume('localized-files', RecursiveMakeBackend)
+
+        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
+
+        expected = [
+            'LOCALIZED_FILES_0_FILES += $(wildcard $(LOCALE_SRCDIR)/abc/*.abc)',
+            'LOCALIZED_FILES_0_FILES += $(call MERGE_FILE,bar.ini)',
+            'LOCALIZED_FILES_0_FILES += $(call MERGE_FILE,foo.js)',
+            'LOCALIZED_FILES_0_DEST = $(FINAL_TARGET)/',
+            'LOCALIZED_FILES_0_TARGET := libs',
+            'INSTALL_TARGETS += LOCALIZED_FILES_0',
+        ]
+
+        found = [str for str in lines if 'LOCALIZED_FILES' in str]
+        self.assertEqual(found, expected)
+
+    def test_localized_pp_files(self):
+        """Test that LOCALIZED_PP_FILES is written to backend.mk correctly."""
+        env = self._consume('localized-pp-files', RecursiveMakeBackend)
+
+        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
+
+        expected = [
+            'LOCALIZED_PP_FILES_0 += $(call MERGE_FILE,bar.ini)',
+            'LOCALIZED_PP_FILES_0 += $(call MERGE_FILE,foo.js)',
+            'LOCALIZED_PP_FILES_0_PATH = $(FINAL_TARGET)/',
+            'LOCALIZED_PP_FILES_0_TARGET := libs',
+            'LOCALIZED_PP_FILES_0_FLAGS := --silence-missing-directive-warnings',
+            'PP_TARGETS += LOCALIZED_PP_FILES_0',
+        ]
+
+        found = [str for str in lines if 'LOCALIZED_PP_FILES' in str]
+        self.assertEqual(found, expected)
+
     def test_config(self):
         """Test that CONFIGURE_SUBST_FILES are properly handled."""
         env = self._consume('test_config', RecursiveMakeBackend)
 
         self.assertEqual(
             open(os.path.join(env.topobjdir, 'file'), 'r').readlines(), [
                 '#ifdef foo\n',
                 'bar baz\n',
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/localized-files-no-en-us/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+LOCALIZED_FILES.foo += [
+    'en-US/bar.ini',
+    'foo.js',
+]
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/localized-files/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+LOCALIZED_FILES.foo += [
+    'en-US/bar.ini',
+    'en-US/code/*.js',
+    'en-US/foo.js',
+]
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/localized-pp-files/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+LOCALIZED_PP_FILES.foo += [
+    'en-US/bar.ini',
+    'en-US/foo.js',
+]
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -28,16 +28,18 @@ from mozbuild.frontend.data import (
     HostDefines,
     HostRustLibrary,
     HostRustProgram,
     HostSources,
     IPDLFile,
     JARManifest,
     LinkageMultipleRustLibrariesError,
     LocalInclude,
+    LocalizedFiles,
+    LocalizedPreprocessedFiles,
     Program,
     RustLibrary,
     RustProgram,
     SharedLibrary,
     SimpleProgram,
     Sources,
     StaticLibrary,
     TestHarnessFiles,
@@ -1230,16 +1232,56 @@ class TestEmitterBasic(unittest.TestCase
 
     def test_final_target_pp_files_non_srcdir(self):
         '''Test that non-srcdir paths in FINAL_TARGET_PP_FILES throws errors.'''
         reader = self.reader('final-target-pp-files-non-srcdir')
         with self.assertRaisesRegexp(SandboxValidationError,
              'Only source directory paths allowed in FINAL_TARGET_PP_FILES:'):
             self.read_topsrcdir(reader)
 
+    def test_localized_files(self):
+        """Test that LOCALIZED_FILES works properly."""
+        reader = self.reader('localized-files')
+        objs = self.read_topsrcdir(reader)
+
+        self.assertEqual(len(objs), 1)
+        self.assertIsInstance(objs[0], LocalizedFiles)
+
+        for path, files in objs[0].files.walk():
+            self.assertEqual(path, 'foo')
+            self.assertEqual(len(files), 3)
+
+            expected = {'en-US/bar.ini', 'en-US/code/*.js', 'en-US/foo.js'}
+            for f in files:
+                self.assertTrue(unicode(f) in expected)
+
+    def test_localized_files_no_en_us(self):
+        """Test that LOCALIZED_FILES errors if a path does not start with
+        `en-US/`."""
+        reader = self.reader('localized-files-no-en-us')
+        with self.assertRaisesRegexp(SandboxValidationError,
+             'LOCALIZED_FILES paths must start with `en-US/`:'):
+            objs = self.read_topsrcdir(reader)
+
+    def test_localized_pp_files(self):
+        """Test that LOCALIZED_PP_FILES works properly."""
+        reader = self.reader('localized-pp-files')
+        objs = self.read_topsrcdir(reader)
+
+        self.assertEqual(len(objs), 1)
+        self.assertIsInstance(objs[0], LocalizedPreprocessedFiles)
+
+        for path, files in objs[0].files.walk():
+            self.assertEqual(path, 'foo')
+            self.assertEqual(len(files), 2)
+
+            expected = {'en-US/bar.ini', 'en-US/foo.js'}
+            for f in files:
+                self.assertTrue(unicode(f) in expected)
+
     def test_rust_library_no_cargo_toml(self):
         '''Test that defining a RustLibrary without a Cargo.toml fails.'''
         reader = self.reader('rust-library-no-cargo-toml')
         with self.assertRaisesRegexp(SandboxValidationError,
              'No Cargo.toml file found'):
             self.read_topsrcdir(reader)
 
     def test_rust_library_name_mismatch(self):
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -427,17 +427,17 @@ win64-rusttests/opt:
 win64-ccov/debug:
     description: "Win64 Debug Code Coverage"
     index:
         product: firefox
         job-name: win64-ccov-debug
     treeherder:
         platform: windows2012-64/ccov
         symbol: tc(B)
-        tier: 2
+        tier: 3
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
         max-run-time: 14400
         env:
             TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
--- a/taskcluster/ci/test/compiled.yml
+++ b/taskcluster/ci/test/compiled.yml
@@ -57,16 +57,17 @@ jittest:
     treeherder-symbol: tc(Jit)
     run-on-projects:
         by-test-platform:
             linux64-qr/.*: ['mozilla-central', 'try']
             default: built-projects
     chunks:
         by-test-platform:
             windows.*: 1
+            windows10-64-ccov/debug: 6
             macosx.*: 1
             default: 6
     mozharness:
         chunked:
             by-test-platform:
                 windows.*: false
                 macosx.*: false
                 default: true
--- a/taskcluster/ci/test/mochitest.yml
+++ b/taskcluster/ci/test/mochitest.yml
@@ -34,16 +34,17 @@ mochitest:
             default: default
     chunks:
         by-test-platform:
             android-4.3-arm7-api-16/debug: 48
             android.*: 20
             linux.*/debug: 16
             linux64-asan/opt: 10
             linux64-.*cov/opt: 10
+            windows10-64-ccov/debug: 10
             default: 5
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows7-32/debug: both
             default: true
     max-run-time:
         by-test-platform:
@@ -99,16 +100,17 @@ mochitest-browser-chrome:
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows7-32/debug: both
             default: true
     max-run-time:
         by-test-platform:
             linux64-*cov/opt: 7200
+            windows10-64-ccov/debug: 7200
             linux.*/debug: 5400
             default: 3600
     mozharness:
         mochitest-flavor: browser
         chunked: true
     # Bug 1281241: migrating to m3.large instances
     instance-size:
         by-test-platform:
--- a/taskcluster/ci/test/reftest.yml
+++ b/taskcluster/ci/test/reftest.yml
@@ -48,16 +48,17 @@ jsreftest:
         by-test-platform:
             android.*: xlarge
             default: default
     chunks:
         by-test-platform:
             android-4.3-arm7-api-16/debug: 100
             android.*: 40
             windows.*: 2
+            windows10-64-ccov/debug: 5
             linux64-ccov/.*: 5
             linux64-qr/.*: 4
             macosx.*: 2
             default: 3
     e10s:
         by-test-platform:
             linux64-jsdcov/opt: false
             windows10-64/debug: both
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -100,43 +100,16 @@ linux64-stylo-disabled/debug:
         - stylo-disabled-tests
         - devtools-tests
 linux64-stylo-disabled/opt:
     build-platform: linux64/opt
     test-sets:
         - awsy-stylo-disabled
         - stylo-disabled-tests
         - devtools-tests
-macosx64-stylo-disabled/debug:
-    build-platform: macosx64/debug
-    test-sets:
-        - stylo-disabled-tests
-macosx64-stylo-disabled/opt:
-    build-platform: macosx64/opt
-    test-sets:
-        - awsy-stylo-disabled
-        - stylo-disabled-tests
-windows7-32-stylo-disabled/debug:
-    build-platform: win32/debug
-    test-sets:
-        - stylo-disabled-tests
-windows7-32-stylo-disabled/opt:
-    build-platform: win32/opt
-    test-sets:
-        - awsy-stylo-disabled
-        - stylo-disabled-tests
-windows10-64-stylo-disabled/debug:
-    build-platform: win64/debug
-    test-sets:
-        - stylo-disabled-tests
-windows10-64-stylo-disabled/opt:
-    build-platform: win64/opt
-    test-sets:
-        - awsy-stylo-disabled
-        - stylo-disabled-tests
 
 # Stylo sequential runs check memory and performance when using a single thread.
 linux64-stylo-sequential/opt:
     build-platform: linux64/opt
     test-sets:
         - awsy-stylo-sequential
 
 # QR builds just run a subset right now. Note that the tests in this
@@ -176,27 +149,25 @@ windows7-32/debug:
 
 windows7-32/opt:
     build-platform: win32/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-reftest-gpu
         - windows-talos
-        - windows-talos-stylo-disabled
         - windows-tests
 
 windows7-32-pgo/opt:
     build-platform: win32-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-reftest-gpu
         - windows-talos
-        - windows-talos-stylo-disabled
         - windows-tests
 
 windows7-32-nightly/opt:
     build-platform: win32-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-reftest-gpu
@@ -226,27 +197,25 @@ windows10-64/debug:
         - mochitest-headless
 
 windows10-64/opt:
     build-platform: win64/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-talos
-        - windows-talos-stylo-disabled
         - windows-tests
         - mochitest-headless
 
 windows10-64-pgo/opt:
     build-platform: win64-pgo/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - windows-talos
-        - windows-talos-stylo-disabled
         - windows-tests
 
 windows10-64-nightly/opt:
     build-platform: win64-nightly/opt
     test-sets:
         - awsy
         - desktop-screenshot-capture
         - desktop-instrumentation
@@ -270,17 +239,16 @@ macosx64/debug:
     build-platform: macosx64/debug
     test-sets:
         - macosx64-tests
 
 macosx64/opt:
     build-platform: macosx64/opt
     test-sets:
         - macosx64-talos
-        - macosx64-talos-stylo-disabled
         - macosx64-tests
         - desktop-screenshot-capture
         - awsy
 
 macosx64-nightly/opt:
     build-platform: macosx64-nightly/opt
     test-sets:
         - macosx64-tests
--- a/taskcluster/ci/test/xpcshell.yml
+++ b/taskcluster/ci/test/xpcshell.yml
@@ -39,16 +39,17 @@ xpcshell:
             default: built-projects
     chunks:
         by-test-platform:
             linux32/debug: 12
             linux64/debug: 10
             android-4.2-x86/opt: 6
             macosx.*: 1
             windows.*: 1
+            windows10-64-ccov/debug: 8
             default: 8
     instance-size:
         by-test-platform:
             android.*: xlarge
             default: default
     max-run-time: 5400
     e10s: false
     allow-software-gl-layers: false
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -567,16 +567,22 @@ def set_tier(config, tests):
                                          'macosx64-stylo-disabled/debug',
                                          'macosx64-stylo-disabled/opt',
                                          'android-4.3-arm7-api-16/opt',
                                          'android-4.3-arm7-api-16/debug',
                                          'android-4.2-x86/opt']:
                 test['tier'] = 1
             else:
                 test['tier'] = 2
+
+        # Temporarily set windows10-64-ccov/debug tests as tier 3, until we get the tests
+        # consistently green.
+        if test['test-platform'] == 'windows10-64-ccov/debug':
+            test['tier'] = 3
+
         yield test
 
 
 @transforms.add
 def set_expires_after(config, tests):
     """Try jobs expire after 2 weeks; everything else lasts 1 year.  This helps
     keep storage costs low."""
     for test in tests:
--- a/testing/mozharness/scripts/release/push-candidate-to-releases.py
+++ b/testing/mozharness/scripts/release/push-candidate-to-releases.py
@@ -33,17 +33,17 @@ class ReleasePusher(BaseScript, Virtuale
             "dest": "credentials",
             "help": "File containing access key and secret access key",
         }],
         [["--exclude"], {
             "dest": "excludes",
             "default": [
                 r"^.*tests.*$",
                 r"^.*crashreporter.*$",
-                r"^.*[^k]\.zip(\.asc)?$",
+                r"^(?!.*jsshell-).*\.zip(\.asc)?$",
                 r"^.*\.log$",
                 r"^.*\.txt$",
                 r"^.*/partner-repacks.*$",
                 r"^.*.checksums(\.asc)?$",
                 r"^.*/logs/.*$",
                 r"^.*json$",
                 r"^.*/host.*$",
                 r"^.*/mar-tools/.*$",
deleted file mode 100644
--- a/testing/web-platform/meta/dom/events/EventTarget-constructible.any.js.ini
+++ /dev/null
@@ -1,17 +0,0 @@
-[EventTarget-constructible.any.html]
-  type: testharness
-  [A constructed EventTarget can be used as expected]
-    expected: FAIL
-
-  [EventTarget can be subclassed]
-    expected: FAIL
-
-
-[EventTarget-constructible.any.worker.html]
-  type: testharness
-  [A constructed EventTarget can be used as expected]
-    expected: FAIL
-
-  [EventTarget can be subclassed]
-    expected: FAIL
-
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -17,58 +17,19 @@
     bug: 931884
 
   [Element interface: element must inherit property "assignedSlot" with the proper type (48)]
     expected: FAIL
 
   [Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type (2)]
     expected: FAIL
 
-  [EventTarget must be primary interface of new EventTarget()]
-    expected: FAIL
-
-  [Stringification of new EventTarget()]
-    expected: FAIL
-
-  [EventTarget interface: new EventTarget() must inherit property "addEventListener" with the proper type (0)]
-    expected: FAIL
-
-  [EventTarget interface: calling addEventListener(DOMString,EventListener,[object Object\],[object Object\]) on new EventTarget() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [EventTarget interface: new EventTarget() must inherit property "removeEventListener" with the proper type (1)]
-    expected: FAIL
-
-  [EventTarget interface: calling removeEventListener(DOMString,EventListener,[object Object\],[object Object\]) on new EventTarget() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [EventTarget interface: new EventTarget() must inherit property "dispatchEvent" with the proper type (2)]
-    expected: FAIL
-
-  [EventTarget interface: calling dispatchEvent(Event) on new EventTarget() with too few arguments must throw TypeError]
-    expected: FAIL
-
   [AbortSignal interface: new AbortController().signal must inherit property "onabort" with the proper type (1)]
     expected: FAIL
 
-  [EventTarget interface: new EventTarget() must inherit property "addEventListener(DOMString, EventListener, [object Object\],[object Object\])" with the proper type]
-    expected: FAIL
-
-  [EventTarget interface: calling addEventListener(DOMString, EventListener, [object Object\],[object Object\]) on new EventTarget() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [EventTarget interface: new EventTarget() must inherit property "removeEventListener(DOMString, EventListener, [object Object\],[object Object\])" with the proper type]
-    expected: FAIL
-
-  [EventTarget interface: calling removeEventListener(DOMString, EventListener, [object Object\],[object Object\]) on new EventTarget() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [EventTarget interface: new EventTarget() must inherit property "dispatchEvent(Event)" with the proper type]
-    expected: FAIL
-
   [AbortSignal interface: new AbortController().signal must inherit property "onabort" with the proper type]
     expected: FAIL
 
   [Document interface: new Document() must inherit property "origin" with the proper type]
     expected: FAIL
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type]
     expected: FAIL
--- a/testing/web-platform/tests/eventsource/interfaces.html
+++ b/testing/web-platform/tests/eventsource/interfaces.html
@@ -4,17 +4,17 @@
 <script src=/resources/testharnessreport.js></script>
 <script src=/resources/WebIDLParser.js></script>
 <script src=/resources/idlharness.js></script>
 
 <h1>EventSource IDL tests</h1>
 <div id=log></div>
 
 <script type=text/plain>
-[Exposed=(Window,Worker)]
+[Constructor(), Exposed=(Window,Worker)]
 interface EventTarget {
   void addEventListener(DOMString type, EventListener? listener, optional (AddEventListenerOptions or boolean) options);
   void removeEventListener(DOMString type, EventListener? listener, optional (EventListenerOptions or boolean) options);
   boolean dispatchEvent(Event event);
 };
 callback interface EventListener {
   void handleEvent(Event event);
 };
--- a/testing/web-platform/tests/interfaces/dedicated-workers.idl
+++ b/testing/web-platform/tests/interfaces/dedicated-workers.idl
@@ -1,12 +1,12 @@
 // -----------------------------------------------------------------------------
 // DOM
 // -----------------------------------------------------------------------------
-[Exposed=(Window,Worker)]
+[Constructor(), Exposed=(Window,Worker)]
 interface EventTarget {
   void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);
   void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
   boolean dispatchEvent(Event event);
 };
 
 callback interface EventListener {
   void handleEvent(Event event);
--- a/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-idls.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-idls.js
@@ -88,17 +88,17 @@ interface ServiceWorkerRegistration : Ev
 
   [NewObject] Promise<void> update();
   [NewObject] Promise<boolean> unregister();
 
   // event
   attribute EventHandler onupdatefound;
 };
 
-[Exposed=(Window,Worker)]
+[Constructor(), Exposed=(Window,Worker)]
 interface EventTarget {
   void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);
   void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
   boolean dispatchEvent(Event event);
 };
 
 [SecureContext, Exposed=(Window,Worker)]
 interface NavigationPreloadManager {
--- a/testing/web-platform/tests/webrtc/RTCPeerConnection-idl.html
+++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-idl.html
@@ -13,16 +13,17 @@
 <div id='log'></div>
 <script src=/resources/testharness.js></script>
 <script src=/resources/testharnessreport.js></script>
 <script src=/resources/WebIDLParser.js></script>
 <script src=/resources/idlharness.js></script>
 
 <!-- The IDL is copied from the 22 September 2015 editors' draft. -->
 <script type="text/plain">
+[Constructor()]
 interface EventTarget {
     // Only a dummy definition is needed here.
 };
 [ Constructor (optional RTCConfiguration configuration)]
 interface RTCPeerConnection : EventTarget  {
     Promise<RTCSessionDescription> createOffer (optional RTCOfferOptions options);
     Promise<RTCSessionDescription> createAnswer (optional RTCAnswerOptions options);
     Promise<void>                  setLocalDescription (RTCSessionDescription description);
--- a/toolkit/components/telemetry/GCTelemetry.jsm
+++ b/toolkit/components/telemetry/GCTelemetry.jsm
@@ -4,22 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /**
  * This module records detailed timing information about selected
  * GCs. The data is sent back in the telemetry session ping. To avoid
  * bloating the ping, only a few GCs are included. There are two
- * selection strategies. We always save the five GCs with the worst
- * max_pause time. Additionally, five collections are selected at
+ * selection strategies. We always save the two GCs with the worst
+ * max_pause time. Additionally, two collections are selected at
  * random. If a GC runs for C milliseconds and the total time for all
  * GCs since the session began is T milliseconds, then the GC has a
- * 5*C/T probablility of being selected (the factor of 5 is because we
- * save 5 of them).
+ * 2*C/T probablility of being selected (the factor of 2 is because we
+ * save two of them).
  *
  * GCs from both the main process and all content processes are
  * recorded. The data is cleared for each new subsession.
  */
 
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm", this);
@@ -77,17 +77,17 @@ class GCData {
     // practice.
     for (let i = 0; i < this.randomlySelected.length; i++) {
       let r = Math.random();
       if (r <= prob) {
         this.randomlySelected[i] = data;
       }
     }
 
-    // Save the 5 worst GCs based on max_pause. A GC may appear in
+    // Save the 2 worst GCs based on max_pause. A GC may appear in
     // both worst and randomlySelected.
     for (let i = 0; i < this.worst.length; i++) {
       if (!this.worst[i]) {
         this.worst[i] = data;
         break;
       }
 
       if (this.worst[i].max_pause < data.max_pause) {
@@ -105,19 +105,19 @@ class GCData {
     };
   }
 }
 
 // If you adjust any of the constants here (slice limit, number of keys, etc.)
 // make sure to update the JSON schema at:
 // https://github.com/mozilla-services/mozilla-pipeline-schemas/blob/master/telemetry/main.schema.json
 // You should also adjust browser_TelemetryGC.js.
-const MAX_GC_KEYS = 30;
+const MAX_GC_KEYS = 24;
 const MAX_SLICES = 4;
-const MAX_SLICE_KEYS = 15;
+const MAX_SLICE_KEYS = 12;
 const MAX_PHASES = 65;
 
 function limitProperties(obj, count) {
   // If there are too many properties, just delete them all. We don't
   // expect this ever to happen.
   if (Object.keys(obj).length > count) {
     for (let key of Object.keys(obj)) {
       delete obj[key];
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -476,50 +476,55 @@ Structure:
           "timestamp": 294872.2,
           // All durations are in milliseconds.
           "max_pause": 73.629,
           "total_time": 364.951, // Sum of all slice times.
           "zones_collected": 9,
           "total_zones": 9,
           "total_compartments": 309,
           "minor_gcs": 44,
+          // Present if non-zero.
           "store_buffer_overflows": 19,
           "mmu_20ms": 0,
           "mmu_50ms": 0,
           // Reasons include "None", "NonIncrementalRequested",
           // "AbortRequested", "KeepAtomsSet", "IncrementalDisabled",
           // "ModeChange", "MallocBytesTrigger", "GCBytesTrigger",
           // "ZoneChange", "CompartmentRevived".
-          "nonincremental_reason": "None",
-          "allocated": 37, // In megabytes.
+          // Present for non-incremental GCs only.
+          "nonincremental_reason": "GCBytesTrigger",
           "allocated_bytes": 38853696 // in bytes
+
+          // Present if non-zero.
           "added_chunks": 54,
           "removed_chunks": 12,
+
           // Total number of slices (some of which may not appear
           // in the "slices" array).
           "slices": 15,
           // We record at most 4 slices.
           "slice_number": 218, // The first slice number for this GC event.
           "slices_list": [
             {
               "slice": 218,  // The global index of this slice.
-              "pause": 23.221,  // How long the slice took.
-              "when": 0,  // Milliseconds since the start of the GC.
+              "pause": 23.221,  // How long the slice took (milliseconds).
               "reason": "SET_NEW_DOCUMENT",
               // GC state when the slice started
               "initial_state": "NotActive",
               // GC state when the slice ended
               "final_state": "Mark",
               // Budget is either "Xms", "work(Y)", or
               // "unlimited".
               "budget": "10ms",
               // Number of page faults during the slice.
-              "page_faults": 0,
+              // optional field, missing means 0.
+              "page_faults": 1,
+              // The start time of this slice in seconds.  The end time is
+              // given by the start_timestamp + pause.
               "start_timestamp": 294875,
-              "end_timestamp": 294879,
               // Time taken by each phase. There are at most 65 possible
               // phases, but usually only a few phases run in a given slice.
               "times": {
                 "wait_background_thread": 0.012,
                 "mark_discard_code": 2.845,
                 "purge": 0.723,
                 "mark": 9.831,
                 "mark_roots": 0.102,
--- a/toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
+++ b/toolkit/components/telemetry/tests/browser/browser_TelemetryGC.js
@@ -36,17 +36,17 @@ function check(entries) {
 
     ok(entries[f].length <= 2, "not too many GCs");
 
     for (let gc of entries[f]) {
       isnot(gc, null, "GC is non-null");
 
       foundGCs++;
 
-      ok(Object.keys(gc).length <= 30, "number of keys in GC is not too large");
+      ok(Object.keys(gc).length <= 24, "number of keys in GC is not too large");
 
       // Sanity check the GC data.
       ok("status" in gc, "status field present");
       is(gc.status, "completed", "status field correct");
       ok("total_time" in gc, "total_time field present");
       ok("max_pause" in gc, "max_pause field present");
 
       ok("slices_list" in gc, "slices_list field present");
@@ -63,17 +63,17 @@ function check(entries) {
         if (key != "slices_list" && key != "totals") {
           isnot(typeof(gc[key]), "object", `${key} property should be primitive`);
         }
       }
 
       let phases = new Set();
 
       for (let slice of gc.slices_list) {
-        ok(Object.keys(slice).length <= 15, "slice is not too large");
+        ok(Object.keys(slice).length <= 12, "slice is not too large");
 
         ok("pause" in slice, "pause field present in slice");
         ok("reason" in slice, "reason field present in slice");
         ok("times" in slice, "times field present in slice");
 
         // Make sure we don't skip any big objects.
         for (let key in slice) {
           if (key != "times") {
--- a/toolkit/content/tests/chrome/chrome.ini
+++ b/toolkit/content/tests/chrome/chrome.ini
@@ -66,16 +66,17 @@ subsuite = clipboard
 [test_autocomplete_placehold_last_complete.xul]
 [test_browser_drop.xul]
 [test_bug253481.xul]
 subsuite = clipboard
 [test_bug263683.xul]
 skip-if = debug && (os == 'win' || os == 'linux')
 [test_bug304188.xul]
 [test_bug331215.xul]
+skip-if = os == 'win' && debug # Bug 1339326
 [test_bug360220.xul]
 [test_bug360437.xul]
 skip-if = os == 'linux' # Bug 1264604
 [test_bug365773.xul]
 [test_bug366992.xul]
 [test_bug382990.xul]
 [test_bug409624.xul]
 [test_bug418874.xul]
--- a/toolkit/locales/Makefile.in
+++ b/toolkit/locales/Makefile.in
@@ -18,19 +18,8 @@ chrome-%: AB_CD=$*
 chrome-%:
 	@$(MAKE) -C $(DEPTH)/netwerk/locales/ chrome AB_CD=$*
 	@$(MAKE) -C $(DEPTH)/dom/locales/ chrome AB_CD=$*
 	@$(MAKE) -C $(DEPTH)/security/manager/locales/ chrome AB_CD=$*
 	@$(MAKE) chrome AB_CD=$*
 
 libs:: update.locale
 	sed -e 's/%AB_CD%/$(AB_CD)/' $< > $(FINAL_TARGET)/update.locale
-
-ifndef IS_LANGPACK
-ifdef MOZ_CRASHREPORTER
-libs:: $(call MERGE_FILE,crashreporter/crashreporter.ini)
-ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-	$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)/crashreporter.app/Contents/Resources
-else
-	$(SYSINSTALL) $(IFLAGS1) $^ $(FINAL_TARGET)
-endif
-endif
-endif
--- a/toolkit/locales/moz.build
+++ b/toolkit/locales/moz.build
@@ -18,8 +18,19 @@ JAR_MANIFESTS += ['jar.mn']
 GENERATED_FILES = [
     'multilocale.json',
 ]
 multilocale = GENERATED_FILES['multilocale.json']
 multilocale.script = 'gen_multilocale.py'
 FINAL_TARGET_FILES.res += [
     '!multilocale.json',
 ]
+
+if CONFIG['MOZ_CRASHREPORTER']:
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+        # TODO: fixing bug 1223748 should let us remove this special case
+        LOCALIZED_FILES['crashreporter.app'].Contents.Resources += [
+            'en-US/crashreporter/crashreporter.ini'
+        ]
+    else:
+        LOCALIZED_FILES += [
+            'en-US/crashreporter/crashreporter.ini'
+        ]