merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 10 Sep 2017 10:07:00 +0200
changeset 429438 00baeed193d4f276694912f0298336880280837d
parent 429403 f8f481901e2d86dd5be8b8a9af1f81f67e1e9158 (current diff)
parent 429437 899ca988e062d99d7913d282c9574d6c6e3fbf98 (diff)
child 429439 bbc4b851bea1ae76072c9c0389cb3d6cc3644b18
child 429445 73ad672a411f900715c6a1fb49c8ffe133f627a2
child 429449 7677154bc3e03ab6e2fa5b1901ada5280509b07d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
00baeed193d4 / 57.0a1 / 20170910100150 / files
nightly linux64
00baeed193d4 / 57.0a1 / 20170910100150 / files
nightly mac
00baeed193d4 / 57.0a1 / 20170910100150 / files
nightly win32
00baeed193d4 / 57.0a1 / 20170910100150 / files
nightly win64
00baeed193d4 / 57.0a1 / 20170910100150 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: LcNJ6RoBeCm
js/src/vm/PropDesc.h
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -144,85 +144,77 @@ CallbackObject::CallSetup::CallSetup(Cal
       NS_LITERAL_CSTRING("Cannot execute callback from a nuked compartment."));
     return;
   }
 
   // First, find the real underlying callback.
   JSObject* realCallback = js::UncheckedUnwrap(wrappedCallback);
   nsIGlobalObject* globalObject = nullptr;
 
-  JSContext* cx;
-  {
-    // Bug 955660: we cannot do "proper" rooting here because we need the
-    // global to get a context. Everything here is simple getters that cannot
-    // GC, so just paper over the necessary dataflow inversion.
-    JS::AutoSuppressGCAnalysis nogc;
-
-    // Now get the global for this callback. Note that for the case of
-    // JS-implemented WebIDL we never have a window here.
-    nsGlobalWindow* win = mIsMainThread && !aIsJSImplementedWebIDL
-                        ? xpc::WindowGlobalOrNull(realCallback)
-                        : nullptr;
-    if (win) {
-      MOZ_ASSERT(win->IsInnerWindow());
-      // We don't want to run script in windows that have been navigated away
-      // from.
-      if (!win->AsInner()->HasActiveDocument()) {
-        aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
-          NS_LITERAL_CSTRING("Refusing to execute function from window "
-                             "whose document is no longer active."));
-        return;
-      }
-      globalObject = win;
-    } else {
-      // No DOM Window. Store the global.
-      JSObject* global = js::GetGlobalForObjectCrossCompartment(realCallback);
-      globalObject = xpc::NativeGlobal(global);
-      MOZ_ASSERT(globalObject);
-    }
-
-    // Bail out if there's no useful global. This seems to happen intermittently
-    // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
-    // null in some kind of teardown state.
-    if (!globalObject->GetGlobalJSObject()) {
+  // Now get the global for this callback. Note that for the case of
+  // JS-implemented WebIDL we never have a window here.
+  nsGlobalWindow* win = mIsMainThread && !aIsJSImplementedWebIDL
+                          ? xpc::WindowGlobalOrNull(realCallback)
+                          : nullptr;
+  if (win) {
+    MOZ_ASSERT(win->IsInnerWindow());
+    // We don't want to run script in windows that have been navigated away
+    // from.
+    if (!win->AsInner()->HasActiveDocument()) {
       aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
-        NS_LITERAL_CSTRING("Refusing to execute function from global which is "
-                           "being torn down."));
+        NS_LITERAL_CSTRING("Refusing to execute function from window "
+                           "whose document is no longer active."));
       return;
     }
+    globalObject = win;
+  } else {
+    // No DOM Window. Store the global.
+    JSObject* global = js::GetGlobalForObjectCrossCompartment(realCallback);
+    globalObject = xpc::NativeGlobal(global);
+    MOZ_ASSERT(globalObject);
+  }
 
-    mAutoEntryScript.emplace(globalObject, aExecutionReason, mIsMainThread);
-    mAutoEntryScript->SetWebIDLCallerPrincipal(webIDLCallerPrincipal);
-    nsIGlobalObject* incumbent = aCallback->IncumbentGlobalOrNull();
-    if (incumbent) {
-      // The callback object traces its incumbent JS global, so in general it
-      // should be alive here. However, it's possible that we could run afoul
-      // of the same IPC global weirdness described above, wherein the
-      // nsIGlobalObject has severed its reference to the JS global. Let's just
-      // be safe here, so that nobody has to waste a day debugging gaia-ui tests.
-      if (!incumbent->GetGlobalJSObject()) {
+  // Bail out if there's no useful global. This seems to happen intermittently
+  // on gaia-ui tests, probably because nsInProcessTabChildGlobal is returning
+  // null in some kind of teardown state.
+  if (!globalObject->GetGlobalJSObject()) {
+    aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+      NS_LITERAL_CSTRING("Refusing to execute function from global which is "
+                         "being torn down."));
+    return;
+  }
+
+  mAutoEntryScript.emplace(globalObject, aExecutionReason, mIsMainThread);
+  mAutoEntryScript->SetWebIDLCallerPrincipal(webIDLCallerPrincipal);
+  nsIGlobalObject* incumbent = aCallback->IncumbentGlobalOrNull();
+  if (incumbent) {
+    // The callback object traces its incumbent JS global, so in general it
+    // should be alive here. However, it's possible that we could run afoul
+    // of the same IPC global weirdness described above, wherein the
+    // nsIGlobalObject has severed its reference to the JS global. Let's just
+    // be safe here, so that nobody has to waste a day debugging gaia-ui tests.
+    if (!incumbent->GetGlobalJSObject()) {
       aRv.ThrowDOMException(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
         NS_LITERAL_CSTRING("Refusing to execute function because our "
                            "incumbent global is being torn down."));
-        return;
-      }
-      mAutoIncumbentScript.emplace(incumbent);
+      return;
     }
+    mAutoIncumbentScript.emplace(incumbent);
+  }
 
-    cx = mAutoEntryScript->cx();
+  JSContext* cx = mAutoEntryScript->cx();
 
-    // Unmark the callable (by invoking CallbackOrNull() and not the
-    // CallbackPreserveColor() variant), and stick it in a Rooted before it can
-    // go gray again.
-    // Nothing before us in this function can trigger a CC, so it's safe to wait
-    // until here it do the unmark. This allows us to construct mRootedCallable
-    // with the cx from mAutoEntryScript, avoiding the cost of finding another
-    // JSContext. (Rooted<> does not care about requests or compartments.)
-    mRootedCallable.emplace(cx, aCallback->CallbackOrNull());
-  }
+  // Unmark the callable (by invoking CallbackOrNull() and not the
+  // CallbackPreserveColor() variant), and stick it in a Rooted before it can
+  // go gray again.
+  // Nothing before us in this function can trigger a CC, so it's safe to wait
+  // until here it do the unmark. This allows us to construct mRootedCallable
+  // with the cx from mAutoEntryScript, avoiding the cost of finding another
+  // JSContext. (Rooted<> does not care about requests or compartments.)
+  mRootedCallable.emplace(cx, aCallback->CallbackOrNull());
 
   // JS-implemented WebIDL is always OK to run, since it runs with Chrome
   // privileges anyway.
   if (mIsMainThread && !aIsJSImplementedWebIDL) {
     // Check that it's ok to run this callback at all.
     // Make sure to use realCallback to get the global of the callback object,
     // not the wrapper.
     bool allowed = xpc::Scriptability::Get(realCallback).Allowed();
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -322,16 +322,20 @@ HTMLEditor::HideGrabber()
 }
 
 NS_IMETHODIMP
 HTMLEditor::ShowGrabberOnElement(nsIDOMElement* aElement)
 {
   nsCOMPtr<Element> element = do_QueryInterface(aElement);
   NS_ENSURE_ARG_POINTER(element);
 
+  if (NS_WARN_IF(!IsDescendantOfEditorRoot(element))) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   if (mGrabber) {
     NS_ERROR("call HideGrabber first");
     return NS_ERROR_UNEXPECTED;
   }
 
   nsAutoString classValue;
   nsresult rv = CheckPositionedElementBGandFG(aElement, classValue);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -299,16 +299,20 @@ HTMLEditor::ShowResizersInner(Element& a
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIContent> parentContent = aResizedElement.GetParent();
   if (NS_WARN_IF(!parentContent)) {
    return NS_ERROR_FAILURE;
   }
 
+  if (NS_WARN_IF(!IsDescendantOfEditorRoot(&aResizedElement))) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   mResizedObject = &aResizedElement;
 
   // The resizers and the shadow will be anonymous siblings of the element.
   mTopLeftHandle =
     CreateResizer(nsIHTMLObjectResizer::eTopLeft, *parentContent);
   NS_ENSURE_TRUE(mTopLeftHandle, NS_ERROR_FAILURE);
   mTopHandle = CreateResizer(nsIHTMLObjectResizer::eTop, *parentContent);
   NS_ENSURE_TRUE(mTopHandle, NS_ERROR_FAILURE);
--- a/editor/libeditor/HTMLInlineTableEditor.cpp
+++ b/editor/libeditor/HTMLInlineTableEditor.cpp
@@ -43,20 +43,25 @@ HTMLEditor::GetInlineTableEditingEnabled
 }
 
 NS_IMETHODIMP
 HTMLEditor::ShowInlineTableEditingUI(nsIDOMElement* aCell)
 {
   NS_ENSURE_ARG_POINTER(aCell);
 
   // do nothing if aCell is not a table cell...
-  if (!HTMLEditUtils::IsTableCell(aCell)) {
+  nsCOMPtr<Element> cell = do_QueryInterface(aCell);
+  if (!cell || !HTMLEditUtils::IsTableCell(cell)) {
     return NS_OK;
   }
 
+  if (NS_WARN_IF(!IsDescendantOfEditorRoot(cell))) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
   if (mInlineEditedCell) {
     NS_ERROR("call HideInlineTableEditingUI first");
     return NS_ERROR_UNEXPECTED;
   }
 
   // the resizers and the shadow will be anonymous children of the body
   RefPtr<Element> bodyElement = GetRoot();
   NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER);
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -566,27 +566,30 @@ WebRenderLayerManager::GenerateFallbackD
       }
     }
   }
 
   gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                     gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
   if (!geometry || !invalidRegion.IsEmpty() || fallbackData->IsInvalid()) {
     if (gfxPrefs::WebRenderBlobImages()) {
+      bool snapped;
+      bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds);
+
       RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>();
-      // TODO: should use 'format' to replace gfx::SurfaceFormat::B8G8R8X8. Currently blob image doesn't support A8 format.
+      // TODO: should use 'format' to replace gfx::SurfaceFormat::B8G8R8A8. Currently blob image doesn't support A8 format.
       RefPtr<gfx::DrawTarget> dummyDt =
-        gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8X8);
+        gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8);
       RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageSize.ToUnknownSize());
       PaintItemByDrawTarget(aItem, dt, aImageRect, aOffset, aDisplayListBuilder);
       recorder->Finish();
 
       Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);
       wr::ImageKey key = WrBridge()->GetNextImageKey();
-      wr::ImageDescriptor descriptor(imageSize.ToUnknownSize(), 0, dt->GetFormat());
+      wr::ImageDescriptor descriptor(imageSize.ToUnknownSize(), 0, dt->GetFormat(), isOpaque);
       aBuilder.Resources().AddBlobImage(key, descriptor, bytes);
       fallbackData->SetKey(key);
     } else {
       fallbackData->CreateImageClientIfNeeded();
       RefPtr<ImageClient> imageClient = fallbackData->GetImageClient();
       RefPtr<ImageContainer> imageContainer = LayerManager::CreateImageContainer();
 
       {
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -107,16 +107,26 @@ struct ImageDescriptor: public wr::WrIma
   ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat)
   {
     format = wr::SurfaceFormatToImageFormat(aFormat).value();
     width = aSize.width;
     height = aSize.height;
     stride = aByteStride;
     is_opaque = gfx::IsOpaqueFormat(aFormat);
   }
+
+  ImageDescriptor(const gfx::IntSize& aSize, uint32_t aByteStride, gfx::SurfaceFormat aFormat, bool opaque)
+  {
+    format = wr::SurfaceFormatToImageFormat(aFormat).value();
+    width = aSize.width;
+    height = aSize.height;
+    stride = aByteStride;
+    is_opaque = opaque;
+  }
+
 };
 
 // Whenever possible, use wr::WindowId instead of manipulating uint64_t.
 inline uint64_t AsUint64(const WindowId& aId) {
   return static_cast<uint64_t>(aId.mHandle);
 }
 
 // Whenever possible, use wr::ImageKey instead of manipulating uint64_t.
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -2336,17 +2336,28 @@ PromiseObject::unforgeableResolve(JSCont
 {
     RootedObject promiseCtor(cx, JS::GetPromiseConstructor(cx));
     if (!promiseCtor)
         return nullptr;
     RootedValue cVal(cx, ObjectValue(*promiseCtor));
     return CommonStaticResolveRejectImpl(cx, cVal, value, ResolveMode);
 }
 
-// ES2016, 25.4.4.6, implemented in Promise.js.
+/**
+ * ES2016, 25.4.4.6 get Promise [ @@species ]
+ */
+static bool
+Promise_static_species(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Step 1: Return the this value.
+    args.rval().set(args.thisv());
+    return true;
+}
 
 // ES2016, 25.4.5.1, implemented in Promise.js.
 
 static PromiseReactionRecord*
 NewReactionRecord(JSContext* cx, HandleObject resultPromise, HandleValue onFulfilled,
                   HandleValue onRejected, HandleObject resolve, HandleObject reject,
                   HandleObject incumbentGlobalObject)
 {
@@ -2368,16 +2379,22 @@ NewReactionRecord(JSContext* cx, HandleO
     reaction->setFixedSlot(ReactionRecordSlot_Resolve, ObjectOrNullValue(resolve));
     reaction->setFixedSlot(ReactionRecordSlot_Reject, ObjectOrNullValue(reject));
     reaction->setFixedSlot(ReactionRecordSlot_IncumbentGlobalObject,
                            ObjectOrNullValue(incumbentGlobalObject));
 
     return reaction;
 }
 
+static bool
+IsPromiseSpecies(JSContext* cx, JSFunction* species)
+{
+    return species->maybeNative() == Promise_static_species;
+}
+
 // ES2016, 25.4.5.3., steps 3-5.
 MOZ_MUST_USE bool
 js::OriginalPromiseThen(JSContext* cx, Handle<PromiseObject*> promise,
                         HandleValue onFulfilled, HandleValue onRejected,
                         MutableHandleObject dependent, bool createDependent)
 {
     RootedObject promiseObj(cx, promise);
     if (promise->compartment() != cx->compartment()) {
@@ -2386,20 +2403,19 @@ js::OriginalPromiseThen(JSContext* cx, H
     }
 
     RootedObject resultPromise(cx);
     RootedObject resolve(cx);
     RootedObject reject(cx);
 
     if (createDependent) {
         // Step 3.
-        RootedValue ctorVal(cx);
-        if (!SpeciesConstructor(cx, promiseObj, JSProto_Promise, &ctorVal))
+        RootedObject C(cx, SpeciesConstructor(cx, promiseObj, JSProto_Promise, IsPromiseSpecies));
+        if (!C)
             return false;
-        RootedObject C(cx, &ctorVal.toObject());
 
         // Step 4.
         if (!NewPromiseCapability(cx, C, &resultPromise, &resolve, &reject, true))
             return false;
     }
 
     // Step 5.
     if (!PerformPromiseThen(cx, promise, onFulfilled, onRejected, resultPromise, resolve, reject))
@@ -3034,21 +3050,20 @@ BlockOnPromise(JSContext* cx, HandleValu
 
     if (promiseObj && promiseObj->is<PromiseObject>() && IsNativeFunction(thenVal, Promise_then)) {
         // |promise| is an unwrapped Promise, and |then| is the original
         // |Promise.prototype.then|, inline it here.
         // 25.4.5.3., step 3.
         RootedObject PromiseCtor(cx);
         if (!GetBuiltinConstructor(cx, JSProto_Promise, &PromiseCtor))
             return false;
-        RootedValue PromiseCtorVal(cx, ObjectValue(*PromiseCtor));
-        RootedValue CVal(cx);
-        if (!SpeciesConstructor(cx, promiseObj, PromiseCtorVal, &CVal))
+
+        RootedObject C(cx, SpeciesConstructor(cx, PromiseCtor, JSProto_Promise, IsPromiseSpecies));
+        if (!C)
             return false;
-        RootedObject C(cx, &CVal.toObject());
 
         RootedObject resultPromise(cx, blockedPromise_);
         RootedObject resolveFun(cx);
         RootedObject rejectFun(cx);
 
         // By default, the blocked promise is added as an extra entry to the
         // rejected promises list.
         bool addToDependent = true;
@@ -3612,17 +3627,17 @@ static const JSFunctionSpec promise_stat
     JS_FN("all", Promise_static_all, 1, 0),
     JS_FN("race", Promise_static_race, 1, 0),
     JS_FN("reject", Promise_reject, 1, 0),
     JS_FN("resolve", Promise_static_resolve, 1, 0),
     JS_FS_END
 };
 
 static const JSPropertySpec promise_static_properties[] = {
-    JS_SELF_HOSTED_SYM_GET(species, "Promise_static_get_species", 0),
+    JS_SYM_GET(species, Promise_static_species, 0),
     JS_PS_END
 };
 
 static const ClassSpec PromiseObjectClassSpec = {
     GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
     CreatePromisePrototype,
     promise_static_methods,
     promise_static_properties,
--- a/js/src/builtin/Promise.js
+++ b/js/src/builtin/Promise.js
@@ -1,16 +1,9 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-// ES6, 25.4.4.6.
-function Promise_static_get_species() {
-    // Step 1.
-    return this;
-}
-_SetCanonicalName(Promise_static_get_species, "get [Symbol.species]");
-
 // ES6, 25.4.5.1.
 function Promise_catch(onRejected) {
     // Steps 1-2.
     return callContentFunction(this.then, this, undefined, onRejected);
 }
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -87,21 +87,16 @@ CompileStandaloneAsyncFunction(JSContext
                                const mozilla::Maybe<uint32_t>& parameterListEnd);
 
 MOZ_MUST_USE bool
 CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
                                 const ReadOnlyCompileOptions& options,
                                 JS::SourceBufferHolder& srcBuf,
                                 const mozilla::Maybe<uint32_t>& parameterListEnd);
 
-MOZ_MUST_USE bool
-CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
-                         const ReadOnlyCompileOptions& options,
-                         Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
-
 ScriptSourceObject*
 CreateScriptSourceObject(JSContext* cx, const ReadOnlyCompileOptions& options,
                          const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing());
 
 /*
  * True if str consists of an IdentifierStart character, followed by one or
  * more IdentifierPart characters, i.e. it matches the IdentifierName production
  * in the language spec.
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3661,32 +3661,16 @@ BytecodeEmitter::reportExtraWarning(Pars
     bool result = parser.tokenStream()
                         .reportExtraWarningErrorNumberVA(nullptr, pos.begin, errorNumber, args);
 
     va_end(args);
     return result;
 }
 
 bool
-BytecodeEmitter::reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...)
-{
-    TokenPos pos = pn ? pn->pn_pos : tokenStream().currentToken().pos;
-
-    va_list args;
-    va_start(args, errorNumber);
-    // FIXME: parser.tokenStream() should be a TokenStreamAnyChars for bug 1351107,
-    // but caused problems, cf. bug 1363116.
-    bool result = parser.tokenStream()
-                        .reportStrictModeErrorNumberVA(nullptr, pos.begin, sc->strict(),
-                                                       errorNumber, args);
-    va_end(args);
-    return result;
-}
-
-bool
 BytecodeEmitter::emitNewInit(JSProtoKey key)
 {
     const size_t len = 1 + UINT32_INDEX_LEN;
     ptrdiff_t offset;
     if (!emitCheck(len, &offset))
         return false;
 
     jsbytecode* code = this->code(offset);
@@ -3705,18 +3689,18 @@ BytecodeEmitter::iteratorResultShape(uns
 {
     // No need to do any guessing for the object kind, since we know exactly how
     // many properties we plan to have.
     gc::AllocKind kind = gc::GetGCObjectKind(2);
     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
     if (!obj)
         return false;
 
-    Rooted<jsid> value_id(cx, AtomToId(cx->names().value));
-    Rooted<jsid> done_id(cx, AtomToId(cx->names().done));
+    Rooted<jsid> value_id(cx, NameToId(cx->names().value));
+    Rooted<jsid> done_id(cx, NameToId(cx->names().done));
     if (!NativeDefineProperty(cx, obj, value_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
     {
         return false;
     }
     if (!NativeDefineProperty(cx, obj, done_id, UndefinedHandleValue, nullptr, nullptr,
                               JSPROP_ENUMERATE))
     {
@@ -3756,28 +3740,16 @@ BytecodeEmitter::emitFinishIteratorResul
     if (!emit1(done ? JSOP_TRUE : JSOP_FALSE))
         return false;
     if (!emitIndex32(JSOP_INITPROP, done_id))
         return false;
     return true;
 }
 
 bool
-BytecodeEmitter::emitToIteratorResult(bool done)
-{
-    if (!emitPrepareIteratorResult())    // VALUE OBJ
-        return false;
-    if (!emit1(JSOP_SWAP))               // OBJ VALUE
-        return false;
-    if (!emitFinishIteratorResult(done)) // RESULT
-        return false;
-    return true;
-}
-
-bool
 BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext)
 {
     switch (loc.kind()) {
       case NameLocation::Kind::Dynamic:
         if (!emitAtomOp(name, JSOP_GETNAME))
             return false;
         break;
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -412,17 +412,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     void setFunctionBodyEndPos(TokenPos pos) {
         functionBodyEndPos = pos.end;
         functionBodyEndPosSet = true;
     }
 
     void reportError(ParseNode* pn, unsigned errorNumber, ...);
     bool reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...);
-    bool reportStrictModeError(ParseNode* pn, unsigned errorNumber, ...);
 
     // If pn contains a useful expression, return true with *answer set to true.
     // If pn contains a useless expression, return true with *answer set to
     // false. Return false on error.
     //
     // The caller should initialize *answer to false and invoke this function on
     // an expression statement or similar subtree to decide whether the tree
     // could produce code that has any side effects.  For an expression
@@ -627,17 +626,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter
                                             ParseNode* initializer);
 
     MOZ_MUST_USE bool emitNewInit(JSProtoKey key);
     MOZ_MUST_USE bool emitSingletonInitialiser(ParseNode* pn);
 
     MOZ_MUST_USE bool emitPrepareIteratorResult();
     MOZ_MUST_USE bool emitFinishIteratorResult(bool done);
     MOZ_MUST_USE bool iteratorResultShape(unsigned* shape);
-    MOZ_MUST_USE bool emitToIteratorResult(bool done);
 
     MOZ_MUST_USE bool emitGetDotGenerator();
 
     MOZ_MUST_USE bool emitInitialYield(ParseNode* pn);
     MOZ_MUST_USE bool emitYield(ParseNode* pn);
     MOZ_MUST_USE bool emitYieldOp(JSOp op);
     MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
     MOZ_MUST_USE bool emitAwait();
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -9474,92 +9474,129 @@ Parser<SyntaxParseHandler, char16_t>::ne
 
     mozilla::Range<const char16_t> source(chars, length);
     if (!js::irregexp::ParsePatternSyntax(tokenStream, alloc, source, flags & UnicodeFlag))
         return null();
 
     return handler.newRegExp(SyntaxParseHandler::NodeGeneric, pos(), *this);
 }
 
+// |exprPossibleError| is the PossibleError state within |expr|,
+// |possibleError| is the surrounding PossibleError state.
+template <class ParseHandler, typename CharT>
+bool
+Parser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                                                PossibleError* exprPossibleError,
+                                                                PossibleError* possibleError,
+                                                                TargetBehavior behavior)
+{
+    // Report any pending expression error if we're definitely not in a
+    // destructuring context or the possible destructuring target is a
+    // property accessor.
+    if (!possibleError || handler.isPropertyAccess(expr))
+        return exprPossibleError->checkForExpressionError();
+
+    // |expr| may end up as a destructuring assignment target, so we need to
+    // validate it's either a name or can be parsed as a nested destructuring
+    // pattern. Property accessors are also valid assignment targets, but
+    // those are already handled above.
+
+    exprPossibleError->transferErrorsTo(possibleError);
+
+    // Return early if a pending destructuring error is already present.
+    if (possibleError->hasPendingDestructuringError())
+        return true;
+
+    if (handler.isNameAnyParentheses(expr)) {
+        checkDestructuringAssignmentName(expr, exprPos, possibleError);
+        return true;
+    }
+
+    if (handler.isUnparenthesizedDestructuringPattern(expr)) {
+        if (behavior == TargetBehavior::ForbidAssignmentPattern)
+            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+        return true;
+    }
+
+    // Parentheses are forbidden around destructuring *patterns* (but allowed
+    // around names). Use our nicer error message for parenthesized, nested
+    // patterns if nested destructuring patterns are allowed.
+    if (handler.isParenthesizedDestructuringPattern(expr) &&
+        behavior != TargetBehavior::ForbidAssignmentPattern)
+    {
+        possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS);
+    } else {
+        possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
+    }
+
+    return true;
+}
+
 template <class ParseHandler, typename CharT>
 void
-Parser<ParseHandler, CharT>::checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
-                                                                PossibleError* possibleError,
-                                                                TargetBehavior behavior)
-{
+Parser<ParseHandler, CharT>::checkDestructuringAssignmentName(Node name, TokenPos namePos,
+                                                              PossibleError* possibleError)
+{
+    MOZ_ASSERT(handler.isNameAnyParentheses(name));
+
     // Return early if a pending destructuring error is already present.
     if (possibleError->hasPendingDestructuringError())
         return;
 
     if (pc->sc()->needStrictChecks()) {
-        if (handler.isArgumentsAnyParentheses(expr, context)) {
+        if (handler.isArgumentsAnyParentheses(name, context)) {
             if (pc->sc()->strict()) {
-                possibleError->setPendingDestructuringErrorAt(exprPos,
+                possibleError->setPendingDestructuringErrorAt(namePos,
                                                               JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
             } else {
-                possibleError->setPendingDestructuringWarningAt(exprPos,
+                possibleError->setPendingDestructuringWarningAt(namePos,
                                                                 JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS);
             }
             return;
         }
 
-        if (handler.isEvalAnyParentheses(expr, context)) {
+        if (handler.isEvalAnyParentheses(name, context)) {
             if (pc->sc()->strict()) {
-                possibleError->setPendingDestructuringErrorAt(exprPos,
+                possibleError->setPendingDestructuringErrorAt(namePos,
                                                               JSMSG_BAD_STRICT_ASSIGN_EVAL);
             } else {
-                possibleError->setPendingDestructuringWarningAt(exprPos,
+                possibleError->setPendingDestructuringWarningAt(namePos,
                                                                 JSMSG_BAD_STRICT_ASSIGN_EVAL);
             }
             return;
         }
     }
-
-    if (behavior == TargetBehavior::ForbidAssignmentPattern) {
-        if (handler.isUnparenthesizedDestructuringPattern(expr) ||
-            handler.isParenthesizedDestructuringPattern(expr))
-        {
-            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
-            return;
-        }
-    }
-
-    // The expression must be either a simple assignment target, i.e. a name
-    // or a property accessor, or a nested destructuring pattern.
-    if (!handler.isUnparenthesizedDestructuringPattern(expr) &&
-        !handler.isNameAnyParentheses(expr) &&
-        !handler.isPropertyAccess(expr))
-    {
-        // Parentheses are forbidden around destructuring *patterns* (but
-        // allowed around names). Use our nicer error message for
-        // parenthesized, nested patterns.
-        if (handler.isParenthesizedDestructuringPattern(expr))
-            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS);
-        else
-            possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET);
-    }
-}
-
-template <class ParseHandler, typename CharT>
-void
+}
+
+template <class ParseHandler, typename CharT>
+bool
 Parser<ParseHandler, CharT>::checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                                                 PossibleError* exprPossibleError,
                                                                  PossibleError* possibleError)
 {
     // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834
     // 12.15.5 Destructuring Assignment
     //
     // AssignmentElement[Yield, Await]:
     //   DestructuringAssignmentTarget[?Yield, ?Await]
     //   DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]
 
     // If |expr| is an assignment element with an initializer expression, its
     // destructuring assignment target was already validated in assignExpr().
     // Otherwise we need to check that |expr| is a valid destructuring target.
-    if (!handler.isUnparenthesizedAssignment(expr))
-        checkDestructuringAssignmentTarget(expr, exprPos, possibleError);
+    if (handler.isUnparenthesizedAssignment(expr)) {
+        // Report any pending expression error if we're definitely not in a
+        // destructuring context.
+        if (!possibleError)
+            return exprPossibleError->checkForExpressionError();
+
+        exprPossibleError->transferErrorsTo(possibleError);
+        return true;
+    }
+    return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::arrayInitializer(YieldHandling yieldHandling,
                                               PossibleError* possibleError)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
@@ -9607,35 +9644,44 @@ Parser<ParseHandler, CharT>::arrayInitia
             } else if (tt == TOK_TRIPLEDOT) {
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
 
                 TokenPos innerPos;
                 if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
                     return null();
 
+                PossibleError possibleErrorInner(*this);
                 Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
-                                        possibleError);
+                                        &possibleErrorInner);
                 if (!inner)
                     return null();
-                if (possibleError)
-                    checkDestructuringAssignmentTarget(inner, innerPos, possibleError);
+                if (!checkDestructuringAssignmentTarget(inner, innerPos, &possibleErrorInner,
+                                                        possibleError))
+                {
+                    return null();
+                }
+
                 if (!handler.addSpreadElement(literal, begin, inner))
                     return null();
             } else {
                 TokenPos elementPos;
                 if (!tokenStream.peekTokenPos(&elementPos, TokenStream::Operand))
                     return null();
 
+                PossibleError possibleErrorInner(*this);
                 Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
-                                          possibleError);
+                                          &possibleErrorInner);
                 if (!element)
                     return null();
-                if (possibleError)
-                    checkDestructuringAssignmentElement(element, elementPos, possibleError);
+                if (!checkDestructuringAssignmentElement(element, elementPos, &possibleErrorInner,
+                                                         possibleError))
+                {
+                    return null();
+                }
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
             if (tt != TOK_COMMA) {
                 /* If we didn't already match TOK_COMMA in above case. */
                 bool matched;
@@ -9908,56 +9954,63 @@ Parser<ParseHandler, CharT>::objectLiter
         if (tt == TOK_TRIPLEDOT) {
             tokenStream.consumeKnownToken(TOK_TRIPLEDOT);
             uint32_t begin = pos().begin;
 
             TokenPos innerPos;
             if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand))
                 return null();
 
+            PossibleError possibleErrorInner(*this);
             Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
-                                    possibleError);
+                                    &possibleErrorInner);
             if (!inner)
                 return null();
-            if (possibleError) {
-                checkDestructuringAssignmentTarget(inner, innerPos, possibleError,
-                                                   TargetBehavior::ForbidAssignmentPattern);
+            if (!checkDestructuringAssignmentTarget(inner, innerPos, &possibleErrorInner,
+                                                    possibleError,
+                                                    TargetBehavior::ForbidAssignmentPattern))
+            {
+                return null();
             }
             if (!handler.addSpreadProperty(literal, begin, inner))
                 return null();
         } else {
             TokenPos namePos = tokenStream.nextToken().pos;
 
             PropertyType propType;
             Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom);
             if (!propName)
                 return null();
 
             if (propType == PropertyType::Normal) {
                 TokenPos exprPos;
                 if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand))
                     return null();
 
+                PossibleError possibleErrorInner(*this);
                 Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited,
-                                           possibleError);
+                                           &possibleErrorInner);
                 if (!propExpr)
                     return null();
 
                 handler.checkAndSetIsDirectRHSAnonFunction(propExpr);
 
-                if (possibleError)
-                    checkDestructuringAssignmentElement(propExpr, exprPos, possibleError);
+                if (!checkDestructuringAssignmentElement(propExpr, exprPos, &possibleErrorInner,
+                                                         possibleError))
+                {
+                    return null();
+                }
 
                 if (foldConstants && !FoldConstants(context, &propExpr, this))
                     return null();
 
                 if (propAtom == context->names().proto) {
                     if (seenPrototypeMutation) {
-                        // Directly report the error when we're not in a
-                        // destructuring context.
+                        // Directly report the error when we're definitely not
+                        // in a destructuring context.
                         if (!possibleError) {
                             errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY);
                             return null();
                         }
 
                         // Otherwise delay error reporting until we've
                         // determined whether or not we're destructuring.
                         possibleError->setPendingExpressionErrorAt(namePos,
@@ -9988,17 +10041,17 @@ Parser<ParseHandler, CharT>::objectLiter
                 if (!name)
                     return null();
 
                 Node nameExpr = identifierReference(name);
                 if (!nameExpr)
                     return null();
 
                 if (possibleError)
-                    checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError);
+                    checkDestructuringAssignmentName(nameExpr, namePos, possibleError);
 
                 if (!handler.addShorthand(literal, propName, nameExpr))
                     return null();
             } else if (propType == PropertyType::CoverInitializedName) {
                 /*
                  * Support, e.g., |({x=1, y=2} = o)| as destructuring
                  * shorthand with default values, as per ES6 12.14.5
                  */
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -923,20 +923,24 @@ class Parser final : public ParserBase, 
                                     TokenKind tt);
     Node objectBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
     Node arrayBindingPattern(DeclarationKind kind, YieldHandling yieldHandling);
 
     enum class TargetBehavior {
         PermitAssignmentPattern,
         ForbidAssignmentPattern
     };
-    void checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+    bool checkDestructuringAssignmentTarget(Node expr, TokenPos exprPos,
+                                            PossibleError* exprPossibleError,
                                             PossibleError* possibleError,
                                             TargetBehavior behavior = TargetBehavior::PermitAssignmentPattern);
-    void checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+    void checkDestructuringAssignmentName(Node name, TokenPos namePos,
+                                          PossibleError* possibleError);
+    bool checkDestructuringAssignmentElement(Node expr, TokenPos exprPos,
+                                             PossibleError* exprPossibleError,
                                              PossibleError* possibleError);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
     static Node null() { return ParseHandler::null(); }
 
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1054,20 +1054,18 @@ IonBuilder::inlineBoolean(CallInfo& call
         pushConstant(BooleanValue(false));
     }
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineNewIterator(CallInfo& callInfo, MNewIterator::Type type)
 {
-    if (callInfo.argc() != 0 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 0);
 
     JSObject* templateObject = nullptr;
     switch (type) {
       case MNewIterator::ArrayIterator:
         templateObject = inspector->getTemplateObjectForNative(pc, js::intrinsic_NewArrayIterator);
         MOZ_ASSERT_IF(templateObject, templateObject->is<ArrayIteratorObject>());
         break;
       case MNewIterator::StringIterator:
@@ -1693,20 +1691,18 @@ IonBuilder::inlineConstantStringSplitStr
     MInstruction* setLength = setInitializedLength(array, unboxedType, initLength);
     MOZ_TRY(resumeAfter(setLength));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineStringSplitString(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* strArg = callInfo.getArg(0);
     MDefinition* sepArg = callInfo.getArg(1);
 
     if (strArg->type() != MIRType::String)
         return InliningStatus_NotInlined;
 
     if (sepArg->type() != MIRType::String)
@@ -1743,20 +1739,18 @@ IonBuilder::inlineStringSplitString(Call
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineObjectHasPrototype(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* objArg = callInfo.getArg(0);
     MDefinition* protoArg = callInfo.getArg(1);
 
     if (objArg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
     if (protoArg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
@@ -2065,34 +2059,32 @@ IonBuilder::inlineStringConvertCase(Call
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 3);
 
     MDefinition* rxArg = callInfo.getArg(0);
     MDefinition* strArg = callInfo.getArg(1);
     MDefinition* lastIndexArg = callInfo.getArg(2);
 
     if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
     const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (strArg->mightBeType(MIRType::Object))
+    if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
     JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
@@ -2111,34 +2103,32 @@ IonBuilder::inlineRegExpMatcher(CallInfo
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineRegExpSearcher(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 3);
 
     MDefinition* rxArg = callInfo.getArg(0);
     MDefinition* strArg = callInfo.getArg(1);
     MDefinition* lastIndexArg = callInfo.getArg(2);
 
     if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* regexpTypes = rxArg->resultTypeSet();
     const Class* clasp = regexpTypes ? regexpTypes->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (strArg->mightBeType(MIRType::Object))
+    if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
     JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpSearcherStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
@@ -2157,34 +2147,32 @@ IonBuilder::inlineRegExpSearcher(CallInf
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineRegExpTester(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 3);
 
     MDefinition* rxArg = callInfo.getArg(0);
     MDefinition* strArg = callInfo.getArg(1);
     MDefinition* lastIndexArg = callInfo.getArg(2);
 
     if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* rxTypes = rxArg->resultTypeSet();
     const Class* clasp = rxTypes ? rxTypes->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (strArg->mightBeType(MIRType::Object))
+    if (strArg->type() != MIRType::String && strArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
     JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
@@ -2199,54 +2187,59 @@ IonBuilder::inlineRegExpTester(CallInfo&
 
     MOZ_TRY(resumeAfter(tester));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
 {
-    if (callInfo.constructing() || callInfo.argc() != 1) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
     MDefinition* arg = callInfo.getArg(0);
 
-    bool isRegExpObject;
-    if (!arg->mightBeType(MIRType::Object)) {
-        isRegExpObject = false;
-    } else {
-        if (arg->type() != MIRType::Object)
-            return InliningStatus_NotInlined;
-
+    bool isRegExpObjectKnown = false;
+    bool isRegExpObjectConstant;
+    if (arg->type() == MIRType::Object) {
         TemporaryTypeSet* types = arg->resultTypeSet();
         const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
-        if (!clasp || clasp->isProxy())
-            return InliningStatus_NotInlined;
-
-        isRegExpObject = (clasp == &RegExpObject::class_);
+        if (clasp) {
+            isRegExpObjectKnown = true;
+            isRegExpObjectConstant = (clasp == &RegExpObject::class_);
+        }
+    } else if (!arg->mightBeType(MIRType::Object)) {
+        // NB: This case only happens when Phi-nodes flow into IsRegExpObject.
+        // IsRegExpObject itself will never be called with a non-Object value.
+        isRegExpObjectKnown = true;
+        isRegExpObjectConstant = false;
+    } else if (arg->type() != MIRType::Value) {
+        return InliningStatus_NotInlined;
     }
 
-    pushConstant(BooleanValue(isRegExpObject));
+    if (isRegExpObjectKnown) {
+        pushConstant(BooleanValue(isRegExpObjectConstant));
+    } else {
+        MHasClass* hasClass = MHasClass::New(alloc(), arg, &RegExpObject::class_);
+        current->add(hasClass);
+        current->push(hasClass);
+    }
 
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineRegExpPrototypeOptimizable(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     MDefinition* protoArg = callInfo.getArg(0);
 
     if (protoArg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
@@ -2258,25 +2251,23 @@ IonBuilder::inlineRegExpPrototypeOptimiz
     current->push(opt);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineRegExpInstanceOptimizable(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* rxArg = callInfo.getArg(0);
     MDefinition* protoArg = callInfo.getArg(1);
 
-    if (rxArg->type() != MIRType::Object)
+    if (rxArg->type() != MIRType::Object && rxArg->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     if (protoArg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
@@ -2287,20 +2278,18 @@ IonBuilder::inlineRegExpInstanceOptimiza
     current->push(opt);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineGetFirstDollarIndex(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     MDefinition* strArg = callInfo.getArg(0);
 
     if (strArg->type() != MIRType::String)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType::Int32)
         return InliningStatus_NotInlined;
@@ -2312,20 +2301,18 @@ IonBuilder::inlineGetFirstDollarIndex(Ca
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineStringReplaceString(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 3);
 
     if (getInlineReturnType() != MIRType::String)
         return InliningStatus_NotInlined;
 
     MDefinition* strArg = callInfo.getArg(0);
     MDefinition* patArg = callInfo.getArg(1);
     MDefinition* replArg = callInfo.getArg(2);
 
@@ -2346,18 +2333,18 @@ IonBuilder::inlineStringReplaceString(Ca
     if (cte->isEffectful())
         MOZ_TRY(resumeAfter(cte));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineSubstringKernel(CallInfo& callInfo)
 {
+    MOZ_ASSERT(!callInfo.constructing());
     MOZ_ASSERT(callInfo.argc() == 3);
-    MOZ_ASSERT(!callInfo.constructing());
 
     // Return: String.
     if (getInlineReturnType() != MIRType::String)
         return InliningStatus_NotInlined;
 
     // Arg 0: String.
     if (callInfo.getArg(0)->type() != MIRType::String)
         return InliningStatus_NotInlined;
@@ -2494,20 +2481,18 @@ IonBuilder::inlineObjectToString(CallInf
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineHasClass(CallInfo& callInfo,
                            const Class* clasp1, const Class* clasp2,
                            const Class* clasp3, const Class* clasp4)
 {
-    if (callInfo.constructing() || callInfo.argc() != 1) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (callInfo.getArg(0)->type() != MIRType::Object)
         return InliningStatus_NotInlined;
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet();
     const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr;
@@ -2541,20 +2526,18 @@ IonBuilder::inlineHasClass(CallInfo& cal
 
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineGetNextEntryForIterator(CallInfo& callInfo, MGetNextEntryForIterator::Mode mode)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* iterArg = callInfo.getArg(0);
     MDefinition* resultArg = callInfo.getArg(1);
 
     if (iterArg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* iterTypes = iterArg->resultTypeSet();
@@ -2870,20 +2853,18 @@ IonBuilder::inlineSetDisjointTypedElemen
     MOZ_TRY(resumeAfter(sets));
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineObjectIsTypeDescr(CallInfo& callInfo)
 {
-    if (callInfo.constructing() || callInfo.argc() != 1) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (callInfo.getArg(0)->type() != MIRType::Object)
         return InliningStatus_NotInlined;
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
     // The test is elaborate: in-line only if there is exact
     // information.
@@ -2909,20 +2890,18 @@ IonBuilder::inlineObjectIsTypeDescr(Call
 
     callInfo.setImplicitlyUsedUnchecked();
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineSetTypedObjectOffset(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* typedObj = callInfo.getArg(0);
     MDefinition* offset = callInfo.getArg(1);
 
     // Return type should be undefined or something wacky is going on.
     if (getInlineReturnType() != MIRType::Undefined)
         return InliningStatus_NotInlined;
 
@@ -2953,20 +2932,19 @@ IonBuilder::inlineSetTypedObjectOffset(C
     current->add(ins);
     current->push(ins);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineUnsafeSetReservedSlot(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 3 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 3);
+
     if (getInlineReturnType() != MIRType::Undefined)
         return InliningStatus_NotInlined;
 
     MDefinition* obj = callInfo.getArg(0);
     if (obj->type() != MIRType::Object && obj->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     MDefinition* arg = callInfo.getArg(1);
@@ -2993,20 +2971,18 @@ IonBuilder::inlineUnsafeSetReservedSlot(
         current->add(MPostWriteBarrier::New(alloc(), obj, callInfo.getArg(2)));
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueType)
 {
-    if (callInfo.argc() != 2 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 2);
 
     MDefinition* obj = callInfo.getArg(0);
     if (obj->type() != MIRType::Object && obj->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
     MDefinition* arg = callInfo.getArg(1);
     if (arg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
@@ -3040,20 +3016,18 @@ IonBuilder::inlineUnsafeGetReservedSlot(
     // We don't track reserved slot types, so always emit a barrier.
     MOZ_TRY(pushTypeBarrier(load, getInlineReturnTypeSet(), BarrierKind::TypeSet));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsCallable(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
     MDefinition* arg = callInfo.getArg(0);
 
     // Try inlining with constant true/false: only objects may be callable at
     // all, and if we know the class check if it is callable.
@@ -3108,20 +3082,19 @@ IonBuilder::inlineIsConstructor(CallInfo
     current->push(ins);
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsObject(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
+
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
     if (callInfo.getArg(0)->type() == MIRType::Object) {
         pushConstant(BooleanValue(true));
     } else {
         MIsObject* isObject = MIsObject::New(alloc(), callInfo.getArg(0));
@@ -3129,20 +3102,18 @@ IonBuilder::inlineIsObject(CallInfo& cal
         current->push(isObject);
     }
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineToObject(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (getInlineReturnType() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     MDefinition* object = callInfo.getArg(0);
     if (object->type() != MIRType::Object && object->type() != MIRType::Value)
         return InliningStatus_NotInlined;
 
@@ -3160,20 +3131,18 @@ IonBuilder::inlineToObject(CallInfo& cal
     }
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsWrappedArrayConstructor(CallInfo& callInfo)
 {
-    if (callInfo.constructing() || callInfo.argc() != 1) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (getInlineReturnType() != MIRType::Boolean)
         return InliningStatus_NotInlined;
     MDefinition* arg = callInfo.getArg(0);
     if (arg->type() != MIRType::Object)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* types = arg->resultTypeSet();
@@ -3191,20 +3160,18 @@ IonBuilder::inlineIsWrappedArrayConstruc
     // Inline only if argument is absolutely *not* a Proxy.
     pushConstant(BooleanValue(false));
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineToInteger(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
-        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
-        return InliningStatus_NotInlined;
-    }
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     MDefinition* input = callInfo.getArg(0);
 
     // Only optimize cases where input contains only number, null or boolean
     if (input->mightBeType(MIRType::Object) ||
         input->mightBeType(MIRType::String) ||
         input->mightBeType(MIRType::Symbol) ||
         input->mightBeType(MIRType::Undefined) ||
@@ -3226,18 +3193,18 @@ IonBuilder::inlineToInteger(CallInfo& ca
     current->add(toInt32);
     current->push(toInt32);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineToString(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing())
-        return InliningStatus_NotInlined;
+    MOZ_ASSERT(!callInfo.constructing());
+    MOZ_ASSERT(callInfo.argc() == 1);
 
     if (getInlineReturnType() != MIRType::String)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
     MToString* toString = MToString::New(alloc(), callInfo.getArg(0));
     current->add(toString);
     current->push(toString);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13639,17 +13639,18 @@ class MHasClass
       public SingleObjectPolicy::Data
 {
     const Class* class_;
 
     MHasClass(MDefinition* object, const Class* clasp)
       : MUnaryInstruction(classOpcode, object)
       , class_(clasp)
     {
-        MOZ_ASSERT(object->type() == MIRType::Object);
+        MOZ_ASSERT(object->type() == MIRType::Object ||
+                   (object->type() == MIRType::Value && object->mightBeType(MIRType::Object)));
         setResultType(MIRType::Boolean);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(HasClass)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object))
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2273,16 +2273,19 @@ inline int CheckIsSetterOp(JSSetterOp op
  * them.
  */
 #define JS_PSG(name, getter, flags) \
     JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, \
                         JSPROP_SHARED)
 #define JS_PSGS(name, getter, setter, flags) \
     JS_PS_ACCESSOR_SPEC(name, JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(setter), flags, \
                          JSPROP_SHARED)
+#define JS_SYM_GET(symbol, getter, flags) \
+    JS_PS_ACCESSOR_SPEC(reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
+                        JSNATIVE_WRAPPER(getter), JSNATIVE_WRAPPER(nullptr), flags, JSPROP_SHARED)
 #define JS_SELF_HOSTED_GET(name, getterName, flags) \
     JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), JSNATIVE_WRAPPER(nullptr), flags, \
                          JSPROP_SHARED | JSPROP_GETTER)
 #define JS_SELF_HOSTED_GETSET(name, getterName, setterName, flags) \
     JS_PS_ACCESSOR_SPEC(name, SELFHOSTED_WRAPPER(getterName), SELFHOSTED_WRAPPER(setterName), \
                          flags, JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER)
 #define JS_SELF_HOSTED_SYM_GET(symbol, getterName, flags) \
     JS_PS_ACCESSOR_SPEC(reinterpret_cast<const char*>(uint32_t(::JS::SymbolCode::symbol) + 1), \
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -630,21 +630,23 @@ array_length_getter(JSContext* cx, Handl
     vp.setNumber(obj->as<ArrayObject>().length());
     return true;
 }
 
 static bool
 array_length_setter(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
                     ObjectOpResult& result)
 {
+    MOZ_ASSERT(id == NameToId(cx->names().length));
+
     if (!obj->is<ArrayObject>()) {
         // This array .length property was found on the prototype
         // chain. Ideally the setter should not have been called, but since
         // we're here, do an impression of SetPropertyByDefining.
-        return DefineDataProperty(cx, obj, cx->names().length, vp, JSPROP_ENUMERATE, result);
+        return DefineDataProperty(cx, obj, id, vp, JSPROP_ENUMERATE, result);
     }
 
     Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
     MOZ_ASSERT(arr->lengthIsWritable(),
                "setter shouldn't be called if property is non-writable");
 
     return ArraySetLength(cx, arr, id, JSPROP_PERMANENT, vp, result);
 }
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -42,16 +42,20 @@ AtomToId(JSAtom* atom)
 
     uint32_t index;
     if (atom->isIndex(&index) && index <= JSID_INT_MAX)
         return INT_TO_JSID(int32_t(index));
 
     return JSID_FROM_BITS(size_t(atom));
 }
 
+// Use the NameToId method instead!
+inline jsid
+AtomToId(PropertyName* name) = delete;
+
 inline bool
 ValueToIdPure(const Value& v, jsid* id)
 {
     if (v.isString()) {
         if (v.toString()->isAtom()) {
             *id = AtomToId(&v.toString()->asAtom());
             return true;
         }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -744,19 +744,16 @@ extern bool
 SetFunctionNameIfNoOwnName(JSContext* cx, HandleFunction fun, HandleValue name,
                            FunctionPrefixKind prefixKind);
 
 extern JSFunction*
 DefineFunction(JSContext* cx, HandleObject obj, HandleId id, JSNative native,
                unsigned nargs, unsigned flags,
                gc::AllocKind allocKind = gc::AllocKind::FUNCTION);
 
-bool
-FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
-
 extern bool
 fun_toString(JSContext* cx, unsigned argc, Value* vp);
 
 struct WellKnownSymbols;
 
 extern bool
 FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols);
 
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -89,19 +89,16 @@ InitMathClass(JSContext* cx, HandleObjec
 extern uint64_t
 GenerateRandomSeed();
 
 // Fill |seed[0]| and |seed[1]| with random bits, suitable for
 // seeding a XorShift128+ random number generator.
 extern void
 GenerateXorShift128PlusSeed(mozilla::Array<uint64_t, 2>& seed);
 
-extern uint64_t
-random_next(uint64_t* rngState, int bits);
-
 extern double
 math_random_impl(JSContext* cx);
 
 extern bool
 math_random(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 math_abs_handle(JSContext* cx, js::HandleValue v, js::MutableHandleValue r);
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -1436,24 +1436,16 @@ js::NumberToAtom(JSContext* cx, double d
         return nullptr;
 
     CacheNumber(cx, d, atom);
 
     return atom;
 }
 
 JSFlatString*
-js::NumberToString(JSContext* cx, double d)
-{
-    if (JSString* str = NumberToStringWithBase<CanGC>(cx, d, 10))
-        return &str->asFlat();
-    return nullptr;
-}
-
-JSFlatString*
 js::IndexToString(JSContext* cx, uint32_t index)
 {
     if (StaticStrings::hasUint(index))
         return cx->staticStrings().getUint(index);
 
     JSCompartment* c = cx->compartment();
     if (JSFlatString* str = c->dtoaCache.lookup(10, index))
         return str;
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -87,20 +87,16 @@ IsInteger(const Value& val);
 
 /*
  * Convert an integer or double (contained in the given value) to a string and
  * append to the given buffer.
  */
 extern MOZ_MUST_USE bool JS_FASTCALL
 NumberValueToStringBuffer(JSContext* cx, const Value& v, StringBuffer& sb);
 
-/* Same as js_NumberToString, different signature. */
-extern JSFlatString*
-NumberToString(JSContext* cx, double d);
-
 extern JSFlatString*
 IndexToString(JSContext* cx, uint32_t index);
 
 /*
  * Usually a small amount of static storage is enough, but sometimes we need
  * to dynamically allocate much more.  This struct encapsulates that.
  * Dynamically allocated memory will be freed when the object is destroyed.
  */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -260,39 +260,23 @@ js::Throw(JSContext* cx, jsid id, unsign
        return false;
     JSAutoByteString bytes(cx, idstr);
     if (!bytes)
         return false;
 
     if (details) {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr(),
                                    details);
-    }
-    else {
+    } else {
         JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, errorNumber, bytes.ptr());
     }
 
     return false;
 }
 
-bool
-js::Throw(JSContext* cx, JSObject* obj, unsigned errorNumber)
-{
-    if (js_ErrorFormatString[errorNumber].argCount == 1) {
-        RootedValue val(cx, ObjectValue(*obj));
-        ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
-                              JSDVG_IGNORE_STACK, val, nullptr,
-                              nullptr, nullptr);
-    } else {
-        MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
-        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
-    }
-    return false;
-}
-
 
 /*** PropertyDescriptor operations and DefineProperties ******************************************/
 
 static Result<>
 CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
 {
     if (obj && !obj->isCallable()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
@@ -1944,19 +1928,19 @@ JSObject::fixupAfterMovingGC()
                 owner = Forwarded(owner);
             if (owner != &obj && owner->hasFixedElements())
                 obj.elements_ = owner->getElementsHeader()->elements();
             MOZ_ASSERT(!IsForwarded(obj.getElementsHeader()->ownerObject().get()));
         }
     }
 }
 
-bool
-js::SetClassAndProto(JSContext* cx, HandleObject obj,
-                     const Class* clasp, Handle<js::TaggedProto> proto)
+static bool
+SetClassAndProto(JSContext* cx, HandleObject obj,
+                 const Class* clasp, Handle<js::TaggedProto> proto)
 {
     // Regenerate the object's shape. If the object is a proto (isDelegate()),
     // we also need to regenerate shapes for all of the objects along the old
     // prototype chain, in case any entries were filled by looking up through
     // obj. Stop when a non-native object is found, prototype lookups will not
     // be cached across these.
     //
     // How this shape change is done is very delicate; the change can be made
@@ -4051,44 +4035,87 @@ JSObject::constructorDisplayAtom(JSConte
 JSAtom*
 JSObject::maybeConstructorDisplayAtom() const
 {
     if (hasLazyGroup())
         return nullptr;
     return displayAtomFromObjectGroup(*group());
 }
 
-bool
-js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor)
+// ES 2016 7.3.20.
+MOZ_MUST_USE JSObject*
+js::SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
+                       bool (*isDefaultSpecies)(JSContext*, JSFunction*))
 {
-    HandlePropertyName shName = cx->names().SpeciesConstructor;
-    RootedValue func(cx);
-    if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), shName, shName, 2, &func))
-        return false;
-
-    FixedInvokeArgs<2> args(cx);
-
-    args[0].setObject(*obj);
-    args[1].set(defaultCtor);
-
-    if (!Call(cx, func, UndefinedHandleValue, args, pctor))
-        return false;
-
-    pctor.set(args.rval());
-    return true;
+    // Step 1 (implicit).
+
+    // Fast-path for steps 2 - 8. Applies if all of the following conditions
+    // are met:
+    // - obj.constructor can be retrieved without side-effects.
+    // - obj.constructor[[@@species]] can be retrieved without side-effects.
+    // - obj.constructor[[@@species]] is the builtin's original @@species
+    //   getter.
+    RootedValue ctor(cx);
+    bool ctorGetSucceeded = GetPropertyPure(cx, obj, NameToId(cx->names().constructor),
+                                            ctor.address());
+    if (ctorGetSucceeded && ctor.isObject() && &ctor.toObject() == defaultCtor) {
+        RootedObject ctorObj(cx, &ctor.toObject());
+        RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
+        JSFunction* getter;
+        if (GetGetterPure(cx, ctorObj, speciesId, &getter) && getter &&
+            isDefaultSpecies(cx, getter))
+        {
+            return defaultCtor;
+        }
+    }
+
+    // Step 2.
+    if (!ctorGetSucceeded && !GetProperty(cx, obj, obj, cx->names().constructor, &ctor))
+        return nullptr;
+
+    // Step 3.
+    if (ctor.isUndefined())
+        return defaultCtor;
+
+    // Step 4.
+    if (!ctor.isObject()) {
+        JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
+                                  "object's 'constructor' property");
+        return nullptr;
+    }
+
+    // Step 5.
+    RootedObject ctorObj(cx, &ctor.toObject());
+    RootedValue s(cx);
+    RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
+    if (!GetProperty(cx, ctorObj, ctor, speciesId, &s))
+        return nullptr;
+
+    // Step 6.
+    if (s.isNullOrUndefined())
+        return defaultCtor;
+
+    // Step 7.
+    if (IsConstructor(s))
+        return &s.toObject();
+
+    // Step 8.
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR,
+                              "[Symbol.species] property of object's constructor");
+    return nullptr;
 }
 
-bool
+MOZ_MUST_USE JSObject*
 js::SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
-                       MutableHandleValue pctor)
+                       bool (*isDefaultSpecies)(JSContext*, JSFunction*))
 {
     if (!GlobalObject::ensureConstructor(cx, cx->global(), ctorKey))
-        return false;
-    RootedValue defaultCtor(cx, cx->global()->getConstructor(ctorKey));
-    return SpeciesConstructor(cx, obj, defaultCtor, pctor);
+        return nullptr;
+    RootedObject defaultCtor(cx, &cx->global()->getConstructor(ctorKey).toObject());
+    return SpeciesConstructor(cx, obj, defaultCtor, isDefaultSpecies);
 }
 
 bool
 js::Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
         return Proxy::boxedValue_unbox(cx, obj, vp);
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -37,40 +37,16 @@ namespace js {
 using PropertyDescriptorVector = JS::GCVector<JS::PropertyDescriptor>;
 class GCMarker;
 class Nursery;
 
 namespace gc {
 class RelocationOverlay;
 } // namespace gc
 
-inline JSObject*
-CastAsObject(GetterOp op)
-{
-    return JS_FUNC_TO_DATA_PTR(JSObject*, op);
-}
-
-inline JSObject*
-CastAsObject(SetterOp op)
-{
-    return JS_FUNC_TO_DATA_PTR(JSObject*, op);
-}
-
-inline Value
-CastAsObjectJsval(GetterOp op)
-{
-    return ObjectOrNullValue(CastAsObject(op));
-}
-
-inline Value
-CastAsObjectJsval(SetterOp op)
-{
-    return ObjectOrNullValue(CastAsObject(op));
-}
-
 /******************************************************************************/
 
 extern const Class IntlClass;
 extern const Class JSONClass;
 extern const Class MathClass;
 
 class GlobalObject;
 class NewObjectCache;
@@ -880,39 +856,24 @@ GetElement(JSContext* cx, HandleObject o
 inline bool
 GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
            MutableHandleValue vp);
 
 inline bool
 GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp);
 
 inline bool
-GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp)
-{
-    return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), id, vp);
-}
-
-inline bool
 GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, PropertyName* name, Value* vp)
 {
     return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
 }
 
 inline bool
-GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, PropertyName* name, Value* vp)
-{
-    return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), name, vp);
-}
-
-inline bool
 GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp);
 
-inline bool
-GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp);
-
 // Returns whether |obj| or an object on its proto chain may have an interesting
 // symbol property (see JSObject::hasInterestingSymbolProperty). If it returns
 // true, *holder is set to the object that may have this property.
 MOZ_ALWAYS_INLINE bool
 MaybeHasInterestingSymbolProperty(JSContext* cx, JSObject* obj, Symbol* symbol,
                                   JSObject** holder = nullptr);
 
 // Like GetProperty but optimized for interesting symbol properties like
@@ -1120,40 +1081,20 @@ GetBuiltinConstructor(JSContext* cx, JSP
 
 bool
 GetBuiltinPrototype(JSContext* cx, JSProtoKey key, MutableHandleObject objp);
 
 JSObject*
 GetBuiltinPrototypePure(GlobalObject* global, JSProtoKey protoKey);
 
 extern bool
-SetClassAndProto(JSContext* cx, HandleObject obj,
-                 const Class* clasp, Handle<TaggedProto> proto);
-
-extern bool
 IsStandardPrototype(JSObject* obj, JSProtoKey key);
 
 } /* namespace js */
 
-/*
- * Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.
- */
-extern const char js_watch_str[];
-extern const char js_unwatch_str[];
-extern const char js_hasOwnProperty_str[];
-extern const char js_isPrototypeOf_str[];
-extern const char js_propertyIsEnumerable_str[];
-
-#ifdef JS_OLD_GETTER_SETTER_METHODS
-extern const char js_defineGetter_str[];
-extern const char js_defineSetter_str[];
-extern const char js_lookupGetter_str[];
-extern const char js_lookupSetter_str[];
-#endif
-
 namespace js {
 
 inline gc::InitialHeap
 GetInitialHeap(NewObjectKind newKind, const Class* clasp)
 {
     if (newKind == NurseryAllocatedProxy) {
         MOZ_ASSERT(clasp->isProxy());
         MOZ_ASSERT(clasp->hasFinalize());
@@ -1279,19 +1220,16 @@ LookupNameWithGlobalDefault(JSContext* c
 extern bool
 LookupNameUnqualified(JSContext* cx, HandlePropertyName name, HandleObject scopeChain,
                       MutableHandleObject objp);
 
 } // namespace js
 
 namespace js {
 
-extern JSObject*
-FindVariableScope(JSContext* cx, JSFunction** funp);
-
 bool
 LookupPropertyPure(JSContext* cx, JSObject* obj, jsid id, JSObject** objp,
                    PropertyResult* propp);
 
 bool
 LookupOwnPropertyPure(JSContext* cx, JSObject* obj, jsid id, PropertyResult* propp,
                       bool* isTypedArrayOutOfRange = nullptr);
 
@@ -1309,19 +1247,16 @@ GetOwnNativeGetterPure(JSContext* cx, JS
 
 bool
 HasOwnDataPropertyPure(JSContext* cx, JSObject* obj, jsid id, bool* result);
 
 bool
 GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                          MutableHandle<JS::PropertyDescriptor> desc);
 
-bool
-GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp);
-
 /*
  * Like JS::FromPropertyDescriptor, but ignore desc.object() and always set vp
  * to an object on success.
  *
  * Use JS::FromPropertyDescriptor for getOwnPropertyDescriptor, since desc.object()
  * is used to indicate whether a result was found or not.  Use this instead for
  * defineProperty: it would be senseless to define a "missing" property.
  */
@@ -1407,23 +1342,20 @@ NonNullObjectWithName(JSContext* cx, con
     return nullptr;
 }
 
 
 extern bool
 GetFirstArgumentAsObject(JSContext* cx, const CallArgs& args, const char* method,
                          MutableHandleObject objp);
 
-/* Helpers for throwing. These always return false. */
+/* Helper for throwing, always returns false. */
 extern bool
 Throw(JSContext* cx, jsid id, unsigned errorNumber, const char* details = nullptr);
 
-extern bool
-Throw(JSContext* cx, JSObject* obj, unsigned errorNumber);
-
 /*
  * ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
  * of obj's own properties' attributes appropriately: each property becomes
  * non-configurable, and if level == Frozen, data properties become
  * non-writable as well.
  */
 extern bool
 SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level);
@@ -1436,21 +1368,23 @@ FreezeObject(JSContext* cx, HandleObject
 
 /*
  * ES6 rev 29 (6 Dec 2014) 7.3.14. Code shared by Object.isSealed and
  * Object.isFrozen.
  */
 extern bool
 TestIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level, bool* resultp);
 
-extern bool
-SpeciesConstructor(JSContext* cx, HandleObject obj, HandleValue defaultCtor, MutableHandleValue pctor);
+extern MOZ_MUST_USE JSObject*
+SpeciesConstructor(JSContext* cx, HandleObject obj, HandleObject defaultCtor,
+                   bool (*isDefaultSpecies)(JSContext*, JSFunction*));
 
-extern bool
-SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey, MutableHandleValue pctor);
+extern MOZ_MUST_USE JSObject*
+SpeciesConstructor(JSContext* cx, HandleObject obj, JSProtoKey ctorKey,
+                   bool (*isDefaultSpecies)(JSContext*, JSFunction*));
 
 extern bool
 GetObjectFromIncumbentGlobal(JSContext* cx, MutableHandleObject obj);
 
 
 #ifdef DEBUG
 inline bool
 IsObjectValueInCompartment(const Value& v, JSCompartment* comp)
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -247,22 +247,16 @@ js::GetElementNoGC(JSContext* cx, JSObje
         return false;
 
     if (index > JSID_INT_MAX)
         return false;
     return GetPropertyNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
 }
 
 inline bool
-js::GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp)
-{
-    return GetElementNoGC(cx, obj, ObjectValue(*receiver), index, vp);
-}
-
-inline bool
 js::DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     MarkTypePropertyNonData(cx, obj, id);
     if (DeletePropertyOp op = obj->getOpsDeleteProperty())
         return op(cx, obj, id, result);
     return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
 }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -81,24 +81,21 @@ enum JSProtoKey {
     JS_FOR_EACH_PROTOTYPE(PROTOKEY_AND_INITIALIZER)
 #undef PROTOKEY_AND_INITIALIZER
     JSProto_LIMIT
 };
 
 /* Struct forward declarations. */
 struct JSClass;
 struct JSCompartment;
-struct JSCrossCompartmentCall;
 class JSErrorReport;
 struct JSExceptionState;
 struct JSFunctionSpec;
 struct JSLocaleCallbacks;
-struct JSObjectMap;
 struct JSPrincipals;
-struct JSPropertyName;
 struct JSPropertySpec;
 struct JSRuntime;
 struct JSSecurityCallbacks;
 struct JSStructuredCloneCallbacks;
 struct JSStructuredCloneReader;
 struct JSStructuredCloneWriter;
 class JS_PUBLIC_API(JSTracer);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1862,25 +1862,16 @@ StringMatch(JSLinearString* text, JSLine
             match = StringMatch(textChars, textLen, pat->twoByteChars(nogc), patLen);
     }
 
     return (match == -1) ? -1 : start + match;
 }
 
 static const size_t sRopeMatchThresholdRatioLog2 = 4;
 
-bool
-js::StringHasPattern(JSLinearString* text, const char16_t* pat, uint32_t patLen)
-{
-    AutoCheckCannotGC nogc;
-    return text->hasLatin1Chars()
-           ? StringMatch(text->latin1Chars(nogc), text->length(), pat, patLen) != -1
-           : StringMatch(text->twoByteChars(nogc), text->length(), pat, patLen) != -1;
-}
-
 int
 js::StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start)
 {
     return StringMatch(text, pat, start);
 }
 
 // When an algorithm does not need a string represented as a single linear
 // array of characters, this range utility may be used to traverse the string a
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -229,20 +229,16 @@ extern int32_t
 CompareAtoms(JSAtom* atom1, JSAtom* atom2);
 
 /*
  * Return true if the string matches the given sequence of ASCII bytes.
  */
 extern bool
 StringEqualsAscii(JSLinearString* str, const char* asciiBytes);
 
-/* Return true if the string contains a pattern anywhere inside it. */
-extern bool
-StringHasPattern(JSLinearString* text, const char16_t* pat, uint32_t patlen);
-
 extern int
 StringFindPattern(JSLinearString* text, JSLinearString* pat, size_t start);
 
 /* Return true if the string contains a pattern at |start|. */
 extern bool
 HasSubstringAt(JSLinearString* text, JSLinearString* pat, size_t start);
 
 template <typename Char1, typename Char2>
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -43,54 +43,16 @@ js_memcpy(void* dst_, const void* src_, 
     MOZ_ASSERT_IF(src >= dst, (size_t) (src - dst) >= len);
 
     return memcpy(dst, src, len);
 }
 
 namespace js {
 
 template <class T>
-class AlignedPtrAndFlag
-{
-    uintptr_t bits;
-
-  public:
-    AlignedPtrAndFlag(T* t, bool aFlag) {
-        MOZ_ASSERT((uintptr_t(t) & 1) == 0);
-        bits = uintptr_t(t) | uintptr_t(aFlag);
-    }
-
-    T* ptr() const {
-        return (T*)(bits & ~uintptr_t(1));
-    }
-
-    bool flag() const {
-        return (bits & 1) != 0;
-    }
-
-    void setPtr(T* t) {
-        MOZ_ASSERT((uintptr_t(t) & 1) == 0);
-        bits = uintptr_t(t) | uintptr_t(flag());
-    }
-
-    void setFlag() {
-        bits |= 1;
-    }
-
-    void unsetFlag() {
-        bits &= ~uintptr_t(1);
-    }
-
-    void set(T* t, bool aFlag) {
-        MOZ_ASSERT((uintptr_t(t) & 1) == 0);
-        bits = uintptr_t(t) | aFlag;
-    }
-};
-
-template <class T>
 static inline void
 Reverse(T* beg, T* end)
 {
     while (beg != end) {
         if (--end == beg)
             return;
         T tmp = *beg;
         *beg = *end;
@@ -178,32 +140,16 @@ Min(T t1, T t2)
 
 template <class T>
 static inline T
 Max(T t1, T t2)
 {
     return t1 > t2 ? t1 : t2;
 }
 
-/* Allows a const variable to be initialized after its declaration. */
-template <class T>
-static T&
-InitConst(const T& t)
-{
-    return const_cast<T&>(t);
-}
-
-template <class T, class U>
-MOZ_ALWAYS_INLINE T&
-ImplicitCast(U& u)
-{
-    T& t = u;
-    return t;
-}
-
 template<typename T>
 class MOZ_RAII AutoScopedAssign
 {
   public:
     AutoScopedAssign(T* addr, const T& value
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : addr_(addr), old(*addr_)
     {
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1404,17 +1404,17 @@ js::proxy_revocable(JSContext* cx, unsig
 
     if (!ProxyCreate(cx, args, "Proxy.revocable"))
         return false;
 
     RootedValue proxyVal(cx, args.rval());
     MOZ_ASSERT(proxyVal.toObject().is<ProxyObject>());
 
     RootedObject revoker(cx, NewFunctionByIdWithReserved(cx, RevokeProxy, 0, 0,
-                         AtomToId(cx->names().revoke)));
+                         NameToId(cx->names().revoke)));
     if (!revoker)
         return false;
 
     revoker->as<JSFunction>().initExtendedSlot(ScriptedProxyHandler::REVOKE_SLOT, proxyVal);
 
     RootedPlainObject result(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!result)
         return false;
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Destructuring/bug1396261.js
@@ -0,0 +1,44 @@
+// Ensure array or object literals with trailing property accessors are not
+// treated as nested destructuring patterns in assignment destructuring
+// contexts.
+
+// Array destructuring with normal element.
+[{a: 0}.x] = [];
+[[0].x] = [];
+
+// Array destructuring with spread element.
+[...{a: 0}.x] = [];
+[...[0].x] = [];
+
+// Object destructuring with normal property.
+({a: {b: 0}.x} = {});
+({a: [0].x} = {});
+
+// Object destructuring with spread property.
+({...{b: 0}.x} = {});
+({...[0].x} = {});
+
+// Object literal with initializer shorthand in destructuring context.
+assertThrowsInstanceOf(() => Function(`[{a = 0}.x] = [];`), SyntaxError);
+assertThrowsInstanceOf(() => Function(`[...{a = 0}.x] = [];`), SyntaxError);
+assertThrowsInstanceOf(() => Function(`({a: {b = 0}.x} = {});`), SyntaxError);
+assertThrowsInstanceOf(() => Function(`({...{b = 0}.x} = {});`), SyntaxError);
+
+// Object destructuring with "eval" or "arguments" shorthand in strict mode.
+(function() {
+    "use strict";
+
+    // Ensure "eval" resp. "arguments" is not treated as an assignment.
+    [{eval}.x] = [];
+    [...{eval}.x] = [];
+    ({a: {eval}.x} = {});
+    ({...{eval}.x} = {});
+
+    [{arguments}.x] = [];
+    [...{arguments}.x] = [];
+    ({a: {arguments}.x} = {});
+    ({...{arguments}.x} = {});
+})();
+
+if (typeof reportCompare === "function")
+    reportCompare(0, 0);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -6756,20 +6756,20 @@ DebuggerScript_getOffsetsCoverage(JSCont
     //  - offset          PC offset of the current opcode.
     //  - lineNumber      Line of the current opcode.
     //  - columnNumber    Column of the current opcode.
     //  - count           Number of times the instruction got executed.
     RootedObject result(cx, NewDenseEmptyArray(cx));
     if (!result)
         return false;
 
-    RootedId offsetId(cx, AtomToId(cx->names().offset));
-    RootedId lineNumberId(cx, AtomToId(cx->names().lineNumber));
-    RootedId columnNumberId(cx, AtomToId(cx->names().columnNumber));
-    RootedId countId(cx, AtomToId(cx->names().count));
+    RootedId offsetId(cx, NameToId(cx->names().offset));
+    RootedId lineNumberId(cx, NameToId(cx->names().lineNumber));
+    RootedId columnNumberId(cx, NameToId(cx->names().columnNumber));
+    RootedId countId(cx, NameToId(cx->names().count));
 
     RootedObject item(cx);
     RootedValue offsetValue(cx);
     RootedValue lineNumberValue(cx);
     RootedValue columnNumberValue(cx);
     RootedValue countValue(cx);
 
     // Iterate linearly over the bytecode.
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1854,44 +1854,16 @@ js::NativeDefineProperty(JSContext* cx, 
                          ObjectOpResult& result)
 {
     Rooted<PropertyDescriptor> desc(cx);
     desc.initFields(nullptr, value, attrs, getter, setter);
     return NativeDefineProperty(cx, obj, id, desc, result);
 }
 
 bool
-js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
-                         HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
-                         ObjectOpResult& result)
-{
-    RootedId id(cx, NameToId(name));
-    return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
-}
-
-bool
-js::NativeDefineElement(JSContext* cx, HandleNativeObject obj, uint32_t index,
-                        HandleValue value, GetterOp getter, SetterOp setter, unsigned attrs,
-                        ObjectOpResult& result)
-{
-    RootedId id(cx);
-    if (index <= JSID_INT_MAX) {
-        id = INT_TO_JSID(index);
-        return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
-    }
-
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    return NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result);
-}
-
-bool
 js::NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                          HandleValue value, JSGetterOp getter, JSSetterOp setter,
                          unsigned attrs)
 {
     ObjectOpResult result;
     if (!NativeDefineProperty(cx, obj, id, value, getter, setter, attrs, result))
         return false;
     if (!result) {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1392,26 +1392,16 @@ NativeDefineProperty(JSContext* cx, Hand
                      Handle<JS::PropertyDescriptor> desc,
                      ObjectOpResult& result);
 
 extern bool
 NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
                      JSGetterOp getter, JSSetterOp setter, unsigned attrs,
                      ObjectOpResult& result);
 
-extern bool
-NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
-                     HandleValue value, GetterOp getter, SetterOp setter,
-                     unsigned attrs, ObjectOpResult& result);
-
-extern bool
-NativeDefineElement(JSContext* cx, HandleNativeObject obj, uint32_t index, HandleValue value,
-                    JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                    ObjectOpResult& result);
-
 /* If the result out-param is omitted, throw on failure. */
 extern bool
 NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
                      JSGetterOp getter, JSSetterOp setter, unsigned attrs);
 
 extern bool
 NativeDefineProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
                      HandleValue value, JSGetterOp getter, JSSetterOp setter,
deleted file mode 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -181,23 +181,22 @@ intrinsic_IsCallable(JSContext* cx, unsi
 }
 
 static bool
 intrinsic_IsConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
-    RootedValue val(cx, args[0]);
-    if (!IsConstructor(val)) {
+    if (!IsConstructor(args[0])) {
         args.rval().setBoolean(false);
         return true;
     }
 
-    RootedObject obj(cx, &val.toObject());
+    JSObject* obj = &args[0].toObject();
     if (!IsWrapper(obj)) {
         args.rval().setBoolean(true);
         return true;
     }
 
     obj = UncheckedUnwrap(obj);
     args.rval().setBoolean(obj && obj->isConstructor());
     return true;
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1062,98 +1062,57 @@ TypedArrayObjectTemplate<T>::AllocateArr
     // ES 2016 draft Mar 25, 2016 24.1.1.1 steps 1 (remaining part), 2-6.
     if (!maybeCreateArrayBuffer(cx, count, unit, proto, buffer))
         return false;
 
     return true;
 }
 
 static bool
-IsArrayBufferConstructor(const Value& v)
-{
-    return v.isObject() &&
-           v.toObject().is<JSFunction>() &&
-           v.toObject().as<JSFunction>().isNative() &&
-           v.toObject().as<JSFunction>().native() == ArrayBufferObject::class_constructor;
-}
-
-static bool
-IsArrayBufferSpecies(JSContext* cx, HandleObject origBuffer)
+IsArrayBufferSpecies(JSContext* cx, JSFunction* species)
 {
-    RootedValue ctor(cx);
-    if (!GetPropertyPure(cx, origBuffer, NameToId(cx->names().constructor), ctor.address()))
-        return false;
-
-    if (!IsArrayBufferConstructor(ctor))
-        return false;
-
-    RootedObject ctorObj(cx, &ctor.toObject());
-    RootedId speciesId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().species));
-    JSFunction* getter;
-    if (!GetGetterPure(cx, ctorObj, speciesId, &getter))
-        return false;
-
-    if (!getter)
-        return false;
-
-    return IsSelfHostedFunctionWithName(getter, cx->names().ArrayBufferSpecies);
+    return IsSelfHostedFunctionWithName(species, cx->names().ArrayBufferSpecies);
 }
 
-static bool
+static JSObject*
 GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
-                      SpeciesConstructorOverride override, MutableHandleValue ctor)
+                      SpeciesConstructorOverride override)
 {
     if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
-        return false;
-    RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
+        return nullptr;
+    RootedObject defaultCtor(cx, &cx->global()->getConstructor(JSProto_ArrayBuffer).toObject());
 
     // Use the current global's ArrayBuffer if the override is set.
-    if (override == SpeciesConstructorOverride::ArrayBuffer) {
-        ctor.set(defaultCtor);
-        return true;
-    }
-
-    if (!isWrapped) {
-        // As an optimization, avoid calling into self-hosted code if |obj|'s
-        // constructor is the built-in ArrayBuffer and the constructor's
-        // species property is the original ArrayBuffer[@@species] function.
-        if (IsArrayBufferSpecies(cx, obj))
-            ctor.set(defaultCtor);
-        else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
-            return false;
-
-        return true;
-    }
+    if (override == SpeciesConstructorOverride::ArrayBuffer)
+        return defaultCtor;
 
     RootedObject wrappedObj(cx, obj);
-    if (!cx->compartment()->wrap(cx, &wrappedObj))
-        return false;
+    if (isWrapped && !cx->compartment()->wrap(cx, &wrappedObj))
+        return nullptr;
 
-    if (!SpeciesConstructor(cx, wrappedObj, defaultCtor, ctor))
-        return false;
-
-    return true;
+    return SpeciesConstructor(cx, wrappedObj, defaultCtor, IsArrayBufferSpecies);
 }
 
 // ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
 template<typename T>
 /* static */ bool
 TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
                                                     Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
                                                     bool isWrapped, uint32_t srcByteOffset,
                                                     uint32_t srcLength,
                                                     SpeciesConstructorOverride override,
                                                     MutableHandle<ArrayBufferObject*> buffer)
 {
     // Step 1 (skipped).
 
     // Step 2.a.
-    RootedValue cloneCtor(cx);
-    if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, override, &cloneCtor))
+    JSObject* ctorObj = GetSpeciesConstructor(cx, srcBuffer, isWrapped, override);
+    if (!ctorObj)
         return false;
+    RootedValue cloneCtor(cx, ObjectValue(*ctorObj));
 
     // Step 2.b.
     if (srcBuffer->isDetached()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
     // Steps 3-4 (skipped).
@@ -1261,19 +1220,20 @@ TypedArrayObjectTemplate<T>::fromTypedAr
         // Steps 16.b-c.
         if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, override,
                                     &buffer))
         {
             return nullptr;
         }
     } else {
         // Steps 17.a-b.
-        RootedValue bufferCtor(cx);
-        if (!GetSpeciesConstructor(cx, srcData, isWrapped, override, &bufferCtor))
+        JSObject* ctorObj = GetSpeciesConstructor(cx, srcData, isWrapped, override);
+        if (!ctorObj)
             return nullptr;
+        RootedValue bufferCtor(cx, ObjectValue(*ctorObj));
 
         // Steps 14-15, 17.c.
         if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
             return nullptr;
 
         // Step 17.d.
         if (srcArray->hasDetachedBuffer()) {
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1760,17 +1760,17 @@ CombinePlainObjectProperties(PlainObject
 
     return true;
 }
 
 static bool
 CombineArrayObjectElements(JSContext* cx, ArrayObject* obj, JSValueType* elementType)
 {
     if (obj->inDictionaryMode() ||
-        obj->lastProperty()->propid() != AtomToId(cx->names().length) ||
+        obj->lastProperty()->propid() != NameToId(cx->names().length) ||
         !obj->lastProperty()->previous()->isEmptyShape())
     {
         // Only use an unboxed representation if the object has no properties.
         return false;
     }
 
     for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
         Value val = obj->getDenseElement(i);
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -55,17 +55,18 @@
 
 using namespace mozilla;
 using namespace mozilla::scache;
 using namespace mozilla::loader;
 using namespace xpc;
 using namespace JS;
 
 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
-static const char kJSCachePrefix[] = "jsloader";
+
+#define JS_CACHE_PREFIX(aType) "jsloader/" aType
 
 /**
  * Buffer sizes for serialization and deserialization of scripts.
  * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
  */
 #define XPC_SERIALIZATION_BUFFER_SIZE   (64 * 1024)
 #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
 
@@ -298,17 +299,26 @@ NS_IMPL_ISUPPORTS(mozJSComponentLoader,
                   xpcIJSModuleLoader,
                   nsIObserver)
 
 nsresult
 mozJSComponentLoader::ReallyInit()
 {
     MOZ_ASSERT(!mInitialized);
 
-    mShareLoaderGlobal = Preferences::GetBool("jsloader.shareGlobal");
+    const char* shareGlobal = PR_GetEnv("MOZ_LOADER_SHARE_GLOBAL");
+    if (shareGlobal && *shareGlobal) {
+        nsDependentCString val(shareGlobal);
+        mShareLoaderGlobal = !(val.EqualsLiteral("0") ||
+                               val.LowerCaseEqualsLiteral("no") ||
+                               val.LowerCaseEqualsLiteral("false") ||
+                               val.LowerCaseEqualsLiteral("off"));
+    } else {
+        mShareLoaderGlobal = Preferences::GetBool("jsloader.shareGlobal");
+    }
 
     nsresult rv;
     nsCOMPtr<nsIObserverService> obsSvc =
         do_GetService(kObserverServiceContractID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -696,17 +706,18 @@ mozJSComponentLoader::ObjectForLocation(
     // the startupcache.  Note: as a rule, startupcache errors are not fatal
     // to loading the script, since we can always slow-load.
 
     bool writeToCache = false;
     StartupCache* cache = StartupCache::GetSingleton();
 
     aInfo.EnsureResolvedURI();
 
-    nsAutoCString cachePath(kJSCachePrefix);
+    nsAutoCString cachePath(reuseGlobal ? JS_CACHE_PREFIX("non-syntactic")
+                                        : JS_CACHE_PREFIX("global"));
     rv = PathifyURI(aInfo.ResolvedURI(), cachePath);
     NS_ENSURE_SUCCESS(rv, rv);
 
     script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath);
     if (!script && cache) {
         ReadCachedScript(cache, cachePath, cx, &script);
     }
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/marionette/manifest.ini
@@ -0,0 +1,3 @@
+
+[test_loader_global_sharing.py]
+skip-if = !manage_instance || appname == 'fennec'
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/marionette/moz.build
@@ -0,0 +1,7 @@
+# -*- 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/.
+
+MARIONETTE_UNIT_MANIFESTS += ['manifest.ini']
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/marionette/test_loader_global_sharing.py
@@ -0,0 +1,119 @@
+from __future__ import print_function
+
+from marionette_harness import MarionetteTestCase
+
+
+GLOBAL_SHARING_PREF = 'jsloader.shareGlobal'
+GLOBAL_SHARING_VAR = 'MOZ_LOADER_SHARE_GLOBAL'
+
+
+class TestLoaderGlobalSharing(MarionetteTestCase):
+    sandbox_name = 'loader-global-sharing'
+
+    def execute_script(self, code, *args, **kwargs):
+        with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
+            return self.marionette.execute_script(code,
+                                                  new_sandbox=False,
+                                                  sandbox=self.sandbox_name,
+                                                  *args, **kwargs)
+
+    def get_global_sharing_enabled(self):
+        return self.execute_script(r'''
+          Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+          return (Cu.getGlobalForObject(Services) ===
+                  Cu.getGlobalForObject(XPCOMUtils))
+        ''')
+
+    def set_env(self, env, value):
+        self.execute_script('env.set(arguments[0], arguments[1]);',
+                            script_args=(env, value))
+
+    def get_env(self, env):
+        return self.execute_script('return env.get(arguments[0]);',
+                                   script_args=(env,))
+
+    def restart(self, prefs=None, env=None):
+        if prefs:
+            self.marionette.set_prefs(prefs)
+
+        if env:
+            for name, value in env.items():
+                self.set_env(name, value)
+
+        self.marionette.restart(in_app=True, clean=False)
+        self.setUpSession()
+
+        # Sanity check our environment.
+        if prefs:
+            for key, val in prefs.items():
+                self.assertEqual(self.marionette.get_pref(key), val)
+        if env:
+            for key, val in env.items():
+                self.assertEqual(self.get_env(key), val or '')
+
+    def setUpSession(self):
+        self.marionette.set_context(self.marionette.CONTEXT_CHROME)
+
+        self.execute_script(r'''
+          const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} =
+                    Components;
+
+          // We're running in a function, in a sandbox, that inherits from an
+          // X-ray wrapped window. Anything we want to be globally available
+          // needs to be defined on that window.
+          Object.assign(window, {Cc, Ci, Cu, Cr});
+
+          Cu.import("resource://gre/modules/Services.jsm");
+          window.env = Cc["@mozilla.org/process/environment;1"]
+                    .getService(Ci.nsIEnvironment);
+        ''')
+
+    def setUp(self):
+        super(TestLoaderGlobalSharing, self).setUp()
+
+        self.default_pref_value = self.marionette.get_pref(GLOBAL_SHARING_PREF)
+
+        self.setUpSession()
+
+    def tearDown(self):
+        self.marionette.restart(clean=True)
+
+        super(TestLoaderGlobalSharing, self).tearDown()
+
+    def test_global_sharing_settings(self):
+        # The different cases to test, with the first element being the value
+        # of the MOZ_LOADER_SHARE_GLOBAL environment variable, and the second
+        # being the value of the "jsloader.shareGlobal" preference.
+        #
+        # The browser is restarted after each change, but the profile is not
+        # reset.
+        CASES = (
+            (None, False),
+            (None, True),
+            ('0', True),
+            ('1', True),
+            ('0', False),
+            ('1', False),
+        )
+
+        for var, pref in CASES:
+            print('Testing %s=%r %s=%r' % (GLOBAL_SHARING_VAR, var,
+                                           GLOBAL_SHARING_PREF, pref))
+
+            # The value of the environment variable takes precedence if it's
+            # defined.
+            expect_sharing = pref if var is None else var != '0'
+
+            self.restart(prefs={GLOBAL_SHARING_PREF: pref},
+                         env={GLOBAL_SHARING_VAR: var})
+
+            have_sharing = self.get_global_sharing_enabled()
+
+            # FIXME: User preference values currently do not always take
+            # effect early enough to influence loader behavior.
+            msg = ('Global sharing state should match settings: %r != %r'
+                   % (have_sharing, expect_sharing))
+            if var is not None or pref == self.default_pref_value:
+                self.assertEqual(have_sharing, expect_sharing, msg)
+            elif have_sharing != expect_sharing:
+                print('TEST-EXPECTED-FAIL: ' + msg)
--- a/js/xpconnect/tests/moz.build
+++ b/js/xpconnect/tests/moz.build
@@ -1,15 +1,16 @@
 # -*- 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/.
 
 TEST_DIRS += [
+    'marionette',
     'mochitest',
     'chrome',
     'browser',
     'components/native',
 ]
 
 if CONFIG['COMPILE_ENVIRONMENT']:
     TEST_DIRS += [
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -38,16 +38,25 @@ class ServoRestyleState
 public:
   ServoRestyleState(ServoStyleSet& aStyleSet, nsStyleChangeList& aChangeList,
                     nsTArray<nsIFrame*>& aPendingWrapperRestyles)
     : mStyleSet(aStyleSet)
     , mChangeList(aChangeList)
     , mPendingWrapperRestyles(aPendingWrapperRestyles)
     , mPendingWrapperRestyleOffset(aPendingWrapperRestyles.Length())
     , mChangesHandled(nsChangeHint(0))
+#ifdef DEBUG
+    // If !mOwner, then we wouldn't have processed our wrapper restyles, because
+    // we only process those when handling an element with a frame.  But that's
+    // OK, because if we started our traversal at an element with no frame
+    // (e.g. it's display:contents), that means the wrapper frames in our list
+    // actually inherit from one of its ancestors, not from it, and hence not
+    // restyling them is OK.
+    , mAssertWrapperRestyleLength(false)
+#endif // DEBUG
   {}
 
   // We shouldn't assume that changes handled from our parent are handled for
   // our children too if we're out of flow since they aren't necessarily
   // parented in DOM order, and thus a change handled by a DOM ancestor doesn't
   // necessarily mean that it's handled for an ancestor frame.
   enum class Type
   {
@@ -154,17 +163,17 @@ private:
   // special cases for stuff like wrapper frames, ::backdrop, and so on).
 #ifdef DEBUG
   const nsIFrame* mOwner { nullptr };
 #endif
 
   // Whether we should assert in our destructor that we've processed all of the
   // relevant wrapper restyles.
 #ifdef DEBUG
-  const bool mAssertWrapperRestyleLength = true;
+  const bool mAssertWrapperRestyleLength;
 #endif // DEBUG
 };
 
 enum class ServoPostTraversalFlags : uint32_t;
 
 /**
  * Restyle manager for a Servo-backed style system.
  */
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1397398-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+window.onload = () => {
+a = document.createElement("x")
+document.documentElement.appendChild(a)
+a.animate([{ "filter": "sepia(7)" }], 3000)
+b = document.createElement("caption")
+a.appendChild(b)
+a.style = "display:contents"
+//DDBEGIN
+b.animate([{ "padding": "912q" }], 1500)
+//DDEND
+a.animate([{}])
+}
+</script>
+</head>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1397398-2.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+window.onload = () => {
+a = document.createElement("x")
+document.documentElement.appendChild(a)
+a.animate([{ "color": "green" }], 3000)
+b = document.createElement("caption")
+a.appendChild(b)
+a.style = "display:contents"
+//DDBEGIN
+b.animate([{ "text-indent": "912q" }], 1500)
+//DDEND
+a.animate([{}])
+}
+</script>
+</head>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1397398-3.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<span id="x" style="display: contents">
+  <span style="display: table-caption">
+</span>
+<script>
+  document.body.offsetWidth;
+  x.style.color = "green";
+</script>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -492,8 +492,11 @@ load 1343937.html
 asserts(0-1) load 1343606.html # bug 1343948
 load 1352380.html
 load 1362423-1.html
 load 1381323.html
 asserts-if(!stylo,1) load 1388625-1.html # bug 1389286
 load 1390389.html
 load 1395591-1.html
 load 1395715-1.html
+load 1397398-1.html
+load 1397398-2.html
+load 1397398-3.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7738,49 +7738,63 @@ nsCSSFrameConstructor::ContentAppended(n
 
   // We should never get here with fieldsets or details, since they have
   // multiple insertion points.
   MOZ_ASSERT(!parentFrame->IsFieldSetFrame() && !parentFrame->IsDetailsFrame(),
              "Parent frame should not be fieldset or details!");
 
   // Deal with possible :after generated content on the parent
   nsIFrame* parentAfterFrame;
+  nsContainerFrame* preAdjustedParentFrame = parentFrame;
   parentFrame =
     ::AdjustAppendParentForAfterContent(this, insertion.mContainer, parentFrame,
                                         aFirstNewContent, &parentAfterFrame);
 
+  // See if the containing block has :first-letter style applied.
+  bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
+  nsContainerFrame* containingBlock = GetFloatContainingBlock(parentFrame);
+  if (containingBlock) {
+    haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
+    haveFirstLineStyle =
+      ShouldHaveFirstLineStyle(containingBlock->GetContent(),
+                               containingBlock->StyleContext());
+  }
+
+  if (haveFirstLetterStyle) {
+    AutoWeakFrame wf(parentAfterFrame);
+    // Before we get going, remove the current letter frames
+    RemoveLetterFrames(mPresShell, containingBlock);
+
+    if (parentAfterFrame && !wf) {
+      // Ouch, parentAfterFrame was a letter frame and we just deleted it!
+      // Retry AdjustAppendParentForAfterContent; fortunately this is rare.
+      parentFrame =
+        ::AdjustAppendParentForAfterContent(this, insertion.mContainer,
+                                            preAdjustedParentFrame,
+                                            aFirstNewContent, &parentAfterFrame);
+      if (parentFrame != preAdjustedParentFrame) {
+        containingBlock = GetFloatContainingBlock(parentFrame);
+      }
+    }
+  }
+
   // Create some new frames
   //
   // We use the provided tree match context, or create a new one on the fly
   // otherwise.
   Maybe<TreeMatchContext> matchContext;
   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
     matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
     matchContext->InitAncestors(aContainer->AsElement());
   }
   nsFrameConstructorState state(mPresShell,
                                 matchContext.ptrOr(aProvidedTreeMatchContext),
                                 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
-                                GetFloatContainingBlock(parentFrame));
-
-  // See if the containing block has :first-letter style applied.
-  bool haveFirstLetterStyle = false, haveFirstLineStyle = false;
-  nsContainerFrame* containingBlock = state.mFloatedItems.containingBlock;
-  if (containingBlock) {
-    haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
-    haveFirstLineStyle =
-      ShouldHaveFirstLineStyle(containingBlock->GetContent(),
-                               containingBlock->StyleContext());
-  }
-
-  if (haveFirstLetterStyle) {
-    // Before we get going, remove the current letter frames
-    RemoveLetterFrames(state.mPresShell, containingBlock);
-  }
+                                containingBlock);
 
   LayoutFrameType frameType = parentFrame->Type();
 
   FlattenedChildIterator iter(aContainer);
   bool haveNoXBLChildren = (!iter.XBLInvolved() || !iter.GetNextChild());
   FrameConstructionItemList items;
   if (aFirstNewContent->GetPreviousSibling() &&
       GetParentType(frameType) == eTypeBlock &&
--- a/layout/generic/nsFrameIdList.h
+++ b/layout/generic/nsFrameIdList.h
@@ -141,16 +141,22 @@ FRAME_ID(nsTextFrame, Text, Leaf)
 FRAME_ID(nsTitleBarFrame, Box, NotLeaf)
 FRAME_ID(nsTreeBodyFrame, LeafBox, Leaf)
 FRAME_ID(nsTreeColFrame, Box, NotLeaf)
 FRAME_ID(nsVideoFrame, HTMLVideo, Leaf)
 FRAME_ID(nsXULLabelFrame, XULLabel, NotLeaf)
 FRAME_ID(nsXULScrollFrame, Scroll, NotLeaf)
 FRAME_ID(ViewportFrame, Viewport, NotLeaf)
 
+// The following ABSTRACT_FRAME_IDs needs to come after the above
+// FRAME_IDs, because we have two separate enums, one that includes
+// only FRAME_IDs and another which includes both and we depend on
+// FRAME_IDs to have the same number in both.
+// See ClassID (the former) and FrameIID in nsQueryFrame.h.
+
 // Non-concrete classes (for FrameIID use)
 ABSTRACT_FRAME_ID(nsContainerFrame)
 ABSTRACT_FRAME_ID(nsFormControlFrame)
 ABSTRACT_FRAME_ID(nsIFrame)
 ABSTRACT_FRAME_ID(nsLeafFrame)
 ABSTRACT_FRAME_ID(nsMathMLContainerFrame)
 ABSTRACT_FRAME_ID(nsRubyContentFrame)
 ABSTRACT_FRAME_ID(nsSplittableFrame)
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -708,16 +708,17 @@ protected:
    *
    * @param  aDestructRoot is the root of the subtree being destroyed
    */
   virtual void DestroyFrom(nsIFrame* aDestructRoot) = 0;
   friend class nsFrameList; // needed to pass aDestructRoot through to children
   friend class nsLineBox;   // needed to pass aDestructRoot through to children
   friend class nsContainerFrame; // needed to pass aDestructRoot through to children
   friend class nsFrame; // need to assign mParent
+  template<class Source> friend class do_QueryFrameHelper; // to read mClass
 public:
 
   /**
    * Get the content object associated with this frame. Does not add a reference.
    */
   nsIContent* GetContent() const { return mContent; }
 
   /**
@@ -4481,16 +4482,23 @@ private:
   void operator delete[](void*) = delete;
 
   void Init(nsIFrame* aFrame);
 
   AutoWeakFrame*  mPrev;
   nsIFrame*       mFrame;
 };
 
+// Use nsIFrame's fast-path to avoid QueryFrame:
+inline do_QueryFrameHelper<nsIFrame>
+do_QueryFrame(AutoWeakFrame& s)
+{
+  return do_QueryFrameHelper<nsIFrame>(s.GetFrame());
+}
+
 /**
  * @see AutoWeakFrame
  */
 class MOZ_HEAP_CLASS WeakFrame
 {
 public:
   WeakFrame() : mFrame(nullptr) {}
 
@@ -4538,16 +4546,23 @@ public:
   nsIFrame* GetFrame() const { return mFrame; }
 
 private:
   void Init(nsIFrame* aFrame);
 
   nsIFrame* mFrame;
 };
 
+// Use nsIFrame's fast-path to avoid QueryFrame:
+inline do_QueryFrameHelper<nsIFrame>
+do_QueryFrame(WeakFrame& s)
+{
+  return do_QueryFrameHelper<nsIFrame>(s.GetFrame());
+}
+
 inline bool
 nsFrameList::ContinueRemoveFrame(nsIFrame* aFrame)
 {
   MOZ_ASSERT(!aFrame->GetPrevSibling() || !aFrame->GetNextSibling(),
              "Forgot to call StartRemoveFrame?");
   if (aFrame == mLastChild) {
     MOZ_ASSERT(!aFrame->GetNextSibling(), "broken frame list");
     nsIFrame* prevSibling = aFrame->GetPrevSibling();
--- a/layout/generic/nsQueryFrame.h
+++ b/layout/generic/nsQueryFrame.h
@@ -77,34 +77,71 @@ public:
 #include "nsFrameIdList.h"
 #undef FRAME_ID
 #undef ABSTRACT_FRAME_ID
   };
 
   virtual void* QueryFrame(FrameIID id) = 0;
 };
 
-class do_QueryFrame
+class nsIFrame;
+
+template<class Source>
+class do_QueryFrameHelper
 {
 public:
-  explicit do_QueryFrame(nsQueryFrame *s) : mRawPtr(s) { }
+  explicit do_QueryFrameHelper(Source* s) : mRawPtr(s) { }
 
   // The return and argument types here are arbitrarily selected so no
   // corresponding member function exists.
-  typedef void (do_QueryFrame::* MatchNullptr)(double, float);
+  typedef void (do_QueryFrameHelper::* MatchNullptr)(double, float);
   // Implicit constructor for nullptr, trick borrowed from already_AddRefed.
-  MOZ_IMPLICIT do_QueryFrame(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
+  MOZ_IMPLICIT do_QueryFrameHelper(MatchNullptr aRawPtr) : mRawPtr(nullptr) {}
 
   template<class Dest>
   operator Dest*() {
     static_assert(mozilla::IsSame<Dest, typename Dest::Has_NS_DECL_QUERYFRAME_TARGET>::value,
                   "Dest must declare itself as a queryframe target");
-    if (!mRawPtr)
+    if (!mRawPtr) {
       return nullptr;
-
+    }
+    if (Dest* f = FastQueryFrame<Source, Dest>::QueryFrame(mRawPtr)) {
+      MOZ_ASSERT(f ==
+                 reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID)),
+                 "fast and slow paths should give the same result");
+      return f;
+    }
     return reinterpret_cast<Dest*>(mRawPtr->QueryFrame(Dest::kFrameIID));
   }
 
 private:
-  nsQueryFrame *mRawPtr;
+  // For non-nsIFrame types there is no fast-path.
+  template<class Src, class Dst, typename = void, typename = void>
+  struct FastQueryFrame
+  {
+    static Dst* QueryFrame(Src* aFrame) { return nullptr; }
+  };
+  
+  // Specialization for any nsIFrame type to any nsIFrame type -- if the source
+  // instance's mClass matches kFrameIID of the destination type then
+  // downcasting is safe.
+  template<class Src, class Dst>
+  struct FastQueryFrame<Src, Dst,
+    typename mozilla::EnableIf<mozilla::IsBaseOf<nsIFrame, Src>::value>::Type,
+    typename mozilla::EnableIf<mozilla::IsBaseOf<nsIFrame, Dst>::value>::Type>
+  {
+    static Dst* QueryFrame(Src* aFrame) {
+      return nsQueryFrame::FrameIID(aFrame->mClass) == Dst::kFrameIID ?
+        reinterpret_cast<Dst*>(aFrame) : nullptr;
+    }
+  };
+
+  Source* mRawPtr;
 };
 
+template<class T>
+inline do_QueryFrameHelper<T>
+do_QueryFrame(T* s)
+{
+  return do_QueryFrameHelper<T>(s);
+}
+
 #endif // nsQueryFrame_h
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1228,17 +1228,17 @@ fuzzy-if(skiaContent,1,5) == 459443-1.ht
 == 460012-1.html 460012-1-ref.html
 == 461266-1.html 461266-1-ref.html
 fuzzy-if(skiaContent,1,12000) fails-if(webrender) == 461512-1.html 461512-1-ref.html # bug 1382896 for webrender
 == 462844-1.html 462844-ref.html
 == 462844-2.html 462844-ref.html
 == 462844-3.html 462844-ref.html
 == 462844-4.html 462844-ref.html
 == 463204-1.html 463204-1-ref.html
-fuzzy-if(webrender,16,3425) == 463217-1.xul 463217-1-ref.xul
+fuzzy-if(webrender,16-16,3425-3425) == 463217-1.xul 463217-1-ref.xul
 == 463952-1.html 463952-1-ref.html
 == 464811-1.html 464811-1-ref.html
 == 465574-1.html 465574-1-ref.html # bug 421436
 == 466258-1.html 466258-1-ref.html
 == 466395-1.html 466395-1-ref.html
 == 466395-2.html 466395-2-ref.html
 == 467084-1.html 467084-1-ref.html
 == 467084-2.html 467084-2-ref.html
--- a/layout/style/ServoStyleContext.h
+++ b/layout/style/ServoStyleContext.h
@@ -106,16 +106,26 @@ public:
   void AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aCVsSize) const
   {
     // Note: |this| sits within a servo_arc::Arc, i.e. it is preceded by a
     // refcount. So we need to measure it with a function that can handle an
     // interior pointer. We use ServoComputedValuesMallocEnclosingSizeOf to
     // clearly identify in DMD's output the memory measured here.
     *aCVsSize += ServoComputedValuesMallocEnclosingSizeOf(this);
     mSource.AddSizeOfExcludingThis(aSizes);
+
+    if (mNextInheritingAnonBoxStyle &&
+        !aSizes.mState.HaveSeenPtr(mNextInheritingAnonBoxStyle)) {
+      mNextInheritingAnonBoxStyle->AddSizeOfIncludingThis(aSizes, aCVsSize);
+    }
+
+    if (mNextLazyPseudoStyle &&
+        !aSizes.mState.HaveSeenPtr(mNextLazyPseudoStyle)) {
+      mNextLazyPseudoStyle->AddSizeOfIncludingThis(aSizes, aCVsSize);
+    }
   }
 
 private:
   nsPresContext* mPresContext;
   ServoComputedData mSource;
 
   // A linked-list cache of inheriting anon boxes inheriting from this style _if
   // the style isn't an inheriting anon-box_.
--- a/layout/xul/moz.build
+++ b/layout/xul/moz.build
@@ -89,16 +89,18 @@ if CONFIG['MOZ_XUL']:
         'nsXULPopupManager.cpp',
         'PopupBoxObject.cpp',
         'ScrollBoxObject.cpp',
     ]
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['tree', 'grid']
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../base',
     '../generic',
     '../painting',
     '../style',
     '/dom/base',
     '/dom/xul',
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -47,16 +47,22 @@
 
 #include "nsContentUtils.h"
 
 #include "mozilla/BasicEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Maybe.h"
 #include "SVGImageContext.h"
 #include "Units.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+
+#if defined(XP_WIN)
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
 
 #define ONLOAD_CALLED_TOO_EARLY 1
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
@@ -336,42 +342,63 @@ nsImageBoxFrame::BuildDisplayList(nsDisp
   list.AppendNewToTop(
     new (aBuilder) nsDisplayXULImage(aBuilder, this));
 
   CreateOwnLayerIfNeeded(aBuilder, &list);
 
   aLists.Content()->AppendToTop(&list);
 }
 
-DrawResult
-nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
-                            const nsRect& aDirtyRect, nsPoint aPt,
-                            uint32_t aFlags)
+already_AddRefed<imgIContainer>
+nsImageBoxFrame::GetImageContainerForPainting(const nsPoint& aPt,
+                                              DrawResult& aDrawResult,
+                                              Maybe<nsPoint>& aAnchorPoint,
+                                              nsRect& aDest)
 {
   if (!mImageRequest) {
     // This probably means we're drawn by a native theme.
-    return DrawResult::SUCCESS;
+    aDrawResult = DrawResult::SUCCESS;
+    return nullptr;
   }
 
   // Don't draw if the image's size isn't available.
   uint32_t imgStatus;
   if (!NS_SUCCEEDED(mImageRequest->GetImageStatus(&imgStatus)) ||
       !(imgStatus & imgIRequest::STATUS_SIZE_AVAILABLE)) {
-    return DrawResult::NOT_READY;
+    aDrawResult = DrawResult::NOT_READY;
+    return nullptr;
   }
 
   nsCOMPtr<imgIContainer> imgCon;
   mImageRequest->GetImage(getter_AddRefs(imgCon));
 
   if (!imgCon) {
-    return DrawResult::NOT_READY;
+    aDrawResult = DrawResult::NOT_READY;
+    return nullptr;
   }
 
+  aDest = GetDestRect(aPt, aAnchorPoint);
+  aDrawResult = DrawResult::SUCCESS;
+  return imgCon.forget();
+}
+
+DrawResult
+nsImageBoxFrame::PaintImage(gfxContext& aRenderingContext,
+                            const nsRect& aDirtyRect, nsPoint aPt,
+                            uint32_t aFlags)
+{
+  DrawResult result;
   Maybe<nsPoint> anchorPoint;
-  nsRect dest = GetDestRect(aPt, anchorPoint);
+  nsRect dest;
+  nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result,
+                                                                anchorPoint,
+                                                                dest);
+  if (!imgCon) {
+    return result;
+  }
 
   // don't draw if the image is not dirty
   // XXX(seth): Can this actually happen anymore?
   nsRect dirty;
   if (!dirty.IntersectRect(aDirtyRect, dest)) {
     return DrawResult::TEMPORARY_ERROR;
   }
 
@@ -384,16 +411,64 @@ nsImageBoxFrame::PaintImage(gfxContext& 
            PresContext(), imgCon,
            nsLayoutUtils::GetSamplingFilterForFrame(this),
            dest, dirty,
            svgContext, aFlags,
            anchorPoint.ptrOr(nullptr),
            hasSubRect ? &mSubRect : nullptr);
 }
 
+DrawResult
+nsImageBoxFrame::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                         const StackingContextHelper& aSc,
+                                         mozilla::layers::WebRenderLayerManager* aManager,
+                                         nsDisplayItem* aItem,
+                                         nsPoint aPt,
+                                         uint32_t aFlags)
+{
+  DrawResult result;
+  Maybe<nsPoint> anchorPoint;
+  nsRect dest;
+  nsCOMPtr<imgIContainer> imgCon = GetImageContainerForPainting(aPt, result,
+                                                                anchorPoint,
+                                                                dest);
+  if (!imgCon) {
+    return result;
+  }
+
+  uint32_t containerFlags = imgIContainer::FLAG_NONE;
+  if (aFlags & nsImageRenderer::FLAG_SYNC_DECODE_IMAGES) {
+    containerFlags |= imgIContainer::FLAG_SYNC_DECODE;
+  }
+  RefPtr<layers::ImageContainer> container =
+    imgCon->GetImageContainer(aManager, containerFlags);
+  if (!container) {
+    NS_WARNING("Failed to get image container");
+    return DrawResult::NOT_READY;
+  }
+
+  gfx::IntSize size;
+  Maybe<wr::ImageKey> key = aManager->CreateImageKey(aItem, container, aBuilder, aSc, size);
+  if (key.isNothing()) {
+    return DrawResult::BAD_IMAGE;
+  }
+  const int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
+  LayoutDeviceRect fillRect = LayoutDeviceRect::FromAppUnits(dest,
+                                                             appUnitsPerDevPixel);
+  wr::LayoutRect fill = aSc.ToRelativeLayoutRect(fillRect);
+
+  LayoutDeviceSize gapSize(0, 0);
+  SamplingFilter sampleFilter = nsLayoutUtils::GetSamplingFilterForFrame(aItem->Frame());
+  aBuilder.PushImage(fill, fill,
+                     wr::ToLayoutSize(fillRect.Size()), wr::ToLayoutSize(gapSize),
+                     wr::ToImageRendering(sampleFilter), key.value());
+
+  return DrawResult::SUCCESS;
+}
+
 nsRect
 nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint)
 {
   nsCOMPtr<imgIContainer> imgCon;
   mImageRequest->GetImage(getter_AddRefs(imgCon));
   MOZ_ASSERT(imgCon);
 
   nsRect clientRect;
@@ -448,16 +523,65 @@ void nsDisplayXULImage::Paint(nsDisplayL
     flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
 
   DrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
     PaintImage(*aCtx, mVisibleRect, ToReferenceFrame(), flags);
 
   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
+LayerState
+nsDisplayXULImage::GetLayerState(nsDisplayListBuilder* aBuilder,
+                                 LayerManager* aManager,
+                                 const ContainerLayerParameters& aParameters)
+{
+  if (ShouldUseAdvancedLayer(aManager, gfxPrefs::LayersAllowImageLayers) &&
+      CanOptimizeToImageLayer(aManager, aBuilder)) {
+    return LAYER_ACTIVE;
+  }
+  return LAYER_NONE;
+}
+
+already_AddRefed<Layer>
+nsDisplayXULImage::BuildLayer(nsDisplayListBuilder* aBuilder,
+                           LayerManager* aManager,
+                           const ContainerLayerParameters& aContainerParameters)
+{
+  return BuildDisplayItemLayer(aBuilder, aManager, aContainerParameters);
+}
+
+bool
+nsDisplayXULImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                           const StackingContextHelper& aSc,
+                                           nsTArray<WebRenderParentCommand>& aParentCommands,
+                                           mozilla::layers::WebRenderLayerManager* aManager,
+                                           nsDisplayListBuilder* aDisplayListBuilder)
+{
+  if (aManager->IsLayersFreeTransaction()) {
+    ContainerLayerParameters parameter;
+    if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
+      return false;
+    }
+  }
+
+  uint32_t flags = imgIContainer::FLAG_SYNC_DECODE_IF_FAST;
+  if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
+    flags |= imgIContainer::FLAG_SYNC_DECODE;
+  }
+  if (aDisplayListBuilder->IsPaintingToWindow()) {
+    flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
+  }
+
+  DrawResult result = static_cast<nsImageBoxFrame*>(mFrame)->
+    CreateWebRenderCommands(aBuilder, aSc, aManager, this, ToReferenceFrame(), flags);
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
+  return true;
+}
+
 nsDisplayItemGeometry*
 nsDisplayXULImage::AllocateGeometry(nsDisplayListBuilder* aBuilder)
 {
   return new nsDisplayItemGenericImageGeometry(this, aBuilder);
 }
 
 void
 nsDisplayXULImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
--- a/layout/xul/nsImageBoxFrame.h
+++ b/layout/xul/nsImageBoxFrame.h
@@ -85,20 +85,32 @@ public:
    */
   void UpdateLoadFlags();
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsDisplayListSet& aLists) override;
 
   virtual ~nsImageBoxFrame();
 
+  already_AddRefed<imgIContainer> GetImageContainerForPainting(const nsPoint& aPt,
+                                                               DrawResult& aDrawResult,
+                                                               Maybe<nsPoint>& aAnchorPoint,
+                                                               nsRect& aDest);
+
   DrawResult PaintImage(gfxContext& aRenderingContext,
                         const nsRect& aDirtyRect,
                         nsPoint aPt, uint32_t aFlags);
 
+  DrawResult CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                     const mozilla::layers::StackingContextHelper& aSc,
+                                     mozilla::layers::WebRenderLayerManager* aManager,
+                                     nsDisplayItem* aItem,
+                                     nsPoint aPt,
+                                     uint32_t aFlags);
+
   bool CanOptimizeToImageLayer();
 
   nsRect GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint);
 
 protected:
   explicit nsImageBoxFrame(nsStyleContext* aContext);
 
   virtual void GetImageSize();
@@ -153,12 +165,27 @@ public:
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion* aInvalidRegion) override;
   // Doesn't handle HitTest because nsLeafBoxFrame already creates an
   // event receiver for us
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      gfxContext* aCtx) override;
+
+  virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
+                                   LayerManager* aManager,
+                                   const ContainerLayerParameters& aParameters) override;
+
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) override;
+
+  virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+                                       const StackingContextHelper& aSc,
+                                       nsTArray<WebRenderParentCommand>& aParentCommands,
+                                       mozilla::layers::WebRenderLayerManager* aManager,
+                                       nsDisplayListBuilder* aDisplayListBuilder) override;
+
   NS_DISPLAY_DECL_NAME("XULImage", TYPE_XUL_IMAGE)
 };
 
 #endif /* nsImageBoxFrame_h___ */
--- a/layout/xul/reftest/reftest.list
+++ b/layout/xul/reftest/reftest.list
@@ -1,6 +1,6 @@
 fails-if(Android) == textbox-multiline-noresize.xul textbox-multiline-ref.xul # reference is blank on Android (due to no native theme support?)
 != textbox-multiline-resize.xul textbox-multiline-ref.xul
 == popup-explicit-size.xul popup-explicit-size-ref.xul
-random-if(Android) fuzzy-if(webrender,21-21,10746-10746) == image-size.xul image-size-ref.xul
+random-if(Android) == image-size.xul image-size-ref.xul
 == image-scaling-min-height-1.xul image-scaling-min-height-1-ref.xul
 == textbox-text-transform.xul textbox-text-transform-ref.xul
--- a/taskcluster/ci/test/test-platforms.yml
+++ b/taskcluster/ci/test/test-platforms.yml
@@ -164,46 +164,52 @@ linux64-jsdcov/opt:
 
 ##
 # Windows platforms (matching /windows.*/)
 
 # win32
 windows7-32/debug:
     build-platform: win32/debug
     test-sets:
+        - windows-reftest-gpu
         - windows-tests
+
 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-tests
+        - 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
         - windows-tests
 
 windows7-32-devedition/opt:
     build-platform: win32-devedition-nightly/opt
     test-sets:
         - desktop-screenshot-capture
+        - windows-reftest-gpu
         - windows-tests
 
 # win64
 windows10-64/debug:
     build-platform: win64/debug
     test-sets:
         - windows-tests
 
--- a/taskcluster/ci/test/test-sets.yml
+++ b/taskcluster/ci/test/test-sets.yml
@@ -141,16 +141,19 @@ linux-talos-stylo-disabled:
     - talos-g4-stylo-disabled
     - talos-g5-stylo-disabled
     - talos-other-stylo-disabled
     - talos-svgr-stylo-disabled
     - talos-tp5o-stylo-disabled
     - talos-perf-reftest-stylo-disabled
     - talos-perf-reftest-singletons-stylo-disabled
 
+windows-reftest-gpu:
+    - reftest-gpu
+
 windows-tests:
     - cppunit
     - crashtest
     - firefox-ui-functional-local
     - firefox-ui-functional-remote
     - gtest
     - jittest
     - jsreftest
@@ -162,17 +165,16 @@ windows-tests:
     - mochitest-chrome
     - mochitest-clipboard
     - mochitest-devtools-chrome
     - mochitest-gpu
     - mochitest-jetpack
     - mochitest-media
     - mochitest-webgl
     - reftest
-    - reftest-gpu
     - reftest-no-accel
     - web-platform-tests
     - web-platform-tests-reftests
     - xpcshell
 
 windows-talos:
     - talos-chrome
     - talos-dromaeojs
--- a/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
+++ b/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
@@ -5,8 +5,11 @@ skip-if = asan # Bug 1223277, 1348961
 # layout tests
 [include:../../../../../layout/base/tests/marionette/manifest.ini]
 
 # microformats tests
 [include:../../../../../toolkit/components/microformats/manifest.ini]
 
 # migration tests
 [include:../../../../../browser/components/migration/tests/marionette/manifest.ini]
+
+# xpconnect tests
+[include:../../../../../js/xpconnect/tests/marionette/manifest.ini]