Merge inbound to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 22 Jun 2018 00:50:23 +0300
changeset 423281 27e90ec610a4c8f6b2a73d79cb1a4df38e822e6a
parent 423246 72caac8023414abd6b0290b4dbda831875c845c0 (current diff)
parent 423280 ea5d5eea145b32eea1b0a055f07a53033d3af331 (diff)
child 423300 dbafc571a6a8d2426e179698f9038a1a3379fa8c
child 423314 2a08d68ca8f3bde85b102fd4958182b99bf0886c
push id34170
push userncsoregi@mozilla.com
push dateThu, 21 Jun 2018 21:50:44 +0000
treeherdermozilla-central@27e90ec610a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
27e90ec610a4 / 62.0a1 / 20180621220127 / files
nightly linux64
27e90ec610a4 / 62.0a1 / 20180621220127 / files
nightly mac
27e90ec610a4 / 62.0a1 / 20180621220127 / files
nightly win32
27e90ec610a4 / 62.0a1 / 20180621220127 / files
nightly win64
27e90ec610a4 / 62.0a1 / 20180621220127 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/locales/en-US/chrome/security/security.properties
toolkit/components/clearsitedata/ClearSiteData.cpp
toolkit/components/clearsitedata/ClearSiteData.h
toolkit/forgetaboutsite/ServiceWorkerCleanUp.jsm
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -4696,17 +4696,17 @@ var XULBrowserWindow = {
     if (url) {
       url = Services.textToSubURI.unEscapeURIForUI("UTF-8", url);
 
       // Encode bidirectional formatting characters.
       // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
       url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
                         encodeURIComponent);
 
-      if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
+      if (gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
         url = trimURL(url);
     }
 
     this.overLink = url;
     LinkTargetDisplay.update();
   },
 
   showTooltip(x, y, tooltip, direction, browser) {
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -542,19 +542,17 @@ namespace module_getter {
       return false;
     }
 
     js::SetFunctionNativeReserved(getter, SLOT_ID, idValue);
     js::SetFunctionNativeReserved(setter, SLOT_ID, idValue);
 
     js::SetFunctionNativeReserved(getter, SLOT_URI, uri);
 
-    return JS_DefinePropertyById(aCx, aTarget, id,
-                                 JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
-                                 JS_DATA_TO_FUNC_PTR(JSNative, setter.get()),
+    return JS_DefinePropertyById(aCx, aTarget, id, getter, setter,
                                  JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE);
   }
 } // namespace module_getter
 
 /* static */ void
 ChromeUtils::DefineModuleGetter(const GlobalObject& global,
                                 JS::Handle<JSObject*> target,
                                 const nsAString& id,
--- a/dom/base/FragmentOrElement.h
+++ b/dom/base/FragmentOrElement.h
@@ -184,17 +184,17 @@ public:
      * SMIL Overridde style rules (for SMIL animation of CSS properties)
      * @see Element::GetSMILOverrideStyle
      */
     RefPtr<nsDOMCSSAttributeDeclaration> mSMILOverrideStyle;
 
     /**
      * Holds any SMIL override style declaration for this element.
      */
-    RefPtr<mozilla::DeclarationBlock> mSMILOverrideStyleDeclaration;
+    RefPtr<DeclarationBlock> mSMILOverrideStyleDeclaration;
 
     /**
     * The controllers of the XUL Element.
     */
     nsCOMPtr<nsIControllers> mControllers;
 
     /**
      * An object implementing the .labels property for this element.
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -181,17 +181,17 @@ nsNameSpaceManager::GetNameSpaceID(nsAto
 nsresult
 NS_NewElement(Element** aResult,
               already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
               FromParser aFromParser,
               const nsAString* aIs)
 {
   RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
   int32_t ns = ni->NamespaceID();
-  RefPtr<nsAtom> isAtom = aIs ? NS_Atomize(*aIs) : nullptr;
+  RefPtr<nsAtom> isAtom = aIs ? NS_AtomizeMainThread(*aIs) : nullptr;
   if (ns == kNameSpaceID_XHTML) {
     return NS_NewHTMLElement(aResult, ni.forget(), aFromParser, isAtom);
   }
 #ifdef MOZ_XUL
   if (ns == kNameSpaceID_XUL) {
     return NS_NewXULElement(aResult, ni.forget(), aFromParser, isAtom);
   }
 #endif
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -1969,18 +1969,17 @@ Console::PopulateConsoleNotificationInTh
         return false;
       }
 
       js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
       js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
                                     JS::PrivateValue(aData->mStack.get()));
 
       if (NS_WARN_IF(!JS_DefineProperty(aCx, eventObj, "stacktrace",
-                                        JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()),
-                                        nullptr,
+                                        funObj, nullptr,
                                         JSPROP_ENUMERATE |
                                         JSPROP_GETTER | JSPROP_SETTER))) {
         return false;
       }
     }
   }
 
   return true;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3223,16 +3223,19 @@ TabChild::ReinitRendering()
     return;
   }
 
   mLayersConnected = Some(true);
   ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
   gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
 
   InitAPZState();
+  RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+  MOZ_ASSERT(lm);
+  lm->SetLayerObserverEpoch(mLayerObserverEpoch);
 
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
 
 void
 TabChild::ReinitRenderingForDeviceReset()
 {
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -89,10 +89,12 @@ BlockScriptWithWrongMimeType=Script from “%1$S” was blocked because of a disallowed MIME type.
 BlockTopLevelDataURINavigation=Navigation to toplevel data: URI not allowed (Blocked loading of: “%1$S”)
 BlockSubresourceRedirectToData=Redirecting to insecure data: URI not allowed (Blocked loading of: “%1$S”)
 
 BlockSubresourceFTP=Loading FTP subresource within http(s) page not allowed (Blocked loading of: “%1$S”)
 
 # LOCALIZATION NOTE (BrowserUpgradeInsecureDisplayRequest):
 # %1$S is the browser name "brandShortName"; %2$S is the URL of the upgraded request; %1$S is the upgraded scheme.
 BrowserUpgradeInsecureDisplayRequest = %1$S is upgrading an insecure display request ‘%2$S’ to use ‘%3$S’
-RunningClearSiteDataValue=Clear-Site-Data header forces the clean up of “%S” data.
+# LOCALIZATION NOTE (RunningClearSiteDataValue):
+# %S is the URI of the resource whose data was cleaned up
+RunningClearSiteDataValue=Clear-Site-Data header forced the clean up of “%S” data.
 UnknownClearSiteDataValue=Clear-Site-Data header found. Unknown value “%S”.
--- a/dom/webidl/CSSStyleDeclaration.webidl
+++ b/dom/webidl/CSSStyleDeclaration.webidl
@@ -2,16 +2,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * http://dev.w3.org/csswg/cssom/
  */
 
+ // Because of getComputedStyle, many CSSStyleDeclaration objects can be
+ // short-living.
+[ProbablyShortLivingWrapper]
 interface CSSStyleDeclaration {
   [CEReactions, SetterNeedsSubjectPrincipal=NonSystem, SetterThrows]
   attribute DOMString cssText;
 
   readonly attribute unsigned long length;
   getter DOMString item(unsigned long index);
 
   [Throws, ChromeOnly]
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -356,19 +356,17 @@ nsXBLProtoImplField::InstallAccessors(JS
 
   // Now, re-enter the class object's scope, wrap the getters/setters, and define
   // them there.
   JSAutoRealm ar2(aCx, aTargetClassObject);
   if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id,
-                               JS_DATA_TO_FUNC_PTR(JSNative, get.get()),
-                               JS_DATA_TO_FUNC_PTR(JSNative, set.get()),
+  if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, get, set,
                                AccessorAttributes())) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -146,18 +146,17 @@ nsXBLProtoImplProperty::InstallMember(JS
       if (!(setter = JS::CloneFunctionObject(aCx, setter)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsDependentString name(mName);
     if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
                                static_cast<const char16_t*>(mName),
                                name.Length(),
-                               JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
-                               JS_DATA_TO_FUNC_PTR(JSNative, setter.get()),
+                               getter, setter,
                                mJSAttributes))
       return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsString& aClassStr,
--- a/dom/xslt/xpath/txExprLexer.cpp
+++ b/dom/xslt/xpath/txExprLexer.cpp
@@ -12,17 +12,18 @@
 #include "nsString.h"
 #include "nsError.h"
 #include "txXMLUtils.h"
 
 /**
  * Creates a new ExprLexer
  */
 txExprLexer::txExprLexer()
-  : mCurrentItem(nullptr),
+  : mPosition(nullptr),
+    mCurrentItem(nullptr),
     mFirstItem(nullptr),
     mLastItem(nullptr),
     mTokenCount(0)
 {
 }
 
 /**
  * Destroys this instance of an txExprLexer
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -51,16 +51,17 @@ txLoadedDocumentsHash::~txLoadedDocument
        }
     }
 }
 
 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
                                    bool aDisableLoads)
     : mOutputHandler(nullptr),
       mResultHandler(nullptr),
+      mOutputHandlerFactory(nullptr),
       mStylesheet(aStylesheet),
       mNextInstruction(nullptr),
       mLocalVariables(nullptr),
       mRecursionDepth(0),
       mEvalContext(nullptr),
       mInitialEvalContext(nullptr),
       mGlobalParams(nullptr),
       mKeyHash(aStylesheet->getKeyMap()),
--- a/dom/xslt/xslt/txStylesheetCompiler.cpp
+++ b/dom/xslt/xslt/txStylesheetCompiler.cpp
@@ -518,19 +518,21 @@ txStylesheetCompiler::maybeDoneCompiling
 txStylesheetCompilerState::txStylesheetCompilerState(txACompileObserver* aObserver)
     : mHandlerTable(nullptr),
       mSorter(nullptr),
       mDOE(false),
       mSearchingForFallback(false),
       mDisAllowed(0),
       mObserver(aObserver),
       mEmbedStatus(eNoEmbed),
+      mIsTopCompiler(false),
       mDoneWithThisStylesheet(false),
       mNextInstrPtr(nullptr),
-      mToplevelIterator(nullptr)
+      mToplevelIterator(nullptr),
+      mReferrerPolicy(mozilla::net::RP_Unset)
 {
     // Embedded stylesheets have another handler, which is set in
     // txStylesheetCompiler::init if the baseURI has a fragment identifier.
     mHandlerTable = gTxRootHandler;
 
 }
 
 nsresult
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -677,31 +677,31 @@ WebRenderBridgeParent::RecvSetDisplayLis
 
     mApi->SendTransaction(txn);
 
     if (!gfxPrefs::WebRenderAsyncSceneBuild()) {
       // With async-scene-build enabled, we will trigger this after the scene
       // build is done, so we don't need to do it here.
       ScheduleGenerateFrame();
     }
-
-    if (ShouldParentObserveEpoch()) {
-      mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
-    }
   }
 
   HoldPendingTransactionId(wrEpoch, aTransactionId, aTxnStartTime, aFwdTime);
 
   if (mIdNamespace != aIdNamespace) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     TimeStamp now = TimeStamp::Now();
     mCompositorBridge->DidComposite(GetLayersId(), now, now);
   }
 
+  if (ShouldParentObserveEpoch()) {
+    mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
+  }
+
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             const ScrollUpdatesMap& aUpdates,
@@ -766,16 +766,20 @@ WebRenderBridgeParent::RecvEmptyTransact
 
   if (scheduleComposite) {
     ScheduleGenerateFrame();
   } else if (sendDidComposite) {
     TimeStamp now = TimeStamp::Now();
     mCompositorBridge->DidComposite(GetLayersId(), now, now);
   }
 
+  if (ShouldParentObserveEpoch()) {
+    mCompositorBridge->ObserveLayerUpdate(GetLayersId(), GetChildLayerObserverEpoch(), true);
+  }
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   UpdateAPZFocusState(aFocusTarget);
   return IPC_OK();
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -1022,17 +1022,17 @@ AddLengthProperty(JSContext* cx, HandleA
      * as accesses to 'length' will use the elements header.
      */
 
     RootedId lengthId(cx, NameToId(cx->names().length));
     MOZ_ASSERT(!obj->lookup(cx, lengthId));
 
     return NativeObject::addAccessorProperty(cx, obj, lengthId,
                                              array_length_getter, array_length_setter,
-                                             JSPROP_PERMANENT | JSPROP_SHADOWABLE);
+                                             JSPROP_PERMANENT);
 }
 
 static bool
 IsArrayConstructor(const JSObject* obj)
 {
     // This must only return true if v is *the* Array constructor for the
     // current compartment; we rely on the fact that any other Array
     // constructor would be represented as a wrapper.
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -6158,18 +6158,17 @@ StructType::DefineInternal(JSContext* cx
       if (!setter)
         return false;
       SetFunctionNativeReserved(setter, StructType::SLOT_FIELDNAME,
                                 StringValue(JS_FORGET_STRING_FLATNESS(name)));
       RootedObject setterObj(cx, JS_GetFunctionObject(setter));
 
       if (!JS_DefineUCProperty(cx, prototype,
              nameChars.twoByteChars(), name->length(),
-             JS_DATA_TO_FUNC_PTR(JSNative, getterObj.get()),
-             JS_DATA_TO_FUNC_PTR(JSNative, setterObj.get()),
+             getterObj, setterObj,
              JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER))
       {
         return false;
       }
 
       size_t fieldSize = CType::GetSize(fieldType);
       size_t fieldAlign = CType::GetAlignment(fieldType);
       size_t fieldOffset = Align(structSize, fieldAlign);
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -276,18 +276,18 @@ function customSection(name, ...body) {
     return { name: userDefinedId, body: [...string(name), ...body] };
 }
 
 const v2vSig = {args:[], ret:VoidCode};
 const v2vSigSection = sigSection([v2vSig]);
 const i2vSig = {args:[I32Code], ret:VoidCode};
 const v2vBody = funcBody({locals:[], body:[]});
 
-assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ])), CompileError, /too many signatures/);
-assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ])), CompileError, /expected function form/);
+assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ])), CompileError, /too many types/);
+assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ])), CompileError, /expected type form/);
 assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, FuncCode, ...U32MAX_LEB], } ])), CompileError, /too many arguments in signature/);
 
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1]}])), CompileError);
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1, 1, 0]}])), CompileError);
 
 wasmEval(moduleWithSections([sigSection([])]));
 wasmEval(moduleWithSections([v2vSigSection]));
 wasmEval(moduleWithSections([sigSection([i2vSig])]));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/structs.js
@@ -0,0 +1,162 @@
+if (!wasmGcEnabled()) {
+    assertErrorMessage(() => wasmEvalText(`(module (type $s (struct)))`),
+                       WebAssembly.CompileError, /Structure types not enabled/);
+    quit();
+}
+
+var bin = wasmTextToBinary(
+    `(module
+
+      (table 2 anyfunc)
+      (elem (i32.const 0) $doit $doitagain)
+
+      ;; Type array has a mix of types
+
+      (type $f1 (func (param i32) (result i32)))
+
+      (type $point (struct
+                    (field $point_x i32)
+                    (field $point_y i32)))
+
+      (type $f2 (func (param f64) (result f64)))
+
+      (type $int_node (struct
+                       (field $intbox_val (mut i32))
+                       (field $intbox_next (mut anyref))))
+
+      ;; Test all the types.
+
+      (type $omni (struct
+                   (field $omni_i32 i32)
+                   (field $omni_i32m (mut i32))
+                   (field $omni_i64 i64)
+                   (field $omni_i64m (mut i64))
+                   (field $omni_f32 f32)
+                   (field $omni_f32m (mut f32))
+                   (field $omni_f64 f64)
+                   (field $omni_f64m (mut f64))
+                   (field $omni_anyref anyref)
+                   (field $omni_anyrefm (mut anyref))))
+
+      ;; Various ways to reference a type in the middle of the
+      ;; type array, make sure we get the right one
+
+      (func $x1 (import "m" "x1") (type $f1))
+      (func $x2 (import "m" "x2") (type $f2))
+
+      (func (export "hello") (param f64) (param i32) (result f64)
+       (call_indirect $f2 (get_local 0) (get_local 1)))
+
+      (func $doit (param f64) (result f64)
+       (f64.sqrt (get_local 0)))
+
+      (func $doitagain (param f64) (result f64)
+       (f64.mul (get_local 0) (get_local 0)))
+
+      (func (export "x1") (param i32) (result i32)
+       (call $x1 (get_local 0)))
+
+      (func (export "x2") (param f64) (result f64)
+       (call $x2 (get_local 0)))
+     )`)
+
+var mod = new WebAssembly.Module(bin);
+var ins = new WebAssembly.Instance(mod, {m:{x1(x){ return x*3 }, x2(x){ return Math.PI }}}).exports;
+
+assertEq(ins.hello(4.0, 0), 2.0)
+assertEq(ins.hello(4.0, 1), 16.0)
+
+assertEq(ins.x1(12), 36)
+assertEq(ins.x2(8), Math.PI)
+
+// Crude but at least checks that we have *something*.
+
+var txt = wasmBinaryToText(bin);
+var re = /\(type\s+\$[a-z0-9]+\s+\(struct/gm;
+assertEq(Array.isArray(re.exec(txt)), true);
+assertEq(Array.isArray(re.exec(txt)), true);
+assertEq(Array.isArray(re.exec(txt)), true);
+assertEq(Array.isArray(re.exec(txt)), false);
+
+// The field name is optional, so this should work.
+
+wasmEvalText(`
+(module
+ (type $s (struct (field i32))))
+`)
+
+// Empty structs are OK.
+
+wasmEvalText(`
+(module
+ (type $s (struct)))
+`)
+
+// Bogus type definition syntax.
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s))
+`),
+SyntaxError, /parsing wasm text/);
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s (field $x i32)))
+`),
+SyntaxError, /bad type definition/);
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s (struct (field $x i31))))
+`),
+SyntaxError, /parsing wasm text/);
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s (struct (fjeld $x i32))))
+`),
+SyntaxError, /parsing wasm text/);
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s (struct abracadabra)))
+`),
+SyntaxError, /parsing wasm text/);
+
+// Function should not reference struct type: syntactic test
+
+assertErrorMessage(() => wasmEvalText(`
+(module
+ (type $s (struct))
+ (type $f (func (param i32) (result i32)))
+ (func (type 0) (param i32) (result i32) (unreachable)))
+`),
+WebAssembly.CompileError, /signature index references non-signature/);
+
+// Function should not reference struct type: binary test
+
+var bad = new Uint8Array([0x00, 0x61, 0x73, 0x6d,
+                          0x01, 0x00, 0x00, 0x00,
+
+                          0x01,                   // Type section
+                          0x03,                   // Section size
+                          0x01,                   // One type
+                          0x50,                   // Struct
+                          0x00,                   // Zero fields
+
+                          0x03,                   // Function section
+                          0x02,                   // Section size
+                          0x01,                   // One function
+                          0x00,                   // Type of function
+
+                          0x0a,                   // Code section
+                          0x05,                   // Section size
+                          0x01,                   // One body
+                          0x03,                   // Body size
+                          0x00,                   // Zero locals
+                          0x00,                   // UNREACHABLE
+                          0x0b]);                 // END
+
+assertErrorMessage(() => new WebAssembly.Module(bad),
+                   WebAssembly.CompileError, /signature index references non-signature/);
--- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
+++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
@@ -29,23 +29,21 @@ BEGIN_TEST(testDefineGetterSetterNonEnum
 
     JSFunction* funSet = JS_NewFunction(cx, NativeGetterSetter, 1, 0, "set");
     CHECK(funSet);
     JS::RootedObject funSetObj(cx, JS_GetFunctionObject(funSet));
     JS::RootedValue vset(cx, JS::ObjectValue(*funSetObj));
 
     JS::RootedObject vObject(cx, vobj.toObjectOrNull());
     CHECK(JS_DefineProperty(cx, vObject, PROPERTY_NAME,
-                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funGetObj),
-                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funSetObj),
+                            funGetObj, funSetObj,
                             JSPROP_GETTER | JSPROP_SETTER | JSPROP_ENUMERATE));
 
     CHECK(JS_DefineProperty(cx, vObject, PROPERTY_NAME,
-                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funGetObj),
-                            JS_DATA_TO_FUNC_PTR(JSNative, (JSObject*) funSetObj),
+                            funGetObj, funSetObj,
                             JSPROP_GETTER | JSPROP_SETTER | JSPROP_PERMANENT));
 
     JS::Rooted<JS::PropertyDescriptor> desc(cx);
     CHECK(JS_GetOwnPropertyDescriptor(cx, vObject, PROPERTY_NAME, &desc));
     CHECK(desc.object());
     CHECK(desc.hasGetterObject());
     CHECK(desc.hasSetterObject());
     CHECK(!desc.configurable());
--- a/js/src/jsapi-tests/testSetProperty.cpp
+++ b/js/src/jsapi-tests/testSetProperty.cpp
@@ -2,71 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi-tests/tests.h"
 
-BEGIN_TEST(testSetProperty_NativeGetterStubSetter)
-{
-    JS::RootedObject obj(cx, JS_NewPlainObject(cx));
-    CHECK(obj);
-
-    CHECK(JS_DefineProperty(cx, global, "globalProp", obj, JSPROP_ENUMERATE));
-
-    CHECK(JS_DefineProperty(cx, obj, "prop",
-                            JS_PROPERTYOP_GETTER(NativeGet), nullptr,
-                            JSPROP_PROPOP_ACCESSORS));
-
-    EXEC("'use strict';                                     \n"
-         "var error, passed = false;                        \n"
-         "try                                               \n"
-         "{                                                 \n"
-         "  this.globalProp.prop = 42;                      \n"
-         "  throw new Error('setting property succeeded!'); \n"
-         "}                                                 \n"
-         "catch (e)                                         \n"
-         "{                                                 \n"
-         "  error = e;                                      \n"
-         "  if (e instanceof TypeError)                     \n"
-         "    passed = true;                                \n"
-         "}                                                 \n"
-         "                                                  \n"
-         "if (!passed)                                      \n"
-         "  throw error;                                    \n");
-
-    EXEC("var error, passed = false;                        \n"
-         "try                                               \n"
-         "{                                                 \n"
-         "  this.globalProp.prop = 42;                      \n"
-         "  if (this.globalProp.prop === 17)                \n"
-         "    passed = true;                                \n"
-         "  else                                            \n"
-         "    throw new Error('bad value after set!');      \n"
-         "}                                                 \n"
-         "catch (e)                                         \n"
-         "{                                                 \n"
-         "  error = e;                                      \n"
-         "}                                                 \n"
-         "                                                  \n"
-         "if (!passed)                                      \n"
-         "  throw error;                                    \n");
-
-    return true;
-}
-static bool
-NativeGet(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp)
-{
-    vp.setInt32(17);
-    return true;
-}
-END_TEST(testSetProperty_NativeGetterStubSetter)
-
 BEGIN_TEST(testSetProperty_InheritedGlobalSetter)
 {
     // This is a JSAPI test because jsapi-test globals do not have a resolve
     // hook and therefore can use the property cache in some cases where the
     // shell can't.
     MOZ_RELEASE_ASSERT(!JS_GetClass(global)->getResolve());
 
     CHECK(JS_DefineProperty(cx, global, "HOTLOOP", 8, 0));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2094,90 +2094,83 @@ JS_DefinePropertyById(JSContext* cx, Han
 {
     ObjectOpResult result;
     return DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
            result.checkStrict(cx, obj, id);
 }
 
 static bool
 DefineAccessorPropertyById(JSContext* cx, HandleObject obj, HandleId id,
-                           const JSNativeWrapper& get, const JSNativeWrapper& set,
-                           unsigned attrs)
-{
-    JSGetterOp getter = JS_CAST_NATIVE_TO(get.op, JSGetterOp);
-    JSSetterOp setter = JS_CAST_NATIVE_TO(set.op, JSSetterOp);
+                           HandleObject getter, HandleObject setter, unsigned attrs)
+{
+    MOZ_ASSERT_IF(getter, attrs & JSPROP_GETTER);
+    MOZ_ASSERT_IF(setter, attrs & JSPROP_SETTER);
 
     // JSPROP_READONLY has no meaning when accessors are involved. Ideally we'd
     // throw if this happens, but we've accepted it for long enough that it's
     // not worth trying to make callers change their ways. Just flip it off on
     // its way through the API layer so that we can enforce this internally.
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         attrs &= ~JSPROP_READONLY;
 
-    // When we use DefineProperty, we need full scriptable Function objects rather
-    // than JSNatives. However, we might be pulling this property descriptor off
-    // of something with JSNative property descriptors. If we are, wrap them in
-    // JS Function objects.
-
-    // If !(attrs & JSPROP_PROPOP_ACCESSORS), then getter/setter are both
-    // possibly-null JSNatives (or possibly-null JSFunction* if JSPROP_GETTER or
-    // JSPROP_SETTER is appropriately set).
-    if (!(attrs & JSPROP_PROPOP_ACCESSORS)) {
-        if (getter && !(attrs & JSPROP_GETTER)) {
-            RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Get));
-            if (!atom)
-                return false;
-            JSFunction* getobj = NewNativeFunction(cx, (Native) getter, 0, atom);
-            if (!getobj)
-                return false;
-
-            if (get.info)
-                getobj->setJitInfo(get.info);
-
-            getter = JS_DATA_TO_FUNC_PTR(GetterOp, getobj);
-            attrs |= JSPROP_GETTER;
-        }
-        if (setter && !(attrs & JSPROP_SETTER)) {
-            // Root just the getter, since the setter is not yet a JSObject.
-            AutoRooterGetterSetter getRoot(cx, JSPROP_GETTER, &getter, nullptr);
-            RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Set));
-            if (!atom)
-                return false;
-            JSFunction* setobj = NewNativeFunction(cx, (Native) setter, 1, atom);
-            if (!setobj)
-                return false;
-
-            if (set.info)
-                setobj->setJitInfo(set.info);
-
-            setter = JS_DATA_TO_FUNC_PTR(SetterOp, setobj);
-            attrs |= JSPROP_SETTER;
-        }
-    } else {
-        attrs &= ~JSPROP_PROPOP_ACCESSORS;
-    }
-
-    AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id,
-                          (attrs & JSPROP_GETTER)
-                          ? JS_FUNC_TO_DATA_PTR(JSObject*, getter)
-                          : nullptr,
-                          (attrs & JSPROP_SETTER)
-                          ? JS_FUNC_TO_DATA_PTR(JSObject*, setter)
-                          : nullptr);
+    AssertHeapIsIdle();
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id, getter, setter);
 
     return js::DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
 }
 
 static bool
+DefineAccessorPropertyById(JSContext* cx, HandleObject obj, HandleId id,
+                           const JSNativeWrapper& get, const JSNativeWrapper& set,
+                           unsigned attrs)
+{
+    // Getter/setter are both possibly-null JSNatives. Wrap them in JSFunctions.
+
+    MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
+
+    RootedFunction getter(cx);
+    if (get.op) {
+        RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Get));
+        if (!atom)
+            return false;
+        getter = NewNativeFunction(cx, get.op, 0, atom);
+        if (!getter)
+            return false;
+
+        if (get.info)
+            getter->setJitInfo(get.info);
+
+        attrs |= JSPROP_GETTER;
+    }
+
+    RootedFunction setter(cx);
+    if (set.op) {
+        RootedAtom atom(cx, IdToFunctionName(cx, id, FunctionPrefixKind::Set));
+        if (!atom)
+            return false;
+        setter = NewNativeFunction(cx, set.op, 1, atom);
+        if (!setter)
+            return false;
+
+        if (set.info)
+            setter->setJitInfo(set.info);
+
+        attrs |= JSPROP_SETTER;
+    }
+
+    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
+}
+
+
+static bool
 DefineDataPropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                        unsigned attrs)
 {
-    MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_PROPOP_ACCESSORS)));
+    MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, value);
 
     return js::DefineDataProperty(cx, obj, id, value, attrs);
 }
 
@@ -2206,16 +2199,23 @@ JS_DefinePropertyById(JSContext* cx, Han
                       unsigned attrs)
 {
     return DefineAccessorPropertyById(cx, obj, id,
                                       NativeOpWrapper(getter), NativeOpWrapper(setter),
                                       attrs);
 }
 
 JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleObject getter,
+                      HandleObject setter, unsigned attrs)
+{
+    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
+}
+
+JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, HandleObject valueArg,
                       unsigned attrs)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return DefineDataPropertyById(cx, obj, id, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
@@ -2246,32 +2246,16 @@ JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, HandleObject obj, HandleId id, double valueArg,
                       unsigned attrs)
 {
     Value value = NumberValue(valueArg);
     return DefineDataPropertyById(cx, obj, id, HandleValue::fromMarkedLocation(&value), attrs);
 }
 
 static bool
-DefineAccessorProperty(JSContext* cx, HandleObject obj, const char* name,
-                       const JSNativeWrapper& getter, const JSNativeWrapper& setter,
-                       unsigned attrs)
-{
-    AutoRooterGetterSetter gsRoot(cx, attrs, const_cast<JSNative*>(&getter.op),
-                                  const_cast<JSNative*>(&setter.op));
-
-    JSAtom* atom = Atomize(cx, name, strlen(name));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-
-    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
-}
-
-static bool
 DefineDataProperty(JSContext* cx, HandleObject obj, const char* name, HandleValue value,
                    unsigned attrs)
 {
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
 
@@ -2284,18 +2268,34 @@ JS_DefineProperty(JSContext* cx, HandleO
 {
     return DefineDataProperty(cx, obj, name, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, Native getter, Native setter,
                   unsigned attrs)
 {
-    return DefineAccessorProperty(cx, obj, name, NativeOpWrapper(getter), NativeOpWrapper(setter),
-                                  attrs);
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return DefineAccessorPropertyById(cx, obj, id, NativeOpWrapper(getter),
+                                      NativeOpWrapper(setter), attrs);
+}
+
+JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleObject getter,
+                  HandleObject setter, unsigned attrs)
+{
+    JSAtom* atom = Atomize(cx, name, strlen(name));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+
+    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, HandleObject obj, const char* name, HandleObject valueArg,
                   unsigned attrs)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return DefineDataProperty(cx, obj, name, value, attrs);
@@ -2356,30 +2356,16 @@ JS_DefineUCProperty(JSContext* cx, Handl
         return false;
     RootedId id(cx, AtomToId(atom));
     ObjectOpResult result;
     return DefinePropertyByDescriptor(cx, obj, id, desc, result) &&
            result.checkStrict(cx, obj, id);
 }
 
 static bool
-DefineUCAccessorProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                         Native getter, Native setter, unsigned attrs)
-{
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
-    if (!atom)
-        return false;
-    RootedId id(cx, AtomToId(atom));
-    return DefineAccessorPropertyById(cx, obj, id,
-                                      NativeOpWrapper(getter), NativeOpWrapper(setter),
-                                      attrs);
-}
-
-static bool
 DefineUCDataProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
                      HandleValue value, unsigned attrs)
 {
     JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
     if (!atom)
         return false;
     RootedId id(cx, AtomToId(atom));
     return DefineDataPropertyById(cx, obj, id, value, attrs);
@@ -2389,19 +2375,23 @@ JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
                     HandleValue value, unsigned attrs)
 {
     return DefineUCDataProperty(cx, obj, name, namelen, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
-                    Native getter, Native setter, unsigned attrs)
-{
-    return DefineUCAccessorProperty(cx, obj, name, namelen, getter, setter, attrs);
+                    HandleObject getter, HandleObject setter, unsigned attrs)
+{
+    JSAtom* atom = AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen));
+    if (!atom)
+        return false;
+    RootedId id(cx, AtomToId(atom));
+    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, HandleObject obj, const char16_t* name, size_t namelen,
                     HandleObject valueArg, unsigned attrs)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return DefineUCDataProperty(cx, obj, name, namelen, value, attrs);
@@ -2438,32 +2428,16 @@ JS_DefineUCProperty(JSContext* cx, Handl
                     double valueArg, unsigned attrs)
 {
     Value value = NumberValue(valueArg);
     return DefineUCDataProperty(cx, obj, name, namelen, HandleValue::fromMarkedLocation(&value),
                                 attrs);
 }
 
 static bool
-DefineAccessorElement(JSContext* cx, HandleObject obj, uint32_t index, unsigned attrs,
-                      Native getter, Native setter)
-{
-    assertSameCompartment(cx, obj);
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-    AssertHeapIsIdle();
-    CHECK_REQUEST(cx);
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return DefineAccessorPropertyById(cx, obj, id,
-                                      NativeOpWrapper(getter), NativeOpWrapper(setter),
-                                      attrs);
-}
-
-static bool
 DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                   unsigned attrs)
 {
     assertSameCompartment(cx, obj, value);
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
@@ -2474,20 +2448,23 @@ DefineDataElement(JSContext* cx, HandleO
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                  unsigned attrs)
 {
     return ::DefineDataElement(cx, obj, index, value, attrs);
 }
 
 JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, Native getter, Native setter,
-                 unsigned attrs)
-{
-    return DefineAccessorElement(cx, obj, index, attrs, getter, setter);
+JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject getter,
+                 HandleObject setter, unsigned attrs)
+{
+    RootedId id(cx);
+    if (!IndexToId(cx, index, &id))
+        return false;
+    return DefineAccessorPropertyById(cx, obj, id, getter, setter, attrs);
 }
 
 JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, HandleObject obj, uint32_t index, HandleObject valueArg,
                  unsigned attrs)
 {
     RootedValue value(cx, ObjectValue(*valueArg));
     return ::DefineDataElement(cx, obj, index, value, attrs);
@@ -3076,17 +3053,16 @@ DefineSelfHostedProperty(JSContext* cx, 
     RootedValue getterValue(cx);
     if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), getterNameName, name, 0,
                                              &getterValue))
     {
         return false;
     }
     MOZ_ASSERT(getterValue.isObject() && getterValue.toObject().is<JSFunction>());
     RootedFunction getterFunc(cx, &getterValue.toObject().as<JSFunction>());
-    JSNative getterOp = JS_DATA_TO_FUNC_PTR(JSNative, getterFunc.get());
 
     RootedFunction setterFunc(cx);
     if (setterName) {
         JSAtom* setterNameAtom = Atomize(cx, setterName, strlen(setterName));
         if (!setterNameAtom)
             return false;
         RootedPropertyName setterNameName(cx, setterNameAtom->asPropertyName());
 
@@ -3094,21 +3070,18 @@ DefineSelfHostedProperty(JSContext* cx, 
         if (!GlobalObject::getSelfHostedFunction(cx, cx->global(), setterNameName, name, 0,
                                                  &setterValue))
         {
             return false;
         }
         MOZ_ASSERT(setterValue.isObject() && setterValue.toObject().is<JSFunction>());
         setterFunc = &setterValue.toObject().as<JSFunction>();
     }
-    JSNative setterOp = JS_DATA_TO_FUNC_PTR(JSNative, setterFunc.get());
-
-    return DefineAccessorPropertyById(cx, obj, id,
-                                      NativeOpWrapper(getterOp), NativeOpWrapper(setterOp),
-                                      attrs);
+
+    return DefineAccessorPropertyById(cx, obj, id, getterFunc, setterFunc, attrs);
 }
 
 JS_PUBLIC_API(JSObject*)
 JS_DefineObject(JSContext* cx, HandleObject obj, const char* name, const JSClass* jsclasp,
                 unsigned attrs)
 {
     AssertHeapIsIdle();
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -471,19 +471,17 @@ static const uint8_t JSPROP_ENUMERATE = 
 
 /* not settable: assignment is no-op.  This flag is only valid when neither
    JSPROP_GETTER nor JSPROP_SETTER is set. */
 static const uint8_t JSPROP_READONLY =         0x02;
 
 /* property cannot be deleted */
 static const uint8_t JSPROP_PERMANENT =        0x04;
 
-/* Passed to JS_Define(UC)Property* and JS_DefineElement if getters/setters are
-   JSGetterOp/JSSetterOp */
-static const uint8_t JSPROP_PROPOP_ACCESSORS = 0x08;
+/* (0x08 is unused) */
 
 /* property holds getter function */
 static const uint8_t JSPROP_GETTER =           0x10;
 
 /* property holds setter function */
 static const uint8_t JSPROP_SETTER =           0x20;
 
 /* internal JS engine use only */
@@ -1457,60 +1455,39 @@ private:
         MOZ_ASSERT(!accessors.getter.selfHosted.unused);
         MOZ_ASSERT(!accessors.setter.selfHosted.unused);
     }
 };
 
 namespace JS {
 namespace detail {
 
-/* NEVER DEFINED, DON'T USE.  For use by JS_CAST_NATIVE_TO only. */
-inline int CheckIsNative(JSNative native);
-
 /* NEVER DEFINED, DON'T USE.  For use by JS_CAST_STRING_TO only. */
 template<size_t N>
 inline int
 CheckIsCharacterLiteral(const char (&arr)[N]);
 
 /* NEVER DEFINED, DON'T USE.  For use by JS_CAST_INT32_TO only. */
 inline int CheckIsInt32(int32_t value);
 
-/* NEVER DEFINED, DON'T USE.  For use by JS_PROPERTYOP_GETTER only. */
-inline int CheckIsGetterOp(JSGetterOp op);
-
-/* NEVER DEFINED, DON'T USE.  For use by JS_PROPERTYOP_SETTER only. */
-inline int CheckIsSetterOp(JSSetterOp op);
-
 } // namespace detail
 } // namespace JS
 
-#define JS_CAST_NATIVE_TO(v, To) \
-  (static_cast<void>(sizeof(JS::detail::CheckIsNative(v))), \
-   reinterpret_cast<To>(v))
-
 #define JS_CAST_STRING_TO(s, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsCharacterLiteral(s))), \
    reinterpret_cast<To>(s))
 
 #define JS_CAST_INT32_TO(s, To) \
   (static_cast<void>(sizeof(JS::detail::CheckIsInt32(s))), \
    reinterpret_cast<To>(s))
 
 #define JS_CHECK_ACCESSOR_FLAGS(flags) \
   (static_cast<mozilla::EnableIf<((flags) & ~(JSPROP_ENUMERATE | JSPROP_PERMANENT)) == 0>::Type>(0), \
    (flags))
 
-#define JS_PROPERTYOP_GETTER(v) \
-  (static_cast<void>(sizeof(JS::detail::CheckIsGetterOp(v))), \
-   reinterpret_cast<JSNative>(v))
-
-#define JS_PROPERTYOP_SETTER(v) \
-  (static_cast<void>(sizeof(JS::detail::CheckIsSetterOp(v))), \
-   reinterpret_cast<JSNative>(v))
-
 #define JS_PS_ACCESSOR_SPEC(name, getter, setter, flags, extraFlags) \
     { name, uint8_t(JS_CHECK_ACCESSOR_FLAGS(flags) | extraFlags), \
       { {  getter, setter  } } }
 #define JS_PS_VALUE_SPEC(name, value, flags) \
     { name, uint8_t(flags | JSPROP_INTERNAL_USE_BIT), \
       { { value, JSNATIVE_WRAPPER(nullptr) } } }
 
 #define SELFHOSTED_WRAPPER(name) \
@@ -2034,19 +2011,16 @@ class WrappedPtrOperations<JS::PropertyD
     bool hasAny(unsigned bits) const {
         return (desc().attrs & bits) != 0;
     }
 
     bool hasAll(unsigned bits) const {
         return (desc().attrs & bits) == bits;
     }
 
-    // Non-API attributes bit used internally for arguments objects.
-    enum { SHADOWABLE = JSPROP_INTERNAL_USE_BIT };
-
   public:
     // Descriptors with JSGetterOp/JSSetterOp are considered data
     // descriptors. It's complicated.
     bool isAccessorDescriptor() const { return hasAny(JSPROP_GETTER | JSPROP_SETTER); }
     bool isGenericDescriptor() const {
         return (desc().attrs&
                 (JSPROP_GETTER | JSPROP_SETTER | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE)) ==
                (JSPROP_IGNORE_READONLY | JSPROP_IGNORE_VALUE);
@@ -2093,24 +2067,24 @@ class WrappedPtrOperations<JS::PropertyD
 #ifdef DEBUG
         MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE | JSPROP_IGNORE_ENUMERATE |
                                      JSPROP_PERMANENT | JSPROP_IGNORE_PERMANENT |
                                      JSPROP_READONLY | JSPROP_IGNORE_READONLY |
                                      JSPROP_IGNORE_VALUE |
                                      JSPROP_GETTER |
                                      JSPROP_SETTER |
                                      JSPROP_RESOLVING |
-                                     SHADOWABLE)) == 0);
+                                     JSPROP_INTERNAL_USE_BIT)) == 0);
         MOZ_ASSERT(!hasAll(JSPROP_IGNORE_ENUMERATE | JSPROP_ENUMERATE));
         MOZ_ASSERT(!hasAll(JSPROP_IGNORE_PERMANENT | JSPROP_PERMANENT));
         if (isAccessorDescriptor()) {
             MOZ_ASSERT(!has(JSPROP_READONLY));
             MOZ_ASSERT(!has(JSPROP_IGNORE_READONLY));
             MOZ_ASSERT(!has(JSPROP_IGNORE_VALUE));
-            MOZ_ASSERT(!has(SHADOWABLE));
+            MOZ_ASSERT(!has(JSPROP_INTERNAL_USE_BIT));
             MOZ_ASSERT(value().isUndefined());
             MOZ_ASSERT_IF(!has(JSPROP_GETTER), !getter());
             MOZ_ASSERT_IF(!has(JSPROP_SETTER), !setter());
         } else {
             MOZ_ASSERT(!hasAll(JSPROP_IGNORE_READONLY | JSPROP_READONLY));
             MOZ_ASSERT_IF(has(JSPROP_IGNORE_VALUE), value().isUndefined());
         }
 
@@ -2125,17 +2099,17 @@ class WrappedPtrOperations<JS::PropertyD
 #ifdef DEBUG
         assertValid();
         MOZ_ASSERT((attributes() & ~(JSPROP_ENUMERATE |
                                      JSPROP_PERMANENT |
                                      JSPROP_READONLY |
                                      JSPROP_GETTER |
                                      JSPROP_SETTER |
                                      JSPROP_RESOLVING |
-                                     SHADOWABLE)) == 0);
+                                     JSPROP_INTERNAL_USE_BIT)) == 0);
         MOZ_ASSERT_IF(isAccessorDescriptor(), has(JSPROP_GETTER) && has(JSPROP_SETTER));
 #endif
     }
 
     void assertCompleteIfFound() const {
 #ifdef DEBUG
         if (object())
             assertComplete();
@@ -2416,16 +2390,20 @@ extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value,
                       unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JSNative getter,
                       JSNative setter, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
+JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject getter,
+                      JS::HandleObject setter, unsigned attrs);
+
+extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject value,
                       unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefinePropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleString value,
                       unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
@@ -2444,16 +2422,20 @@ extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue value,
                   unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JSNative getter,
                   JSNative setter, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
+JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleObject getter,
+                  JS::HandleObject setter, unsigned attrs);
+
+extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleObject value,
                   unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleString value,
                   unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
@@ -2478,17 +2460,17 @@ JS_DefineUCProperty(JSContext* cx, JS::H
                     JS::Handle<JS::PropertyDescriptor> desc);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::HandleValue value, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
-                    JSNative getter, JSNative setter, unsigned attrs);
+                    JS::HandleObject getter, JS::HandleObject setter, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::HandleObject value, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     JS::HandleString value, unsigned attrs);
@@ -2505,18 +2487,18 @@ extern JS_PUBLIC_API(bool)
 JS_DefineUCProperty(JSContext* cx, JS::HandleObject obj, const char16_t* name, size_t namelen,
                     double value, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleValue value,
                  unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
-JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JSNative getter,
-                 JSNative setter, unsigned attrs);
+JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject getter,
+                 JS::HandleObject setter, unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleObject value,
                  unsigned attrs);
 
 extern JS_PUBLIC_API(bool)
 JS_DefineElement(JSContext* cx, JS::HandleObject obj, uint32_t index, JS::HandleString value,
                  unsigned attrs);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -571,17 +571,17 @@ MappedArgumentsObject::obj_resolve(JSCon
             return true;
 
         if (!DefineArgumentsIterator(cx, argsobj))
             return false;
         *resolvedp = true;
         return true;
     }
 
-    unsigned attrs = JSPROP_SHADOWABLE | JSPROP_RESOLVING;
+    unsigned attrs = JSPROP_RESOLVING;
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
         if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
             return true;
 
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->names().length)) {
         if (argsobj->hasOverriddenLength())
@@ -771,45 +771,48 @@ UnmappedArgumentsObject::obj_resolve(JSC
             return true;
 
         if (!DefineArgumentsIterator(cx, argsobj))
             return false;
         *resolvedp = true;
         return true;
     }
 
-    unsigned attrs = JSPROP_SHADOWABLE;
-    GetterOp getter = UnmappedArgGetter;
-    SetterOp setter = UnmappedArgSetter;
+    if (JSID_IS_ATOM(id, cx->names().callee)) {
+        RootedObject throwTypeError(cx, GlobalObject::getOrCreateThrowTypeError(cx, cx->global()));
+        if (!throwTypeError)
+            return false;
 
+        unsigned attrs = JSPROP_RESOLVING | JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER;
+        if (!NativeDefineAccessorProperty(cx, argsobj, id, throwTypeError, throwTypeError, attrs))
+            return false;
+
+        *resolvedp = true;
+        return true;
+    }
+
+    unsigned attrs = JSPROP_RESOLVING;
     if (JSID_IS_INT(id)) {
         uint32_t arg = uint32_t(JSID_TO_INT(id));
         if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
             return true;
 
         attrs |= JSPROP_ENUMERATE;
     } else if (JSID_IS_ATOM(id, cx->names().length)) {
         if (argsobj->hasOverriddenLength())
             return true;
     } else {
-        if (!JSID_IS_ATOM(id, cx->names().callee))
-            return true;
-
-        JSObject* throwTypeError = GlobalObject::getOrCreateThrowTypeError(cx, cx->global());
-        if (!throwTypeError)
-            return false;
-
-        attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER;
-        getter = CastAsGetterOp(throwTypeError);
-        setter = CastAsSetterOp(throwTypeError);
+        return true;
     }
 
-    attrs |= JSPROP_RESOLVING;
-    if (!NativeDefineAccessorProperty(cx, argsobj, id, getter, setter, attrs))
+    if (!NativeDefineAccessorProperty(cx, argsobj, id, UnmappedArgGetter, UnmappedArgSetter,
+                                      attrs))
+    {
         return false;
+    }
 
     *resolvedp = true;
     return true;
 }
 
 /* static */ bool
 UnmappedArgumentsObject::obj_enumerate(JSContext* cx, HandleObject obj)
 {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -8426,18 +8426,17 @@ DebuggerArguments::create(JSContext* cx,
     for (unsigned i = 0; i < fargc; i++) {
         RootedFunction getobj(cx);
         getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
                                    gc::AllocKind::FUNCTION_EXTENDED);
         if (!getobj)
             return nullptr;
         id = INT_TO_JSID(i);
         if (!getobj ||
-            !NativeDefineAccessorProperty(cx, obj, id,
-                                          JS_DATA_TO_FUNC_PTR(GetterOp, getobj.get()), nullptr,
+            !NativeDefineAccessorProperty(cx, obj, id, getobj, nullptr,
                                           JSPROP_ENUMERATE | JSPROP_GETTER))
         {
             return nullptr;
         }
         getobj->setExtendedSlot(0, Int32Value(i));
     }
 
     return obj;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -4829,40 +4829,34 @@ js::GetInitDataPropAttrs(JSOp op)
     MOZ_CRASH("Unknown data initprop");
 }
 
 bool
 js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
                               HandleObject val)
 {
     MOZ_ASSERT(val->isCallable());
-    GetterOp getter;
-    SetterOp setter;
 
     JSOp op = JSOp(*pc);
 
     unsigned attrs = 0;
     if (!IsHiddenInitOp(op))
         attrs |= JSPROP_ENUMERATE;
 
     if (op == JSOP_INITPROP_GETTER || op == JSOP_INITELEM_GETTER ||
         op == JSOP_INITHIDDENPROP_GETTER || op == JSOP_INITHIDDENELEM_GETTER)
     {
-        getter = CastAsGetterOp(val);
-        setter = nullptr;
         attrs |= JSPROP_GETTER;
-    } else {
-        MOZ_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER ||
-                   op == JSOP_INITHIDDENPROP_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
-        getter = nullptr;
-        setter = CastAsSetterOp(val);
-        attrs |= JSPROP_SETTER;
+        return DefineAccessorProperty(cx, obj, id, val, nullptr, attrs);
     }
 
-    return DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
+    MOZ_ASSERT(op == JSOP_INITPROP_SETTER || op == JSOP_INITELEM_SETTER ||
+               op == JSOP_INITHIDDENPROP_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
+    attrs |= JSPROP_SETTER;
+    return DefineAccessorProperty(cx, obj, id, nullptr, val, attrs);
 }
 
 bool
 js::InitGetterSetterOperation(JSContext* cx, jsbytecode* pc, HandleObject obj,
                               HandlePropertyName name, HandleObject val)
 {
     RootedId id(cx, NameToId(name));
     return InitGetterSetterOperation(cx, pc, obj, id, val);
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -2798,23 +2798,27 @@ js::DefineProperty(JSContext* cx, Handle
     desc.assertValid();
     if (DefinePropertyOp op = obj->getOpsDefineProperty())
         return op(cx, obj, id, desc, result);
     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
 js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
-                           JSGetterOp getter, JSSetterOp setter, unsigned attrs,
+                           HandleObject getter, HandleObject setter, unsigned attrs,
                            ObjectOpResult& result)
 {
-    MOZ_ASSERT(!(attrs & JSPROP_PROPOP_ACCESSORS));
-
     Rooted<PropertyDescriptor> desc(cx);
-    desc.initFields(nullptr, UndefinedHandleValue, attrs, getter, setter);
+
+    {
+        GetterOp getterOp = JS_DATA_TO_FUNC_PTR(GetterOp, getter.get());
+        SetterOp setterOp = JS_DATA_TO_FUNC_PTR(SetterOp, setter.get());
+        desc.initFields(nullptr, UndefinedHandleValue, attrs, getterOp, setterOp);
+    }
+
     if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
         MOZ_ASSERT(!cx->helperThread());
         return op(cx, obj, id, desc, result);
     }
     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
@@ -2826,56 +2830,36 @@ js::DefineDataProperty(JSContext* cx, Ha
     if (DefinePropertyOp op = obj->getOpsDefineProperty()) {
         MOZ_ASSERT(!cx->helperThread());
         return op(cx, obj, id, desc, result);
     }
     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
 }
 
 bool
-js::DefineAccessorProperty(JSContext* cx, HandleObject obj, PropertyName* name,
-                           JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                           ObjectOpResult& result)
-{
-    RootedId id(cx, NameToId(name));
-    return DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result);
-}
-
-bool
 js::DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
                        unsigned attrs, ObjectOpResult& result)
 {
     RootedId id(cx, NameToId(name));
     return DefineDataProperty(cx, obj, id, value, attrs, result);
 }
 
 bool
-js::DefineAccessorElement(JSContext* cx, HandleObject obj, uint32_t index,
-                          JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                          ObjectOpResult& result)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result);
-}
-
-bool
 js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                       unsigned attrs, ObjectOpResult& result)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return DefineDataProperty(cx, obj, id, value, attrs, result);
 }
 
 bool
 js::DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
-                           JSGetterOp getter, JSSetterOp setter, unsigned attrs)
+                           HandleObject getter, HandleObject setter, unsigned attrs)
 {
     ObjectOpResult result;
     if (!DefineAccessorProperty(cx, obj, id, getter, setter, attrs, result))
         return false;
     if (!result) {
         MOZ_ASSERT(!cx->helperThread());
         result.reportError(cx, obj, id);
         return false;
@@ -2894,42 +2878,24 @@ js::DefineDataProperty(JSContext* cx, Ha
         MOZ_ASSERT(!cx->helperThread());
         result.reportError(cx, obj, id);
         return false;
     }
     return true;
 }
 
 bool
-js::DefineAccessorProperty(JSContext* cx, HandleObject obj, PropertyName* name,
-                           JSGetterOp getter, JSSetterOp setter, unsigned attrs)
-{
-    RootedId id(cx, NameToId(name));
-    return DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
-}
-
-bool
 js::DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
                        unsigned attrs)
 {
     RootedId id(cx, NameToId(name));
     return DefineDataProperty(cx, obj, id, value, attrs);
 }
 
 bool
-js::DefineAccessorElement(JSContext* cx, HandleObject obj, uint32_t index,
-                          JSGetterOp getter, JSSetterOp setter, unsigned attrs)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return DefineAccessorProperty(cx, obj, id, getter, setter, attrs);
-}
-
-bool
 js::DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                       unsigned attrs)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return DefineDataProperty(cx, obj, id, value, attrs);
 }
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -711,69 +711,52 @@ GetOwnPropertyDescriptor(JSContext* cx, 
 
 /* ES6 [[DefineOwnProperty]]. Define a property on obj. */
 extern bool
 DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
                Handle<JS::PropertyDescriptor> desc, ObjectOpResult& result);
 
 extern bool
 DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
-                       JSGetterOp getter, JSSetterOp setter, unsigned attrs,
+                       HandleObject getter, HandleObject setter, unsigned attrs,
                        ObjectOpResult& result);
 
 extern bool
 DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                    unsigned attrs, ObjectOpResult& result);
 
 extern bool
-DefineAccessorProperty(JSContext* cx, HandleObject obj, PropertyName* name,
-                       JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                       ObjectOpResult& result);
-
-extern bool
 DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
                    unsigned attrs, ObjectOpResult& result);
 
 extern bool
-DefineAccessorElement(JSContext* cx, HandleObject obj, uint32_t index,
-                      JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                      ObjectOpResult& result);
-
-extern bool
 DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                   unsigned attrs, ObjectOpResult& result);
 
 /*
  * When the 'result' out-param is omitted, the behavior is the same as above, except
  * that any failure results in a TypeError.
  */
 extern bool
 DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<JS::PropertyDescriptor> desc);
 
 extern bool
 DefineAccessorProperty(JSContext* cx, HandleObject obj, HandleId id,
-                       JSGetterOp getter, JSSetterOp setter, unsigned attrs = JSPROP_ENUMERATE);
+                       HandleObject getter, HandleObject setter,
+                       unsigned attrs = JSPROP_ENUMERATE);
 
 extern bool
 DefineDataProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue value,
                    unsigned attrs = JSPROP_ENUMERATE);
 
 extern bool
-DefineAccessorProperty(JSContext* cx, HandleObject obj, PropertyName* name,
-                       JSGetterOp getter, JSSetterOp setter, unsigned attrs = JSPROP_ENUMERATE);
-
-extern bool
 DefineDataProperty(JSContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
                    unsigned attrs = JSPROP_ENUMERATE);
 
 extern bool
-DefineAccessorElement(JSContext* cx, HandleObject obj, uint32_t index,
-                      JSGetterOp getter, JSSetterOp setter, unsigned attrs = JSPROP_ENUMERATE);
-
-extern bool
 DefineDataElement(JSContext* cx, HandleObject obj, uint32_t index, HandleValue value,
                   unsigned attrs = JSPROP_ENUMERATE);
 
 /*
  * ES6 [[Has]]. Set *foundp to true if `id in obj` (that is, if obj has an own
  * or inherited property obj[id]), false otherwise.
  */
 inline bool
@@ -1089,28 +1072,16 @@ extern JSObject*
 CreateThis(JSContext* cx, const js::Class* clasp, js::HandleObject callee);
 
 extern JSObject*
 CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto);
 
 extern JSObject*
 DeepCloneObjectLiteral(JSContext* cx, HandleObject obj, NewObjectKind newKind = GenericObject);
 
-inline JSGetterOp
-CastAsGetterOp(JSObject* object)
-{
-    return JS_DATA_TO_FUNC_PTR(JSGetterOp, object);
-}
-
-inline JSSetterOp
-CastAsSetterOp(JSObject* object)
-{
-    return JS_DATA_TO_FUNC_PTR(JSSetterOp, object);
-}
-
 /* ES6 draft rev 32 (2015 Feb 2) 6.2.4.5 ToPropertyDescriptor(Obj) */
 bool
 ToPropertyDescriptor(JSContext* cx, HandleValue descval, bool checkAccessors,
                      MutableHandle<JS::PropertyDescriptor> desc);
 
 /*
  * Throw a TypeError if desc.getterObject() or setterObject() is not
  * callable. This performs exactly the checks omitted by ToPropertyDescriptor
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1819,49 +1819,71 @@ js::NativeDefineProperty(JSContext* cx, 
     if (!AddOrChangeProperty<IsAddOrChange::AddOrChange>(cx, obj, id, desc))
         return false;
 
     // Step 10.
     return result.succeed();
 }
 
 bool
-js::NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
-                                 GetterOp getter, SetterOp setter, unsigned attrs,
-                                 ObjectOpResult& result)
-{
-    Rooted<PropertyDescriptor> desc(cx);
-    desc.initFields(nullptr, UndefinedHandleValue, attrs, getter, setter);
-    return NativeDefineProperty(cx, obj, id, desc, result);
-}
-
-bool
 js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                              HandleValue value, unsigned attrs, ObjectOpResult& result)
 {
     Rooted<PropertyDescriptor> desc(cx);
     desc.initFields(nullptr, value, attrs, nullptr, nullptr);
     return NativeDefineProperty(cx, obj, id, desc, result);
 }
 
 bool
 js::NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
-                                 JSGetterOp getter, JSSetterOp setter, unsigned attrs)
+                                 GetterOp getter, SetterOp setter, unsigned attrs)
 {
+    Rooted<PropertyDescriptor> desc(cx);
+    desc.initFields(nullptr, UndefinedHandleValue, attrs, getter, setter);
+
     ObjectOpResult result;
-    if (!NativeDefineAccessorProperty(cx, obj, id, getter, setter, attrs, result))
+    if (!NativeDefineProperty(cx, obj, id, desc, result))
         return false;
+
     if (!result) {
         // Off-thread callers should not get here: they must call this
         // function only with known-valid arguments. Populating a new
         // PlainObject with configurable properties is fine.
         MOZ_ASSERT(!cx->helperThread());
         result.reportError(cx, obj, id);
         return false;
     }
+
+    return true;
+}
+
+bool
+js::NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
+                                 HandleObject getter, HandleObject setter, unsigned attrs)
+{
+    Rooted<PropertyDescriptor> desc(cx);
+    {
+        GetterOp getterOp = JS_DATA_TO_FUNC_PTR(GetterOp, getter.get());
+        SetterOp setterOp = JS_DATA_TO_FUNC_PTR(SetterOp, setter.get());
+        desc.initFields(nullptr, UndefinedHandleValue, attrs, getterOp, setterOp);
+    }
+
+    ObjectOpResult result;
+    if (!NativeDefineProperty(cx, obj, id, desc, result))
+        return false;
+
+    if (!result) {
+        // Off-thread callers should not get here: they must call this
+        // function only with known-valid arguments. Populating a new
+        // PlainObject with configurable properties is fine.
+        MOZ_ASSERT(!cx->helperThread());
+        result.reportError(cx, obj, id);
+        return false;
+    }
+
     return true;
 }
 
 bool
 js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                              HandleValue value, unsigned attrs)
 {
     ObjectOpResult result;
@@ -1874,24 +1896,16 @@ js::NativeDefineDataProperty(JSContext* 
         MOZ_ASSERT(!cx->helperThread());
         result.reportError(cx, obj, id);
         return false;
     }
     return true;
 }
 
 bool
-js::NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
-                                 JSGetterOp getter, JSSetterOp setter, unsigned attrs)
-{
-    RootedId id(cx, NameToId(name));
-    return NativeDefineAccessorProperty(cx, obj, id, getter, setter, attrs);
-}
-
-bool
 js::NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
                              HandleValue value, unsigned attrs)
 {
     RootedId id(cx, NameToId(name));
     return NativeDefineDataProperty(cx, obj, id, value, attrs);
 }
 
 static bool
@@ -2682,35 +2696,19 @@ SetExistingProperty(JSContext* cx, Handl
 
         // steps 5.c-f.
         if (receiver.isObject() && pobj == &receiver.toObject()) {
             // Pure optimization for the common case. There's no point performing
             // the lookup in step 5.c again, as our caller just did it for us. The
             // result is |shape|.
 
             // Steps 5.e.i-ii.
-            if (pobj->is<ArrayObject>() && id == NameToId(cx->names().length)) {
-                Rooted<ArrayObject*> arr(cx, &pobj->as<ArrayObject>());
-                return ArraySetLength(cx, arr, id, shape->attributes(), v, result);
-            }
             return NativeSetExistingDataProperty(cx, pobj, shape, v, result);
         }
 
-        // SpiderMonkey special case: assigning to an inherited slotless
-        // property causes the setter to be called, instead of shadowing,
-        // unless the existing property is JSPROP_SHADOWABLE (see bug 552432).
-        if (!shape->isDataProperty() && !shape->hasShadowable()) {
-            // Even weirder sub-special-case: inherited slotless data property
-            // with default setter. Wut.
-            if (shape->hasDefaultSetter())
-                return result.succeed();
-
-            return CallJSSetterOp(cx, shape->setterOp(), obj, id, v, result);
-        }
-
         // Shadow pobj[id] by defining a new data property receiver[id].
         // Delegate everything to SetPropertyByDefining.
         return SetPropertyByDefining(cx, id, v, receiver, result);
     }
 
     // Steps 6-11.
     MOZ_ASSERT(shape->isAccessorDescriptor());
     MOZ_ASSERT_IF(!shape->hasSetterObject(), shape->hasDefaultSetter());
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1524,38 +1524,33 @@ NativeObject::privateWriteBarrierPre(voi
  */
 
 extern bool
 NativeDefineProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                      Handle<JS::PropertyDescriptor> desc,
                      ObjectOpResult& result);
 
 extern bool
-NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
-                             JSGetterOp getter, JSSetterOp setter, unsigned attrs,
-                             ObjectOpResult& result);
-
-extern bool
 NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
                          unsigned attrs, ObjectOpResult& result);
 
 /* If the result out-param is omitted, throw on failure. */
 extern bool
 NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
-                             JSGetterOp getter, JSSetterOp setter, unsigned attrs);
+                             GetterOp getter, SetterOp setter, unsigned attrs);
+
+extern bool
+NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
+                             HandleObject getter, HandleObject setter, unsigned attrs);
 
 extern bool
 NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleValue value,
                          unsigned attrs);
 
 extern bool
-NativeDefineAccessorProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
-                             JSGetterOp getter, JSSetterOp setter, unsigned attrs);
-
-extern bool
 NativeDefineDataProperty(JSContext* cx, HandleNativeObject obj, PropertyName* name,
                          HandleValue value, unsigned attrs);
 
 extern bool
 NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);
 
 extern bool
 NativeGetOwnPropertyDescriptor(JSContext* cx, HandleNativeObject obj, HandleId id,
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -233,28 +233,16 @@ AutoRooterGetterSetter::AutoRooterGetter
                                                GetterOp* pgetter, SetterOp* psetter
                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
 {
     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
         inner.emplace(cx, attrs, pgetter, psetter);
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
-inline
-AutoRooterGetterSetter::AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
-                                               JSNative* pgetter, JSNative* psetter
-                                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-{
-    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
-        inner.emplace(cx, attrs, reinterpret_cast<GetterOp*>(pgetter),
-                      reinterpret_cast<SetterOp*>(psetter));
-    }
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-}
-
 static inline uint8_t
 GetPropertyAttributes(JSObject* obj, PropertyResult prop)
 {
     MOZ_ASSERT(obj->isNative());
 
     if (prop.isDenseOrTypedArrayElement()) {
         if (obj->is<TypedArrayObject>())
             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -536,26 +536,43 @@ Shape::updateDictionaryTable(ShapeTable*
     entry->setPreservingCollision(this);
     table->incEntryCount();
 
     // Pass the table along to the new last property, namely *this.
     MOZ_ASSERT(parent->maybeTable(keep) == table);
     parent->handoffTableTo(this);
 }
 
+static void
+AssertValidPropertyOp(NativeObject* obj, GetterOp getter, SetterOp setter, unsigned attrs)
+{
+    // We only support PropertyOp accessors on ArrayObject and ArgumentsObject
+    // and we don't want to add more of these properties (bug 1404885).
+
+#ifdef DEBUG
+    if ((getter && !(attrs & JSPROP_GETTER)) ||
+        (setter && !(attrs & JSPROP_SETTER)))
+    {
+        MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<ArgumentsObject>());
+    }
+#endif
+}
+
 /* static */ Shape*
 NativeObject::addAccessorPropertyInternal(JSContext* cx,
                                           HandleNativeObject obj, HandleId id,
                                           GetterOp getter, SetterOp setter, unsigned attrs,
                                           ShapeTable* table, ShapeTable::Entry* entry,
                                           const AutoKeepShapeTables& keep)
 {
     AutoCheckShapeConsistency check(obj);
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
+    AssertValidPropertyOp(obj, getter, setter, attrs);
+
     if (!maybeConvertToOrGrowDictionaryForAdd(cx, obj, id, &table, &entry, keep))
         return nullptr;
 
     // Find or create a property tree node labeled by our arguments.
     RootedShape shape(cx);
     {
         RootedShape last(cx, obj->lastProperty());
         Rooted<UnownedBaseShape*> nbase(cx, GetBaseShapeForNewShape(cx, last, id));
@@ -967,16 +984,17 @@ NativeObject::putDataProperty(JSContext*
 /* static */ Shape*
 NativeObject::putAccessorProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
                                   GetterOp getter, SetterOp setter, unsigned attrs)
 {
     MOZ_ASSERT(!JSID_IS_VOID(id));
 
     AutoCheckShapeConsistency check(obj);
     AssertValidArrayIndex(obj, id);
+    AssertValidPropertyOp(obj, getter, setter, attrs);
 
     AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
 
     // Search for id in order to claim its entry if table has been allocated.
     AutoKeepShapeTables keep(cx);
     RootedShape shape(cx);
     {
         ShapeTable* table;
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -383,21 +383,16 @@ class MOZ_RAII AutoKeepShapeTables
     void operator=(const AutoKeepShapeTables&) = delete;
 
   public:
     explicit inline AutoKeepShapeTables(JSContext* cx);
     inline ~AutoKeepShapeTables();
 };
 
 /*
- * Use the reserved attribute bit to mean shadowability.
- */
-#define JSPROP_SHADOWABLE       JSPROP_INTERNAL_USE_BIT
-
-/*
  * Shapes encode information about both a property lineage *and* a particular
  * property. This information is split across the Shape and the BaseShape
  * at shape->base(). Both Shape and BaseShape can be either owned or unowned
  * by, respectively, the Object or Shape referring to them.
  *
  * Owned Shapes are used in dictionary objects, and form a doubly linked list
  * whose entries are all owned by that dictionary. Unowned Shapes are all in
  * the property tree.
@@ -1073,18 +1068,16 @@ class Shape : public gc::TenuredCell
 
     bool isDataDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
     }
     bool isAccessorDescriptor() const {
         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
     }
 
-    bool hasShadowable() const { return attrs & JSPROP_SHADOWABLE; }
-
     uint32_t entryCount() {
         JS::AutoCheckCannotGC nogc;
         if (ShapeTable* table = maybeTable(nogc))
             return table->entryCount();
         uint32_t count = 0;
         for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
             ++count;
         return count;
@@ -1211,19 +1204,16 @@ class MOZ_RAII AutoRooterGetterSetter
         GetterOp* pgetter;
         SetterOp* psetter;
     };
 
   public:
     inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
                                   GetterOp* pgetter, SetterOp* psetter
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
-    inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
-                                  JSNative* pgetter, JSNative* psetter
-                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
   private:
     mozilla::Maybe<Inner> inner;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct EmptyShape : public js::Shape
 {
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1633,27 +1633,27 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         PropertyName* name;
         Scalar::Type type;
     };
 
   private:
     class HashableSig
     {
         uint32_t sigIndex_;
-        const SigWithIdVector& sigs_;
+        const TypeDefVector& types_;
 
       public:
-        HashableSig(uint32_t sigIndex, const SigWithIdVector& sigs)
-          : sigIndex_(sigIndex), sigs_(sigs)
+        HashableSig(uint32_t sigIndex, const TypeDefVector& types)
+          : sigIndex_(sigIndex), types_(types)
         {}
         uint32_t sigIndex() const {
             return sigIndex_;
         }
         const Sig& sig() const {
-            return sigs_[sigIndex_];
+            return types_[sigIndex_].funcType();
         }
 
         // Implement HashPolicy:
         typedef const Sig& Lookup;
         static HashNumber hash(Lookup l) {
             return l.hash();
         }
         static bool match(HashableSig lhs, Lookup rhs) {
@@ -1661,18 +1661,18 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         }
     };
 
     class NamedSig : public HashableSig
     {
         PropertyName* name_;
 
       public:
-        NamedSig(PropertyName* name, uint32_t sigIndex, const SigWithIdVector& sigs)
-          : HashableSig(sigIndex, sigs), name_(name)
+        NamedSig(PropertyName* name, uint32_t sigIndex, const TypeDefVector& types)
+          : HashableSig(sigIndex, types), name_(name)
         {}
         PropertyName* name() const {
             return name_;
         }
 
         // Implement HashPolicy:
         struct Lookup {
             PropertyName* name;
@@ -1750,32 +1750,32 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     }
     bool addStandardLibrarySimdOpName(const char* name, SimdOperation op) {
         JSAtom* atom = Atomize(cx_, name, strlen(name));
         if (!atom)
             return false;
         return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
     }
     bool newSig(Sig&& sig, uint32_t* sigIndex) {
-        if (env_.sigs.length() >= MaxTypes)
+        if (env_.types.length() >= MaxTypes)
             return failCurrentOffset("too many signatures");
 
-        *sigIndex = env_.sigs.length();
-        return env_.sigs.append(std::move(sig));
+        *sigIndex = env_.types.length();
+        return env_.types.append(std::move(sig));
     }
     bool declareSig(Sig&& sig, uint32_t* sigIndex) {
         SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
         if (p) {
             *sigIndex = p->sigIndex();
-            MOZ_ASSERT(env_.sigs[*sigIndex] == sig);
+            MOZ_ASSERT(env_.types[*sigIndex].funcType() == sig);
             return true;
         }
 
         return newSig(std::move(sig), sigIndex) &&
-               sigSet_.add(p, HashableSig(*sigIndex, env_.sigs));
+               sigSet_.add(p, HashableSig(*sigIndex, env_.types));
     }
 
   public:
     ModuleValidator(JSContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
       : cx_(cx),
         parser_(parser),
         moduleFunctionNode_(moduleFunctionNode),
         moduleFunctionName_(FunctionName(moduleFunctionNode)),
@@ -2303,17 +2303,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
 
         if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex))
             return false;
 
         uint32_t sigIndex;
         if (!declareSig(std::move(sig), &sigIndex))
             return false;
 
-        return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.sigs), *importIndex);
+        return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.types), *importIndex);
     }
 
     bool tryConstantAccess(uint64_t start, uint64_t width) {
         MOZ_ASSERT(UINT64_MAX - start > width);
         uint64_t len = start + width;
         if (len > uint64_t(INT32_MAX) + 1)
             return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
@@ -2452,22 +2452,22 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     }
     SharedModule finish() {
         MOZ_ASSERT(env_.funcSigs.empty());
         if (!env_.funcSigs.resize(funcImportMap_.count() + funcDefs_.length()))
             return nullptr;
         for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty(); r.popFront()) {
             uint32_t funcIndex = r.front().value();
             MOZ_ASSERT(!env_.funcSigs[funcIndex]);
-            env_.funcSigs[funcIndex] = &env_.sigs[r.front().key().sigIndex()];
+            env_.funcSigs[funcIndex] = &env_.types[r.front().key().sigIndex()].funcType();
         }
         for (const Func& func : funcDefs_) {
             uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
             MOZ_ASSERT(!env_.funcSigs[funcIndex]);
-            env_.funcSigs[funcIndex] = &env_.sigs[func.sigIndex()];
+            env_.funcSigs[funcIndex] = &env_.types[func.sigIndex()].funcType();
         }
 
         if (!env_.funcImportGlobalDataOffsets.resize(funcImportMap_.count()))
             return nullptr;
 
         asmJSMetadata_->usesSimd = simdPresent_;
 
         MOZ_ASSERT(asmJSMetadata_->asmJSFuncNames.empty());
@@ -4888,17 +4888,17 @@ CheckFunctionSignature(ModuleValidator& 
 
     ModuleValidator::Func* existing = m.lookupFuncDef(name);
     if (!existing) {
         if (!CheckModuleLevelName(m, usepn, name))
             return false;
         return m.addFuncDef(name, usepn->pn_pos.begin, std::move(sig), func);
     }
 
-    const SigWithId& existingSig = m.env().sigs[existing->sigIndex()];
+    const SigWithId& existingSig = m.env().types[existing->sigIndex()].funcType();
 
     if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig))
         return false;
 
     *func = existing;
     return true;
 }
 
@@ -4946,17 +4946,17 @@ CheckFuncPtrTableAgainstExisting(ModuleV
     if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
         if (existing->which() != ModuleValidator::Global::Table)
             return m.failName(usepn, "'%s' is not a function-pointer table", name);
 
         ModuleValidator::Table& table = m.table(existing->tableIndex());
         if (mask != table.mask())
             return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
 
-        if (!CheckSignatureAgainstExisting(m, usepn, sig, m.env().sigs[table.sigIndex()]))
+        if (!CheckSignatureAgainstExisting(m, usepn, sig, m.env().types[table.sigIndex()].funcType()))
             return false;
 
         *tableIndex = existing->tableIndex();
         return true;
     }
 
     if (!CheckModuleLevelName(m, usepn, name))
         return false;
@@ -7371,17 +7371,17 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
         if (!elem->isKind(ParseNodeKind::Name))
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         PropertyName* funcName = elem->name();
         const ModuleValidator::Func* func = m.lookupFuncDef(funcName);
         if (!func)
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
-        const Sig& funcSig = m.env().sigs[func->sigIndex()];
+        const Sig& funcSig = m.env().types[func->sigIndex()].funcType();
         if (sig) {
             if (*sig != funcSig)
                 return m.fail(elem, "all functions in table must have same signature");
         } else {
             sig = &funcSig;
         }
 
         if (!elemFuncDefIndices.append(func->funcDefIndex()))
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -121,33 +121,58 @@ typedef AstVector<AstRef> AstRefVector;
 
 struct AstBase
 {
     void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
         return astLifo.alloc(numBytes);
     }
 };
 
-class AstSig : public AstBase
+class AstSig;
+class AstStruct;
+
+class AstTypeDef : public AstBase
+{
+  protected:
+    enum class Which { IsSig, IsStruct };
+
+  private:
+    Which which_;
+
+  public:
+    explicit AstTypeDef(Which which) : which_(which) {}
+
+    bool isSig() const { return which_ == Which::IsSig; }
+    bool isStruct() const { return which_ == Which::IsStruct; }
+    inline AstSig& asSig();
+    inline AstStruct& asStruct();
+    inline const AstSig& asSig() const;
+    inline const AstStruct& asStruct() const;
+};
+
+class AstSig : public AstTypeDef
 {
     AstName name_;
     AstValTypeVector args_;
     ExprType ret_;
 
   public:
     explicit AstSig(LifoAlloc& lifo)
-      : args_(lifo),
+      : AstTypeDef(Which::IsSig),
+        args_(lifo),
         ret_(ExprType::Void)
     {}
     AstSig(AstValTypeVector&& args, ExprType ret)
-      : args_(std::move(args)),
+      : AstTypeDef(Which::IsSig),
+        args_(std::move(args)),
         ret_(ret)
     {}
     AstSig(AstName name, AstSig&& rhs)
-      : name_(name),
+      : AstTypeDef(Which::IsSig),
+        name_(name),
         args_(std::move(rhs.args_)),
         ret_(rhs.ret_)
     {}
     const AstValTypeVector& args() const {
         return args_;
     }
     ExprType ret() const {
         return ret_;
@@ -166,16 +191,78 @@ class AstSig : public AstBase
             hn = mozilla::AddToHash(hn, vt.code());
         return hn;
     }
     static bool match(const AstSig* lhs, Lookup rhs) {
         return *lhs == rhs;
     }
 };
 
+class AstStruct : public AstTypeDef
+{
+    AstName          name_;
+    AstNameVector    fieldNames_;
+    AstValTypeVector fieldTypes_;
+
+  public:
+    explicit AstStruct(LifoAlloc& lifo)
+      : AstTypeDef(Which::IsStruct),
+        fieldNames_(lifo),
+        fieldTypes_(lifo)
+    {}
+    AstStruct(AstNameVector&& names, AstValTypeVector&& types)
+      : AstTypeDef(Which::IsStruct),
+        fieldNames_(std::move(names)),
+        fieldTypes_(std::move(types))
+    {}
+    AstStruct(AstName name, AstStruct&& rhs)
+      : AstTypeDef(Which::IsStruct),
+        name_(name),
+        fieldNames_(std::move(rhs.fieldNames_)),
+        fieldTypes_(std::move(rhs.fieldTypes_))
+    {}
+    AstName name() const {
+        return name_;
+    }
+    const AstNameVector& fieldNames() const {
+        return fieldNames_;
+    }
+    const AstValTypeVector& fieldTypes() const {
+        return fieldTypes_;
+    }
+};
+
+inline AstSig&
+AstTypeDef::asSig()
+{
+    MOZ_ASSERT(isSig());
+    return *static_cast<AstSig*>(this);
+}
+
+inline AstStruct&
+AstTypeDef::asStruct()
+{
+    MOZ_ASSERT(isStruct());
+    return *static_cast<AstStruct*>(this);
+}
+
+inline const AstSig&
+AstTypeDef::asSig() const
+{
+    MOZ_ASSERT(isSig());
+    return *static_cast<const AstSig*>(this);
+}
+
+inline const AstStruct&
+AstTypeDef::asStruct() const
+{
+    MOZ_ASSERT(isStruct());
+    return *static_cast<const AstStruct*>(this);
+}
+
 const uint32_t AstNodeUnknownOffset = 0;
 
 class AstNode : public AstBase
 {
     uint32_t offset_; // if applicable, offset in the binary format file
 
   public:
     AstNode() : offset_(AstNodeUnknownOffset) {}
@@ -943,25 +1030,25 @@ struct AstResizable
 };
 
 class AstModule : public AstNode
 {
   public:
     typedef AstVector<AstFunc*> FuncVector;
     typedef AstVector<AstImport*> ImportVector;
     typedef AstVector<AstExport*> ExportVector;
-    typedef AstVector<AstSig*> SigVector;
+    typedef AstVector<AstTypeDef*> TypeDefVector;
     typedef AstVector<AstName> NameVector;
     typedef AstVector<AstResizable> AstResizableVector;
 
   private:
     typedef AstHashMap<AstSig*, uint32_t, AstSig> SigMap;
 
     LifoAlloc&           lifo_;
-    SigVector            sigs_;
+    TypeDefVector        types_;
     SigMap               sigMap_;
     ImportVector         imports_;
     NameVector           funcImportNames_;
     AstResizableVector   tables_;
     AstResizableVector   memories_;
     ExportVector         exports_;
     Maybe<AstStartFunc>  startFunc_;
     FuncVector           funcs_;
@@ -969,17 +1056,17 @@ class AstModule : public AstNode
     AstElemSegmentVector elemSegments_;
     AstGlobalVector      globals_;
 
     size_t numGlobalImports_;
 
   public:
     explicit AstModule(LifoAlloc& lifo)
       : lifo_(lifo),
-        sigs_(lifo),
+        types_(lifo),
         sigMap_(lifo),
         imports_(lifo),
         funcImportNames_(lifo),
         tables_(lifo),
         memories_(lifo),
         exports_(lifo),
         funcs_(lifo),
         dataSegments_(lifo),
@@ -1033,38 +1120,41 @@ class AstModule : public AstNode
         return *startFunc_;
     }
     bool declare(AstSig&& sig, uint32_t* sigIndex) {
         SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
         if (p) {
             *sigIndex = p->value();
             return true;
         }
-        *sigIndex = sigs_.length();
+        *sigIndex = types_.length();
         auto* lifoSig = new (lifo_) AstSig(AstName(), std::move(sig));
         return lifoSig &&
-               sigs_.append(lifoSig) &&
-               sigMap_.add(p, sigs_.back(), *sigIndex);
+               types_.append(lifoSig) &&
+               sigMap_.add(p, static_cast<AstSig*>(types_.back()), *sigIndex);
     }
     bool append(AstSig* sig) {
-        uint32_t sigIndex = sigs_.length();
-        if (!sigs_.append(sig))
+        uint32_t sigIndex = types_.length();
+        if (!types_.append(sig))
             return false;
         SigMap::AddPtr p = sigMap_.lookupForAdd(*sig);
         return p || sigMap_.add(p, sig, sigIndex);
     }
-    const SigVector& sigs() const {
-        return sigs_;
+    const TypeDefVector& types() const {
+        return types_;
     }
     bool append(AstFunc* func) {
         return funcs_.append(func);
     }
     const FuncVector& funcs() const {
         return funcs_;
     }
+    bool append(AstStruct* str) {
+        return types_.append(str);
+    }
     bool append(AstImport* imp) {
         switch (imp->kind()) {
           case DefinitionKind::Function:
             if (!funcImportNames_.append(imp->name()))
                 return false;
             break;
           case DefinitionKind::Table:
             if (!tables_.append(AstResizable(imp->limits(), true)))
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -3757,17 +3757,17 @@ class BaseCompiler final : public BaseCo
         CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Symbolic);
         masm.call(desc, callee);
     }
 
     // Precondition: sync()
 
     void callIndirect(uint32_t sigIndex, const Stk& indexVal, const FunctionCall& call)
     {
-        const SigWithId& sig = env_.sigs[sigIndex];
+        const SigWithId& sig = env_.types[sigIndex].funcType();
         MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
 
         MOZ_ASSERT(env_.tables.length() == 1);
         const TableDesc& table = env_.tables[0];
 
         loadI32(indexVal, RegI32(WasmTableCallIndexReg));
 
         CallSiteDesc desc(call.lineOrBytecode, CallSiteDesc::Dynamic);
@@ -7856,17 +7856,17 @@ BaseCompiler::emitCallIndirect()
     if (!iter_.readCallIndirect(&sigIndex, &callee_, &args_))
         return false;
 
     if (deadCode_)
         return true;
 
     sync();
 
-    const SigWithId& sig = env_.sigs[sigIndex];
+    const SigWithId& sig = env_.types[sigIndex].funcType();
 
     // Stack: ... arg1 .. argn callee
 
     uint32_t numArgs = sig.args().length();
     size_t stackSpace = stackConsumed(numArgs + 1);
 
     // The arguments must be at the stack top for emitCallArgs, so pop the
     // callee if it is on top.  Note this only pops the compiler's stack,
@@ -8825,16 +8825,19 @@ BaseCompiler::emitInstanceCall(uint32_t 
         }
         passArg(t, peek(numArgs - i), &baselineCall);
     }
     builtinInstanceMethodCall(builtin, instanceArg, baselineCall);
     endCall(baselineCall, stackSpace);
 
     popValueStackBy(numArgs);
 
+    // Note, a number of clients of emitInstanceCall currently assume that the
+    // following operation does not destroy ReturnReg.
+
     pushReturnedIfNonVoid(baselineCall, retType);
 }
 
 bool
 BaseCompiler::emitGrowMemory()
 {
     uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
 
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -63,16 +63,19 @@ enum class TypeCode
     AnyFunc                              = 0x70,  // SLEB128(-0x10)
 
     // A reference to any type.
     AnyRef                               = 0x6f,
 
     // Type constructor for function types
     Func                                 = 0x60,  // SLEB128(-0x20)
 
+    // Type constructor for structure types - unofficial
+    Struct                               = 0x50,  // SLEB128(-0x30)
+
     // Special code representing the block signature ()->()
     BlockVoid                            = 0x40,  // SLEB128(-0x40)
 
     Limit                                = 0x80
 };
 
 // The representation of a null reference value throughout the compiler.
 
@@ -598,16 +601,17 @@ static const unsigned MaxFuncs          
 static const unsigned MaxImports             =   100000;
 static const unsigned MaxExports             =   100000;
 static const unsigned MaxGlobals             =  1000000;
 static const unsigned MaxDataSegments        =   100000;
 static const unsigned MaxElemSegments        = 10000000;
 static const unsigned MaxTableMaximumLength  = 10000000;
 static const unsigned MaxLocals              =    50000;
 static const unsigned MaxParams              =     1000;
+static const unsigned MaxStructFields        =     1000;
 static const unsigned MaxMemoryMaximumPages  =    65536;
 static const unsigned MaxStringBytes         =   100000;
 static const unsigned MaxModuleBytes         = 1024 * 1024 * 1024;
 static const unsigned MaxFunctionBytes       =  7654321;
 
 // These limits pertain to our WebAssembly implementation only.
 
 static const unsigned MaxTableInitialLength  = 10000000;
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -92,22 +92,22 @@ class AstDecodeContext
     DepthStack depths_;
     const ValTypeVector* locals_;
     AstNameVector blockLabels_;
     uint32_t currentLabelIndex_;
     ExprType retType_;
 
   public:
     AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module,
-                     bool generateNames)
+                     bool generateNames, HasGcTypes hasGcTypes)
      : cx(cx),
        lifo(lifo),
        d(d),
        generateNames(generateNames),
-       env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, HasGcTypes::False,
+       env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, hasGcTypes,
             cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()
             ? Shareable::True
             : Shareable::False),
        module_(module),
        iter_(nullptr),
        exprs_(lifo),
        depths_(lifo),
        locals_(nullptr),
@@ -346,17 +346,17 @@ AstDecodeCallIndirect(AstDecodeContext& 
         return true;
 
     AstDecodeStackItem index = c.popCopy();
 
     AstRef sigRef;
     if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
         return false;
 
-    const SigWithId& sig = c.env().sigs[sigIndex];
+    const SigWithId& sig = c.env().types[sigIndex].funcType();
     AstExprVector args(c.lifo);
     if (!AstDecodeCallArgs(c, sig, &args))
         return false;
 
     AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, sig.ret(), std::move(args), index.expr);
     if (!call)
         return false;
 
@@ -1953,36 +1953,69 @@ AstDecodeFunctionBody(AstDecodeContext &
 
     return true;
 }
 
 /*****************************************************************************/
 // wasm decoding and generation
 
 static bool
-AstCreateSignatures(AstDecodeContext& c)
+AstCreateTypes(AstDecodeContext& c)
 {
-    SigWithIdVector& sigs = c.env().sigs;
+    uint32_t typeIndexForNames = 0;
+    for (const TypeDef& td : c.env().types) {
+        if (td.isFuncType()) {
+            const Sig& sig = td.funcType();
+
+            AstValTypeVector args(c.lifo);
+            if (!args.appendAll(sig.args()))
+                return false;
+
+            AstSig sigNoName(std::move(args), sig.ret());
 
-    for (size_t sigIndex = 0; sigIndex < sigs.length(); sigIndex++) {
-        const Sig& sig = sigs[sigIndex];
+            AstName sigName;
+            if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &sigName))
+                return false;
+
+            AstSig* astSig = new(c.lifo) AstSig(sigName, std::move(sigNoName));
+            if (!astSig || !c.module().append(astSig))
+                return false;
+        } else if (td.isStructType()) {
+            const StructType& str = td.structType();
+
+            AstValTypeVector fieldTypes(c.lifo);
+            if (!fieldTypes.appendAll(str.fields_))
+                return false;
 
-        AstValTypeVector args(c.lifo);
-        if (!args.appendAll(sig.args()))
-            return false;
+            AstNameVector fieldNames(c.lifo);
+            if (!fieldNames.resize(fieldTypes.length()))
+                return false;
 
-        AstSig sigNoName(std::move(args), sig.ret());
+            // The multiplication ensures that generated field names are unique
+            // within the module, though the resulting namespace is very sparse.
+
+            for (size_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) {
+                size_t idx = (typeIndexForNames * MaxStructFields) + fieldIndex;
+                if (!GenerateName(c, AstName(u"f"), idx, &fieldNames[fieldIndex]))
+                    return false;
+            }
 
-        AstName sigName;
-        if (!GenerateName(c, AstName(u"type"), sigIndex, &sigName))
-            return false;
+            AstStruct structNoName(std::move(fieldNames), std::move(fieldTypes));
+
+            AstName structName;
+            if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &structName))
+                return false;
 
-        AstSig* astSig = new(c.lifo) AstSig(sigName, std::move(sigNoName));
-        if (!astSig || !c.module().append(astSig))
-            return false;
+            AstStruct* astStruct = new(c.lifo) AstStruct(structName, std::move(structNoName));
+            if (!astStruct || !c.module().append(astStruct))
+                return false;
+        } else {
+            MOZ_CRASH();
+        }
+        typeIndexForNames++;
     }
 
     return true;
 }
 
 static bool
 ToAstName(AstDecodeContext& c, const char* name, AstName* out)
 {
@@ -2225,17 +2258,17 @@ AstCreateElems(AstDecodeContext &c)
 }
 
 static bool
 AstDecodeEnvironment(AstDecodeContext& c)
 {
     if (!DecodeModuleEnvironment(c.d, &c.env()))
         return false;
 
-    if (!AstCreateSignatures(c))
+    if (!AstCreateTypes(c))
         return false;
 
     if (!AstCreateImports(c))
         return false;
 
     if (!AstCreateTables(c))
         return false;
 
@@ -2328,17 +2361,17 @@ wasm::BinaryToAst(JSContext* cx, const u
                   AstModule** module)
 {
     AstModule* result = new(lifo) AstModule(lifo);
     if (!result || !result->init())
         return false;
 
     UniqueChars error;
     Decoder d(bytes, bytes + length, 0, &error, nullptr, /* resilient */ true);
-    AstDecodeContext c(cx, lifo, d, *result, true);
+    AstDecodeContext c(cx, lifo, d, *result, /* generateNames */ true, HasGcTypes::True);
 
     if (!AstDecodeEnvironment(c) ||
         !AstDecodeCodeSection(c) ||
         !AstDecodeModuleTail(c))
     {
         if (error) {
             JS_ReportErrorNumberUTF8(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
                                      error.get());
--- a/js/src/wasm/WasmBinaryToText.cpp
+++ b/js/src/wasm/WasmBinaryToText.cpp
@@ -186,16 +186,17 @@ static bool
 RenderExprType(WasmRenderContext& c, ExprType type)
 {
     switch (type) {
       case ExprType::Void: return true; // ignoring void
       case ExprType::I32: return c.buffer.append("i32");
       case ExprType::I64: return c.buffer.append("i64");
       case ExprType::F32: return c.buffer.append("f32");
       case ExprType::F64: return c.buffer.append("f64");
+      case ExprType::AnyRef: return c.buffer.append("anyref");
       default:;
     }
 
     MOZ_CRASH("bad type");
 }
 
 static bool
 RenderValType(WasmRenderContext& c, ValType type)
@@ -205,16 +206,22 @@ RenderValType(WasmRenderContext& c, ValT
 
 static bool
 RenderName(WasmRenderContext& c, const AstName& name)
 {
     return c.buffer.append(name.begin(), name.end());
 }
 
 static bool
+RenderNonemptyName(WasmRenderContext& c, const AstName& name)
+{
+    return name.empty() || (RenderName(c, name) && c.buffer.append(' '));
+}
+
+static bool
 RenderRef(WasmRenderContext& c, const AstRef& ref)
 {
     if (ref.name().empty())
         return RenderInt32(c, ref.index());
 
     return RenderName(c, ref.name());
 }
 
@@ -1476,22 +1483,18 @@ RenderSignature(WasmRenderContext& c, co
 {
     uint32_t paramsNum = sig.args().length();
 
     if (maybeLocals) {
         for (uint32_t i = 0; i < paramsNum; i++) {
             if (!c.buffer.append(" (param "))
                 return false;
             const AstName& name = (*maybeLocals)[i];
-            if (!name.empty()) {
-                if (!RenderName(c, name))
-                    return false;
-                if (!c.buffer.append(" "))
-                    return false;
-            }
+            if (!RenderNonemptyName(c, name))
+                return false;
             ValType arg = sig.args()[i];
             if (!RenderValType(c, arg))
                 return false;
             if (!c.buffer.append(")"))
                 return false;
         }
     } else if (paramsNum > 0) {
         if (!c.buffer.append(" (param"))
@@ -1513,44 +1516,86 @@ RenderSignature(WasmRenderContext& c, co
             return false;
         if (!c.buffer.append(")"))
             return false;
     }
     return true;
 }
 
 static bool
-RenderTypeSection(WasmRenderContext& c, const AstModule::SigVector& sigs)
+RenderFields(WasmRenderContext& c, const AstStruct& str)
 {
-    uint32_t numSigs = sigs.length();
-    if (!numSigs)
-        return true;
+    const AstNameVector& fieldNames = str.fieldNames();
+    const AstValTypeVector& fieldTypes = str.fieldTypes();
 
-    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
-        const AstSig* sig = sigs[sigIndex];
+    for (uint32_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) {
+        if (!c.buffer.append("\n"))
+            return false;
         if (!RenderIndent(c))
             return false;
-        if (!c.buffer.append("(type"))
+        if (!c.buffer.append("(field "))
+            return false;
+        if (!RenderNonemptyName(c, fieldNames[fieldIndex]))
             return false;
-        if (!sig->name().empty()) {
-            if (!c.buffer.append(" "))
-                return false;
-            if (!RenderName(c, sig->name()))
-                return false;
-        }
-        if (!c.buffer.append(" (func"))
+        if (!RenderValType(c, fieldTypes[fieldIndex]))
             return false;
-        if (!RenderSignature(c, *sig))
-            return false;
-        if (!c.buffer.append("))\n"))
+        if (!c.buffer.append(')'))
             return false;
     }
     return true;
 }
 
+template<size_t ArrayLength>
+static bool
+RenderTypeStart(WasmRenderContext& c, const AstName& name, const char (&keyword)[ArrayLength])
+{
+    if (!RenderIndent(c))
+        return false;
+    if (!c.buffer.append("(type "))
+        return false;
+    if (!RenderNonemptyName(c, name))
+        return false;
+    if (!c.buffer.append("("))
+        return false;
+    return c.buffer.append(keyword);
+}
+
+static bool
+RenderTypeEnd(WasmRenderContext& c)
+{
+    return c.buffer.append("))\n");
+}
+
+static bool
+RenderTypeSection(WasmRenderContext& c, const AstModule::TypeDefVector& types)
+{
+    for (uint32_t typeIndex = 0; typeIndex < types.length(); typeIndex++) {
+        const AstTypeDef* type = types[typeIndex];
+        if (type->isSig()) {
+            const AstSig* sig = &type->asSig();
+            if (!RenderTypeStart(c, sig->name(), "func"))
+                return false;
+            if (!RenderSignature(c, *sig))
+                return false;
+        } else {
+            const AstStruct* strukt = &type->asStruct();
+            if (!RenderTypeStart(c, strukt->name(), "struct"))
+                return false;
+            c.indent++;
+            if (!RenderFields(c, *strukt))
+                return false;
+            c.indent--;
+        }
+        if (!RenderTypeEnd(c))
+            return false;
+    }
+
+    return true;
+}
+
 static bool
 RenderLimits(WasmRenderContext& c, const Limits& limits)
 {
     if (!RenderInt32(c, limits.initial))
         return false;
     if (limits.maximum) {
         if (!c.buffer.append(" "))
             return false;
@@ -1750,17 +1795,17 @@ RenderImport(WasmRenderContext& c, AstIm
 
     if (!c.buffer.append("\" "))
         return false;
 
     switch (import.kind()) {
       case DefinitionKind::Function: {
         if (!c.buffer.append("(func"))
             return false;
-        const AstSig* sig = module.sigs()[import.funcSig().index()];
+        const AstSig* sig = &module.types()[import.funcSig().index()]->asSig();
         if (!RenderSignature(c, *sig))
             return false;
         if (!c.buffer.append(")"))
             return false;
         break;
       }
       case DefinitionKind::Table: {
         if (!RenderResizableTable(c, import.limits()))
@@ -1853,19 +1898,19 @@ RenderExportSection(WasmRenderContext& c
     for (uint32_t i = 0; i < numExports; i++) {
         if (!RenderExport(c, *exports[i], funcImportNames, funcs))
             return false;
     }
     return true;
 }
 
 static bool
-RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVector& sigs)
+RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDefVector& types)
 {
-    const AstSig* sig = sigs[func.sig().index()];
+    const AstSig* sig = &types[func.sig().index()]->asSig();
 
     uint32_t argsNum = sig->args().length();
     uint32_t localsNum = func.vars().length();
     if (localsNum > 0) {
         if (!RenderIndent(c))
             return false;
         for (uint32_t i = 0; i < localsNum; i++) {
             if (!c.buffer.append("(local "))
@@ -1899,23 +1944,23 @@ RenderFunctionBody(WasmRenderContext& c,
             return false;
     }
 
     return true;
 }
 
 static bool
 RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs,
-                  const AstModule::SigVector& sigs)
+                  const AstModule::TypeDefVector& types)
 {
     uint32_t numFuncBodies = funcs.length();
     for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
         AstFunc* func = funcs[funcIndex];
         uint32_t sigIndex = func->sig().index();
-        AstSig* sig = sigs[sigIndex];
+        AstSig* sig = &types[sigIndex]->asSig();
 
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append("(func "))
             return false;
         if (!func->name().empty()) {
             if (!RenderName(c, func->name()))
                 return false;
@@ -1924,17 +1969,17 @@ RenderCodeSection(WasmRenderContext& c, 
         if (!RenderSignature(c, *sig, &(func->locals())))
             return false;
         if (!c.buffer.append("\n"))
             return false;
 
         c.currentFuncIndex = funcIndex;
 
         c.indent++;
-        if (!RenderFunctionBody(c, *func, sigs))
+        if (!RenderFunctionBody(c, *func, types))
             return false;
         c.indent--;
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append(")\n"))
             return false;
     }
 
@@ -2021,17 +2066,17 @@ RenderStartSection(WasmRenderContext& c,
 static bool
 RenderModule(WasmRenderContext& c, AstModule& module)
 {
     if (!c.buffer.append("(module\n"))
         return false;
 
     c.indent++;
 
-    if (!RenderTypeSection(c, module.sigs()))
+    if (!RenderTypeSection(c, module.types()))
         return false;
 
     if (!RenderImportSection(c, module))
         return false;
 
     if (!RenderTableSection(c, module))
         return false;
 
@@ -2045,17 +2090,17 @@ RenderModule(WasmRenderContext& c, AstMo
         return false;
 
     if (!RenderStartSection(c, module))
         return false;
 
     if (!RenderElemSection(c, module))
         return false;
 
-    if (!RenderCodeSection(c, module.funcs(), module.sigs()))
+    if (!RenderCodeSection(c, module.funcs(), module.types()))
         return false;
 
     if (!RenderDataSection(c, module))
         return false;
 
     c.indent--;
 
     if (!c.buffer.append(")"))
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -238,17 +238,21 @@ ModuleGenerator::init(Metadata* maybeAsm
     }
 
     for (TableDesc& table : env_->tables) {
         if (!allocateGlobalBytes(sizeof(TableTls), sizeof(void*), &table.globalDataOffset))
             return false;
     }
 
     if (!isAsmJS()) {
-        for (SigWithId& sig : env_->sigs) {
+        for (TypeDef& td : env_->types) {
+            if (!td.isFuncType())
+                continue;
+
+            SigWithId& sig = td.funcType();
             if (SigIdDesc::isGlobal(sig)) {
                 uint32_t globalDataOffset;
                 if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset))
                     return false;
 
                 sig.id = SigIdDesc::global(sig, globalDataOffset);
 
                 Sig copy;
@@ -974,24 +978,31 @@ ModuleGenerator::finishModule(const Shar
     auto codeTier = js::MakeUnique<CodeTier>(std::move(metadataTier_), std::move(moduleSegment));
     if (!codeTier)
         return nullptr;
 
     MutableCode code = js_new<Code>(std::move(codeTier), *metadata_, std::move(jumpTables));
     if (!code || !code->initialize(bytecode, *linkDataTier_))
         return nullptr;
 
+    StructTypeVector structTypes;
+    for (TypeDef& td : env_->types) {
+        if (td.isStructType() && !structTypes.append(std::move(td.structType())))
+            return nullptr;
+    }
+
     SharedModule module(js_new<Module>(std::move(assumptions_),
                                        *code,
                                        std::move(maybeDebuggingBytes),
                                        LinkData(std::move(linkDataTier_)),
                                        std::move(env_->imports),
                                        std::move(env_->exports),
                                        std::move(env_->dataSegments),
                                        std::move(env_->elemSegments),
+                                       std::move(structTypes),
                                        bytecode));
     if (!module)
         return nullptr;
 
     if (mode() == CompileMode::Tier1)
         module->startTier2(*compileArgs_);
 
     return module;
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -1207,17 +1207,17 @@ class FunctionCompiler
     bool callIndirect(uint32_t sigIndex, MDefinition* index, const CallCompileState& call,
                       MDefinition** def)
     {
         if (inDeadCode()) {
             *def = nullptr;
             return true;
         }
 
-        const SigWithId& sig = env_.sigs[sigIndex];
+        const SigWithId& sig = env_.types[sigIndex].funcType();
 
         CalleeDesc callee;
         if (env_.isAsmJS()) {
             MOZ_ASSERT(sig.id.kind() == SigIdDesc::Kind::None);
             const TableDesc& table = env_.tables[env_.asmJSSigToTableIndex[sigIndex]];
             MOZ_ASSERT(IsPowerOfTwo(table.limits.initial));
             MOZ_ASSERT(!table.external);
 
@@ -2230,17 +2230,17 @@ EmitCallIndirect(FunctionCompiler& f, bo
     } else {
         if (!f.iter().readCallIndirect(&sigIndex, &callee, &args))
             return false;
     }
 
     if (f.inDeadCode())
         return true;
 
-    const Sig& sig = f.env().sigs[sigIndex];
+    const Sig& sig = f.env().types[sigIndex].funcType();
 
     CallCompileState call(f, lineOrBytecode);
     if (!EmitCallArgs(f, sig, args, &call))
         return false;
 
     MDefinition* def;
     if (!f.callIndirect(sigIndex, callee, call, &def))
         return false;
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -393,16 +393,17 @@ Module::compiledSerializedSize() const
         return 0;
 
     return assumptions_.serializedSize() +
            linkData_.serializedSize() +
            SerializedVectorSize(imports_) +
            SerializedVectorSize(exports_) +
            SerializedPodVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
+           SerializedVectorSize(structTypes_) +
            code_->serializedSize();
 }
 
 /* virtual */ void
 Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
 {
     MOZ_ASSERT(!tiering_.lock()->active);
 
@@ -418,16 +419,17 @@ Module::compiledSerialize(uint8_t* compi
 
     uint8_t* cursor = compiledBegin;
     cursor = assumptions_.serialize(cursor);
     cursor = linkData_.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
+    cursor = SerializeVector(cursor, structTypes_);
     cursor = code_->serialize(cursor, linkData_);
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
 }
 
 /* static */ bool
 Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
 {
     Assumptions cached;
@@ -481,32 +483,38 @@ Module::deserialize(const uint8_t* bytec
     if (!cursor)
         return nullptr;
 
     ElemSegmentVector elemSegments;
     cursor = DeserializeVector(cursor, &elemSegments);
     if (!cursor)
         return nullptr;
 
+    StructTypeVector structTypes;
+    cursor = DeserializeVector(cursor, &structTypes);
+    if (!cursor)
+        return nullptr;
+
     SharedCode code;
     cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code);
     if (!cursor)
         return nullptr;
 
     MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS());
 
     return js_new<Module>(std::move(assumptions),
                           *code,
                           nullptr,            // Serialized code is never debuggable
                           std::move(linkData),
                           std::move(imports),
                           std::move(exports),
                           std::move(dataSegments),
                           std::move(elemSegments),
+                          std::move(structTypes),
                           *bytecode);
 }
 
 /* virtual */ JSObject*
 Module::createObject(JSContext* cx)
 {
     if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly))
         return nullptr;
@@ -622,16 +630,17 @@ Module::addSizeOfMisc(MallocSizeOf mallo
     code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
     *data += mallocSizeOf(this) +
              assumptions_.sizeOfExcludingThis(mallocSizeOf) +
              linkData_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
              dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
+             SizeOfVectorExcludingThis(structTypes_, mallocSizeOf) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
     if (unlinkedCodeForDebugging_)
         *data += unlinkedCodeForDebugging_->sizeOfExcludingThis(mallocSizeOf);
 }
 
 
 // Extracting machine code as JS object. The result has the "code" property, as
 // a Uint8Array, and the "segments" property as array objects. The objects
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -130,16 +130,17 @@ class Module : public JS::WasmModule
     const Assumptions       assumptions_;
     const SharedCode        code_;
     const UniqueConstBytes  unlinkedCodeForDebugging_;
     const LinkData          linkData_;
     const ImportVector      imports_;
     const ExportVector      exports_;
     const DataSegmentVector dataSegments_;
     const ElemSegmentVector elemSegments_;
+    const StructTypeVector  structTypes_;
     const SharedBytes       bytecode_;
     ExclusiveTiering        tiering_;
 
     // `codeIsBusy_` is set to false initially and then to true when `code_` is
     // already being used for an instance and can't be shared because it may be
     // patched by the debugger. Subsequent instances must then create copies
     // by linking the `unlinkedCodeForDebugging_`.
 
@@ -165,25 +166,27 @@ class Module : public JS::WasmModule
     Module(Assumptions&& assumptions,
            const Code& code,
            UniqueConstBytes unlinkedCodeForDebugging,
            LinkData&& linkData,
            ImportVector&& imports,
            ExportVector&& exports,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
+           StructTypeVector&& structTypes,
            const ShareableBytes& bytecode)
       : assumptions_(std::move(assumptions)),
         code_(&code),
         unlinkedCodeForDebugging_(std::move(unlinkedCodeForDebugging)),
         linkData_(std::move(linkData)),
         imports_(std::move(imports)),
         exports_(std::move(exports)),
         dataSegments_(std::move(dataSegments)),
         elemSegments_(std::move(elemSegments)),
+        structTypes_(std::move(structTypes)),
         bytecode_(&bytecode),
         tiering_(mutexid::WasmModuleTieringLock),
         codeIsBusy_(false)
     {
         MOZ_ASSERT_IF(metadata().debugEnabled, unlinkedCodeForDebugging_);
     }
     ~Module() override { /* Note: can be called on any thread */ }
 
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -1727,30 +1727,33 @@ OpIter<Policy>::readCallIndirect(uint32_
     MOZ_ASSERT(Classify(op_) == OpKind::CallIndirect);
 
     if (!env_.tables.length())
         return fail("can't call_indirect without a table");
 
     if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
-    if (*sigIndex >= env_.numSigs())
+    if (*sigIndex >= env_.numTypes())
         return fail("signature index out of range");
 
     uint8_t flags;
     if (!readFixedU8(&flags))
         return false;
 
     if (flags != uint8_t(MemoryTableFlags::Default))
         return fail("unexpected flags");
 
     if (!popWithType(ValType::I32, callee))
         return false;
 
-    const Sig& sig = env_.sigs[*sigIndex];
+    if (!env_.types[*sigIndex].isFuncType())
+        return fail("expected signature type");
+
+    const Sig& sig = env_.types[*sigIndex].funcType();
 
     if (!popCallArgs(sig.args(), argValues))
         return false;
 
     return push(sig.ret());
 }
 
 template <typename Policy>
@@ -1784,20 +1787,23 @@ template <typename Policy>
 inline bool
 OpIter<Policy>::readOldCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector* argValues)
 {
     MOZ_ASSERT(Classify(op_) == OpKind::OldCallIndirect);
 
     if (!readVarU32(sigIndex))
         return fail("unable to read call_indirect signature index");
 
-    if (*sigIndex >= env_.numSigs())
+    if (*sigIndex >= env_.numTypes())
         return fail("signature index out of range");
 
-    const Sig& sig = env_.sigs[*sigIndex];
+    if (!env_.types[*sigIndex].isFuncType())
+        return fail("expected signature type");
+
+    const Sig& sig = env_.types[*sigIndex].funcType();
 
     if (!popCallArgs(sig.args(), argValues))
         return false;
 
     if (!popWithType(ValType::I32, callee))
         return false;
 
     if (!push(sig.ret()))
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -89,16 +89,17 @@ class WasmToken
         End,
         EndOfFile,
         Equal,
         Error,
         Export,
 #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
         ExtraConversionOpcode,
 #endif
+        Field,
         Float,
         Func,
         GetGlobal,
         GetLocal,
         Global,
         GrowMemory,
         If,
         Import,
@@ -122,16 +123,17 @@ class WasmToken
         RefNull,
         Result,
         Return,
         SetGlobal,
         SetLocal,
         Shared,
         SignedInteger,
         Start,
+        Struct,
         Store,
         Table,
         TeeLocal,
         TernaryOpcode,
         Text,
         Then,
         Type,
         UnaryOpcode,
@@ -359,16 +361,17 @@ class WasmToken
           case Data:
           case Elem:
           case Else:
           case EndOfFile:
           case Equal:
           case End:
           case Error:
           case Export:
+          case Field:
           case Float:
           case Func:
           case Global:
           case Mutable:
           case Import:
           case Index:
           case Memory:
           case NegativeZero:
@@ -377,16 +380,17 @@ class WasmToken
           case Name:
           case Offset:
           case OpenParen:
           case Param:
           case Result:
           case Shared:
           case SignedInteger:
           case Start:
+          case Struct:
           case Table:
           case Text:
           case Then:
           case Type:
           case UnsignedInteger:
           case ValueType:
             return false;
           case Invalid:
@@ -946,16 +950,19 @@ WasmTokenStream::next()
             return WasmToken(WasmToken::Else, begin, cur_);
         if (consume(u"end"))
             return WasmToken(WasmToken::End, begin, cur_);
         if (consume(u"export"))
             return WasmToken(WasmToken::Export, begin, cur_);
         break;
 
       case 'f':
+        if (consume(u"field"))
+            return WasmToken(WasmToken::Field, begin, cur_);
+
         if (consume(u"func"))
             return WasmToken(WasmToken::Func, begin, cur_);
 
         if (consume(u"f32")) {
             if (!consume(u"."))
                 return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
 
             switch (*cur_) {
@@ -1703,16 +1710,18 @@ WasmTokenStream::next()
         if (consume(u"set_local"))
             return WasmToken(WasmToken::SetLocal, begin, cur_);
 #ifdef ENABLE_WASM_THREAD_OPS
         if (consume(u"shared"))
             return WasmToken(WasmToken::Shared, begin, cur_);
 #endif
         if (consume(u"start"))
             return WasmToken(WasmToken::Start, begin, cur_);
+        if (consume(u"struct"))
+            return WasmToken(WasmToken::Struct, begin, cur_);
         break;
 
       case 't':
         if (consume(u"table"))
             return WasmToken(WasmToken::Table, begin, cur_);
         if (consume(u"tee_local"))
             return WasmToken(WasmToken::TeeLocal, begin, cur_);
         if (consume(u"then"))
@@ -3326,34 +3335,81 @@ ParseFunc(WasmParseContext& c, AstModule
             return false;
         sigRef.setIndex(sigIndex);
     }
 
     auto* func = new(c.lifo) AstFunc(funcName, sigRef, std::move(vars), std::move(locals), std::move(body));
     return func && module->append(func);
 }
 
-static AstSig*
+static bool
+ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, bool* isMutable);
+
+static bool
+ParseStructFields(WasmParseContext& c, AstStruct* str)
+{
+    AstNameVector    names(c.lifo);
+    AstValTypeVector types(c.lifo);
+
+    while (true) {
+        if (!c.ts.getIf(WasmToken::OpenParen))
+            break;
+
+        if (!c.ts.match(WasmToken::Field, c.error))
+            return false;
+
+        AstName name = c.ts.getIfName();
+
+        WasmToken typeToken;
+        bool isMutable;
+        if (!ParseGlobalType(c, &typeToken, &isMutable))
+            return false;
+        if (!c.ts.match(WasmToken::CloseParen, c.error))
+            return false;
+
+        if (!names.append(name))
+            return false;
+        if (!types.append(typeToken.valueType()))
+            return false;
+    }
+
+    *str = AstStruct(std::move(names), std::move(types));
+    return true;
+}
+
+static AstTypeDef*
 ParseTypeDef(WasmParseContext& c)
 {
     AstName name = c.ts.getIfName();
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return nullptr;
-    if (!c.ts.match(WasmToken::Func, c.error))
+
+    AstTypeDef* type = nullptr;
+    if (c.ts.getIf(WasmToken::Func)) {
+        AstSig sig(c.lifo);
+        if (!ParseFuncSig(c, &sig))
+            return nullptr;
+
+        type = new(c.lifo) AstSig(name, std::move(sig));
+    } else if (c.ts.getIf(WasmToken::Struct)) {
+        AstStruct str(c.lifo);
+        if (!ParseStructFields(c, &str))
+            return nullptr;
+
+        type = new(c.lifo) AstStruct(name, std::move(str));
+    } else {
+        c.ts.generateError(c.ts.peek(), "bad type definition", c.error);
         return nullptr;
-
-    AstSig sig(c.lifo);
-    if (!ParseFuncSig(c, &sig))
-        return nullptr;
+    }
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
 
-    return new(c.lifo) AstSig(name, std::move(sig));
+    return type;
 }
 
 static bool
 MaybeParseOwnerIndex(WasmParseContext& c)
 {
     if (c.ts.peek().kind() == WasmToken::Index) {
         WasmToken elemIndex = c.ts.get();
         if (elemIndex.index()) {
@@ -3917,18 +3973,20 @@ ParseModule(const char16_t* text, uintpt
         return ParseBinaryModule(c, module);
     }
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmToken section = c.ts.get();
 
         switch (section.kind()) {
           case WasmToken::Type: {
-            AstSig* sig = ParseTypeDef(c);
-            if (!sig || !module->append(sig))
+            AstTypeDef* typeDef = ParseTypeDef(c);
+            if (!typeDef)
+                return nullptr;
+            if (!module->append(static_cast<AstSig*>(typeDef)))
                 return nullptr;
             break;
           }
           case WasmToken::Start: {
             if (!ParseStartFunc(c, section, module))
                 return nullptr;
             break;
           }
@@ -4003,16 +4061,17 @@ class Resolver
     UniqueChars* error_;
     AstNameMap varMap_;
     AstNameMap globalMap_;
     AstNameMap sigMap_;
     AstNameMap funcMap_;
     AstNameMap importMap_;
     AstNameMap tableMap_;
     AstNameMap memoryMap_;
+    AstNameMap typeMap_;
     AstNameVector targetStack_;
 
     bool registerName(AstNameMap& map, AstName name, size_t index) {
         AstNameMap::AddPtr p = map.lookupForAdd(name);
         if (!p) {
             if (!map.add(p, name, index))
                 return false;
         } else {
@@ -4040,24 +4099,26 @@ class Resolver
       : error_(error),
         varMap_(lifo),
         globalMap_(lifo),
         sigMap_(lifo),
         funcMap_(lifo),
         importMap_(lifo),
         tableMap_(lifo),
         memoryMap_(lifo),
+        typeMap_(lifo),
         targetStack_(lifo)
     {}
     bool init() {
         return sigMap_.init() &&
                funcMap_.init() &&
                importMap_.init() &&
                tableMap_.init() &&
                memoryMap_.init() &&
+               typeMap_.init() &&
                varMap_.init() &&
                globalMap_.init();
     }
     void beginFunc() {
         varMap_.clear();
         MOZ_ASSERT(targetStack_.empty());
     }
 
@@ -4067,16 +4128,17 @@ class Resolver
     }
 
     REGISTER(Sig, sigMap_)
     REGISTER(Func, funcMap_)
     REGISTER(Var, varMap_)
     REGISTER(Global, globalMap_)
     REGISTER(Table, tableMap_)
     REGISTER(Memory, memoryMap_)
+    REGISTER(Type, typeMap_)
 
 #undef REGISTER
 
     bool pushTarget(AstName name) {
         return targetStack_.append(name);
     }
     void popTarget(AstName name) {
         MOZ_ASSERT(targetStack_.back() == name);
@@ -4092,16 +4154,17 @@ class Resolver
     }
 
     RESOLVE(sigMap_, Signature)
     RESOLVE(funcMap_, Function)
     RESOLVE(varMap_, Local)
     RESOLVE(globalMap_, Global)
     RESOLVE(tableMap_, Table)
     RESOLVE(memoryMap_, Memory)
+    RESOLVE(typeMap_, Type)
 
 #undef RESOLVE
 
     bool resolveBranchTarget(AstRef& ref) {
         if (ref.name().empty())
             return true;
         for (size_t i = 0, e = targetStack_.length(); i < e; i++) {
             if (targetStack_[e - i - 1] == ref.name()) {
@@ -4533,21 +4596,28 @@ ResolveFunc(Resolver& r, AstFunc& func)
 static bool
 ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
 {
     Resolver r(lifo, error);
 
     if (!r.init())
         return false;
 
-    size_t numSigs = module->sigs().length();
-    for (size_t i = 0; i < numSigs; i++) {
-        AstSig* sig = module->sigs()[i];
-        if (!r.registerSigName(sig->name(), i))
-            return r.fail("duplicate signature");
+    size_t numTypes = module->types().length();
+    for (size_t i = 0; i < numTypes; i++) {
+        AstTypeDef* ty = module->types()[i];
+        if (ty->isSig()) {
+            AstSig* sig = static_cast<AstSig*>(ty);
+            if (!r.registerSigName(sig->name(), i))
+                return r.fail("duplicate signature");
+        } else if (ty->isStruct()) {
+            AstStruct* str = static_cast<AstStruct*>(ty);
+            if (!r.registerTypeName(str->name(), i))
+                return r.fail("duplicate struct");
+        }
     }
 
     size_t lastFuncIndex = 0;
     size_t lastGlobalIndex = 0;
     size_t lastMemoryIndex = 0;
     size_t lastTableIndex = 0;
     for (AstImport* imp : module->imports()) {
         switch (imp->kind()) {
@@ -5164,44 +5234,61 @@ EncodeExpr(Encoder& e, AstExpr& expr)
 }
 
 /*****************************************************************************/
 // wasm AST binary serialization
 
 static bool
 EncodeTypeSection(Encoder& e, AstModule& module)
 {
-    if (module.sigs().empty())
+    if (module.types().empty())
         return true;
 
     size_t offset;
     if (!e.startSection(SectionId::Type, &offset))
         return false;
 
-    if (!e.writeVarU32(module.sigs().length()))
+    if (!e.writeVarU32(module.types().length()))
         return false;
 
-    for (AstSig* sig : module.sigs()) {
-        if (!e.writeVarU32(uint32_t(TypeCode::Func)))
-            return false;
-
-        if (!e.writeVarU32(sig->args().length()))
-            return false;
-
-        for (ValType t : sig->args()) {
-            if (!e.writeValType(t))
+    for (AstTypeDef* ty : module.types()) {
+        if (ty->isSig()) {
+            AstSig* sig = static_cast<AstSig*>(ty);
+            if (!e.writeVarU32(uint32_t(TypeCode::Func)))
+                return false;
+
+            if (!e.writeVarU32(sig->args().length()))
+                return false;
+
+            for (ValType t : sig->args()) {
+                if (!e.writeValType(t))
+                    return false;
+            }
+
+            if (!e.writeVarU32(!IsVoid(sig->ret())))
                 return false;
-        }
-
-        if (!e.writeVarU32(!IsVoid(sig->ret())))
-            return false;
-
-        if (!IsVoid(sig->ret())) {
-            if (!e.writeValType(NonVoidToValType(sig->ret())))
+
+            if (!IsVoid(sig->ret())) {
+                if (!e.writeValType(NonVoidToValType(sig->ret())))
+                    return false;
+            }
+        } else if (ty->isStruct()) {
+            AstStruct* str = static_cast<AstStruct*>(ty);
+            if (!e.writeVarU32(uint32_t(TypeCode::Struct)))
                 return false;
+
+            if (!e.writeVarU32(str->fieldTypes().length()))
+                return false;
+
+            for (ValType t : str->fieldTypes()) {
+                if (!e.writeValType(t))
+                    return false;
+            }
+        } else {
+            MOZ_CRASH();
         }
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -323,16 +323,46 @@ SigWithId::deserialize(const uint8_t* cu
 
 size_t
 SigWithId::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return Sig::sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
+StructType::serializedSize() const
+{
+    return SerializedPodVectorSize(fields_) +
+           SerializedPodVectorSize(fieldOffsets_);
+}
+
+uint8_t*
+StructType::serialize(uint8_t* cursor) const
+{
+    cursor = SerializePodVector(cursor, fields_);
+    cursor = SerializePodVector(cursor, fieldOffsets_);
+    return cursor;
+}
+
+const uint8_t*
+StructType::deserialize(const uint8_t* cursor)
+{
+    (cursor = DeserializePodVector(cursor, &fields_));
+    (cursor = DeserializePodVector(cursor, &fieldOffsets_));
+    return cursor;
+}
+
+size_t
+StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
+{
+    return fields_.sizeOfExcludingThis(mallocSizeOf) +
+           fieldOffsets_.sizeOfExcludingThis(mallocSizeOf);
+}
+
+size_t
 Import::serializedSize() const
 {
     return module.serializedSize() +
            field.serializedSize() +
            sizeof(kind);
 }
 
 uint8_t*
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -19,17 +19,16 @@
 #ifndef wasm_types_h
 #define wasm_types_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Maybe.h"
-#include "mozilla/Move.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Unused.h"
 
 #include "NamespaceImports.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/IonTypes.h"
 #include "js/RefCounted.h"
@@ -748,16 +747,41 @@ class Sig
 
 struct SigHashPolicy
 {
     typedef const Sig& Lookup;
     static HashNumber hash(Lookup sig) { return sig.hash(); }
     static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
 };
 
+// Structure type.
+//
+// The Module owns a dense array of Struct values that represent the structure
+// types that the module knows about.  It is created from the sparse array of
+// types in the ModuleEnvironment when the Module is created.
+
+class StructType
+{
+  public:
+    ValTypeVector fields_;       // Scalar types of fields
+    Uint32Vector  fieldOffsets_; // Byte offsets into an object for corresponding field
+
+  public:
+    StructType() : fields_(), fieldOffsets_() {}
+
+    StructType(ValTypeVector&& fields, Uint32Vector&& fieldOffsets)
+      : fields_(std::move(fields)),
+        fieldOffsets_(std::move(fieldOffsets))
+    {}
+
+    WASM_DECLARE_SERIALIZABLE(StructType)
+};
+
+typedef Vector<StructType, 0, SystemAllocPolicy> StructTypeVector;
+
 // An InitExpr describes a deferred initializer expression, used to initialize
 // a global or a table element offset. Such expressions are created during
 // decoding and actually executed on module instantiation.
 
 class InitExpr
 {
   public:
     enum class Kind {
@@ -1108,16 +1132,107 @@ struct SigWithId : Sig
     void operator=(Sig&& rhs) { Sig::operator=(std::move(rhs)); }
 
     WASM_DECLARE_SERIALIZABLE(SigWithId)
 };
 
 typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
 typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
 
+// A tagged container for the various types that can be present in a wasm
+// module's type section.
+
+class TypeDef
+{
+    enum { IsFuncType, IsStructType, IsNone } tag_;
+    union {
+        SigWithId  funcType_;
+        StructType structType_;
+    };
+
+  public:
+    TypeDef() : tag_(IsNone), structType_(StructType()) {}
+
+    explicit TypeDef(Sig&& sig) : tag_(IsFuncType), funcType_(SigWithId(std::move(sig))) {}
+
+    explicit TypeDef(StructType&& structType) : tag_(IsStructType), structType_(std::move(structType)) {}
+
+    TypeDef(TypeDef&& td) : tag_(td.tag_), structType_(StructType()) {
+        switch (tag_) {
+          case IsFuncType:   funcType_ = std::move(td.funcType_); break;
+          case IsStructType: structType_ = std::move(td.structType_); break;
+          case IsNone:       break;
+        }
+    }
+
+    ~TypeDef() {
+        switch (tag_) {
+          case IsFuncType:   funcType_.~SigWithId(); break;
+          case IsStructType: structType_.~StructType(); break;
+          case IsNone:       break;
+        }
+    }
+
+    TypeDef& operator=(TypeDef&& that) {
+        tag_ = that.tag_;
+        switch (tag_) {
+          case IsFuncType:   funcType_ = std::move(that.funcType_); break;
+          case IsStructType: structType_ = std::move(that.structType_); break;
+          case IsNone:       break;
+        }
+        return *this;
+    }
+
+    bool isFuncType() const {
+        return tag_ == IsFuncType;
+    }
+
+    bool isStructType() const {
+        return tag_ == IsStructType;
+    }
+
+    const SigWithId& funcType() const {
+        MOZ_ASSERT(isFuncType());
+        return funcType_;
+    }
+
+    SigWithId& funcType() {
+        MOZ_ASSERT(isFuncType());
+        return funcType_;
+    }
+
+    // p has to point to the sig_ embedded within a TypeDef for this to be
+    // valid.
+    static const TypeDef* fromSigWithIdPtr(const SigWithId* p) {
+        const TypeDef* q = (const TypeDef*)((char*)p - offsetof(TypeDef, funcType_));
+        MOZ_ASSERT(q->tag_ == IsFuncType);
+        return q;
+    }
+
+    const StructType& structType() const {
+        MOZ_ASSERT(isStructType());
+        return structType_;
+    }
+
+    StructType& structType() {
+        MOZ_ASSERT(isStructType());
+        return structType_;
+    }
+
+    // p has to point to the struct_ embedded within a TypeDef for this to be
+    // valid.
+    static const TypeDef* fromStructPtr(const StructType* p) {
+        const TypeDef* q = (const TypeDef*)((char*)p - offsetof(TypeDef, structType_));
+        MOZ_ASSERT(q->tag_ == IsStructType);
+        return q;
+    }
+};
+
+typedef Vector<TypeDef, 0, SystemAllocPolicy> TypeDefVector;
+
 // A wasm::Trap represents a wasm-defined trap that can occur during execution
 // which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
 // symbolically, passing the bytecode offset to report as the trap offset. The
 // generated jump will be bound to a tiny stub which fills the offset and
 // then jumps to a per-Trap shared stub at the end of the module.
 
 enum class Trap
 {
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -1059,73 +1059,123 @@ DecodePreamble(Decoder& d)
         return d.failf("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
                        u32, EncodingVersion);
     }
 
     return true;
 }
 
 static bool
+DecodeFuncType(Decoder& d, ModuleEnvironment* env, uint32_t typeIndex)
+{
+    uint32_t numArgs;
+    if (!d.readVarU32(&numArgs))
+        return d.fail("bad number of function args");
+
+    if (numArgs > MaxParams)
+        return d.fail("too many arguments in signature");
+
+    ValTypeVector args;
+    if (!args.resize(numArgs))
+        return false;
+
+    for (uint32_t i = 0; i < numArgs; i++) {
+        if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &args[i]))
+            return false;
+    }
+
+    uint32_t numRets;
+    if (!d.readVarU32(&numRets))
+        return d.fail("bad number of function returns");
+
+    if (numRets > 1)
+        return d.fail("too many returns in signature");
+
+    ExprType result = ExprType::Void;
+
+    if (numRets == 1) {
+        ValType type;
+        if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &type))
+            return false;
+
+        result = ToExprType(type);
+    }
+
+    env->types[typeIndex] = TypeDef(Sig(std::move(args), result));
+    return true;
+}
+
+static bool
+DecodeStructType(Decoder& d, ModuleEnvironment* env, uint32_t typeIndex)
+{
+    if (env->gcTypesEnabled == HasGcTypes::False)
+        return d.fail("Structure types not enabled");
+
+    uint32_t numFields;
+    if (!d.readVarU32(&numFields))
+        return d.fail("Bad number of fields");
+
+    if (numFields > MaxStructFields)
+        return d.fail("too many fields in structure");
+
+    ValTypeVector fields;
+    if (!fields.resize(numFields))
+        return false;
+
+    Uint32Vector fieldOffsets;
+    if (!fieldOffsets.resize(numFields))
+        return false;
+
+    // TODO (subsequent patch): lay out the fields.
+
+    for (uint32_t i = 0; i < numFields; i++) {
+        if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &fields[i]))
+            return false;
+    }
+
+    env->types[typeIndex] = TypeDef(StructType(std::move(fields), std::move(fieldOffsets)));
+    return true;
+}
+
+static bool
 DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
 {
     MaybeSectionRange range;
     if (!d.startSection(SectionId::Type, env, &range, "type"))
         return false;
     if (!range)
         return true;
 
-    uint32_t numSigs;
-    if (!d.readVarU32(&numSigs))
-        return d.fail("expected number of signatures");
+    uint32_t numTypes;
+    if (!d.readVarU32(&numTypes))
+        return d.fail("expected number of types");
 
-    if (numSigs > MaxTypes)
-        return d.fail("too many signatures");
+    if (numTypes > MaxTypes)
+        return d.fail("too many types");
 
-    if (!env->sigs.resize(numSigs))
+    if (!env->types.resize(numTypes))
         return false;
 
-    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
+    for (uint32_t typeIndex = 0; typeIndex < numTypes; typeIndex++) {
         uint8_t form;
-        if (!d.readFixedU8(&form) || form != uint8_t(TypeCode::Func))
-            return d.fail("expected function form");
-
-        uint32_t numArgs;
-        if (!d.readVarU32(&numArgs))
-            return d.fail("bad number of function args");
+        if (!d.readFixedU8(&form))
+            return d.fail("expected type form");
 
-        if (numArgs > MaxParams)
-            return d.fail("too many arguments in signature");
-
-        ValTypeVector args;
-        if (!args.resize(numArgs))
-            return false;
-
-        for (uint32_t i = 0; i < numArgs; i++) {
-            if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &args[i]))
+        switch (form) {
+          case uint8_t(TypeCode::Func):
+            if (!DecodeFuncType(d, env, typeIndex))
                 return false;
+            break;
+          case uint8_t(TypeCode::Struct):
+            if (!DecodeStructType(d, env, typeIndex))
+                return false;
+            break;
+          default:
+            return d.fail("expected type form");
         }
-
-        uint32_t numRets;
-        if (!d.readVarU32(&numRets))
-            return d.fail("bad number of function returns");
-
-        if (numRets > 1)
-            return d.fail("too many returns in signature");
-
-        ExprType result = ExprType::Void;
-
-        if (numRets == 1) {
-            ValType type;
-            if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &type))
-                return false;
-
-            result = ToExprType(type);
-        }
-
-        env->sigs[sigIndex] = Sig(std::move(args), result);
     }
 
     return d.finishSection(*range, "type");
 }
 
 static UniqueChars
 DecodeName(Decoder& d)
 {
@@ -1149,24 +1199,27 @@ DecodeName(Decoder& d)
 
     memcpy(name.get(), bytes, numBytes);
     name[numBytes] = '\0';
 
     return name;
 }
 
 static bool
-DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex)
+DecodeSignatureIndex(Decoder& d, const TypeDefVector& types, uint32_t* sigIndex)
 {
     if (!d.readVarU32(sigIndex))
         return d.fail("expected signature index");
 
-    if (*sigIndex >= sigs.length())
+    if (*sigIndex >= types.length())
         return d.fail("signature index out of range");
 
+    if (!types[*sigIndex].isFuncType())
+        return d.fail("signature index references non-signature");
+
     return true;
 }
 
 static bool
 DecodeLimits(Decoder& d, Limits* limits, Shareable allowShared = Shareable::False)
 {
     uint8_t flags;
     if (!d.readFixedU8(&flags))
@@ -1330,19 +1383,19 @@ DecodeImport(Decoder& d, ModuleEnvironme
     if (!d.readFixedU8(&rawImportKind))
         return d.fail("failed to read import kind");
 
     DefinitionKind importKind = DefinitionKind(rawImportKind);
 
     switch (importKind) {
       case DefinitionKind::Function: {
         uint32_t sigIndex;
-        if (!DecodeSignatureIndex(d, env->sigs, &sigIndex))
+        if (!DecodeSignatureIndex(d, env->types, &sigIndex))
             return false;
-        if (!env->funcSigs.append(&env->sigs[sigIndex]))
+        if (!env->funcSigs.append(&env->types[sigIndex].funcType()))
             return false;
         if (env->funcSigs.length() > MaxFuncs)
             return d.fail("too many functions");
         break;
       }
       case DefinitionKind::Table: {
         if (!DecodeTableLimits(d, &env->tables))
             return false;
@@ -1423,19 +1476,19 @@ DecodeFunctionSection(Decoder& d, Module
     if (!numFuncs.isValid() || numFuncs.value() > MaxFuncs)
         return d.fail("too many functions");
 
     if (!env->funcSigs.reserve(numFuncs.value()))
         return false;
 
     for (uint32_t i = 0; i < numDefs; i++) {
         uint32_t sigIndex;
-        if (!DecodeSignatureIndex(d, env->sigs, &sigIndex))
+        if (!DecodeSignatureIndex(d, env->types, &sigIndex))
             return false;
-        env->funcSigs.infallibleAppend(&env->sigs[sigIndex]);
+        env->funcSigs.infallibleAppend(&env->types[sigIndex].funcType());
     }
 
     return d.finishSection(*range, "function");
 }
 
 static bool
 DecodeTableSection(Decoder& d, ModuleEnvironment* env)
 {
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -63,17 +63,17 @@ struct ModuleEnvironment
     const HasGcTypes          gcTypesEnabled;
     const Tier                tier;
 
     // Module fields decoded from the module environment (or initialized while
     // validating an asm.js module) and immutable during compilation:
     MemoryUsage               memoryUsage;
     uint32_t                  minMemoryLength;
     Maybe<uint32_t>           maxMemoryLength;
-    SigWithIdVector           sigs;
+    TypeDefVector             types;
     SigWithIdPtrVector        funcSigs;
     Uint32Vector              funcImportGlobalDataOffsets;
     GlobalDescVector          globals;
     TableDescVector           tables;
     Uint32Vector              asmJSSigToTableIndex;
     ImportVector              imports;
     ExportVector              exports;
     Maybe<uint32_t>           startFuncIndex;
@@ -100,18 +100,18 @@ struct ModuleEnvironment
         tier(tier),
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0)
     {}
 
     size_t numTables() const {
         return tables.length();
     }
-    size_t numSigs() const {
-        return sigs.length();
+    size_t numTypes() const {
+        return types.length();
     }
     size_t numFuncs() const {
         return funcSigs.length();
     }
     size_t numFuncImports() const {
         return funcImportGlobalDataOffsets.length();
     }
     size_t numFuncDefs() const {
@@ -128,17 +128,17 @@ struct ModuleEnvironment
     }
     bool debugEnabled() const {
         return debug == DebugEnabled::True;
     }
     bool funcIsImport(uint32_t funcIndex) const {
         return funcIndex < funcImportGlobalDataOffsets.length();
     }
     uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
-        return funcSigs[funcIndex] - sigs.begin();
+        return TypeDef::fromSigWithIdPtr(funcSigs[funcIndex]) - types.begin();
     }
 };
 
 // The Encoder class appends bytes to the Bytes object it is given during
 // construction. The client is responsible for the Bytes's lifetime and must
 // keep the Bytes alive as long as the Encoder is used.
 
 class Encoder
--- a/js/xpconnect/loader/ChromeScriptLoader.cpp
+++ b/js/xpconnect/loader/ChromeScriptLoader.cpp
@@ -44,16 +44,18 @@ public:
                         const CompileScriptOptionsDictionary& aOptions,
                         Promise* aPromise)
       : mozilla::Runnable("AsyncScriptCompiler")
       , mOptions(aCx)
       , mURL(aURL)
       , mGlobalObject(aGlobal)
       , mPromise(aPromise)
       , mCharset(aOptions.mCharset)
+      , mToken(nullptr)
+      , mScriptLength(0)
     {
         mOptions.setNoScriptRval(!aOptions.mHasReturnValue)
                 .setCanLazilyParse(aOptions.mLazilyParse)
                 .setFile(aCx, mURL.get());
     }
 
     nsresult Start(nsIPrincipal* aPrincipal);
 
--- a/js/xpconnect/src/BackstagePass.h
+++ b/js/xpconnect/src/BackstagePass.h
@@ -36,17 +36,18 @@ public:
 
   void ForgetGlobalObject() {
     mWrapper = nullptr;
   }
 
   void SetGlobalObject(JSObject* global);
 
   explicit BackstagePass(nsIPrincipal* prin) :
-    mPrincipal(prin)
+    mPrincipal(prin),
+    mWrapper(nullptr)
   {
   }
 
 private:
   virtual ~BackstagePass() { }
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   XPCWrappedNative* mWrapper;
--- a/js/xpconnect/src/XPCCallContext.cpp
+++ b/js/xpconnect/src/XPCCallContext.cpp
@@ -29,17 +29,22 @@ XPCCallContext::XPCCallContext(JSContext
                                Value* rval         /* = nullptr               */)
     :   mAr(cx),
         mState(INIT_FAILED),
         mXPC(nsXPConnect::XPConnect()),
         mXPCJSContext(nullptr),
         mJSContext(cx),
         mWrapper(nullptr),
         mTearOff(nullptr),
-        mName(cx)
+        mMember(nullptr),
+        mName(cx),
+        mStaticMemberIsLocal(false),
+        mArgc(0),
+        mArgv(nullptr),
+        mRetVal(nullptr)
 {
     MOZ_ASSERT(cx);
     MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
 
     if (!mXPC)
         return;
 
     mXPCJSContext = XPCJSContext::Get();
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -335,20 +335,17 @@ DefinePropertyIfFound(XPCCallContext& cc
                 return false;
 
             propFlags |= JSPROP_GETTER;
             propFlags &= ~JSPROP_ENUMERATE;
 
             AutoResolveName arn(ccx, id);
             if (resolved)
                 *resolved = true;
-            return JS_DefinePropertyById(ccx, obj, id,
-                                         JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()),
-                                         nullptr,
-                                         propFlags);
+            return JS_DefinePropertyById(ccx, obj, id, funobj, nullptr, propFlags);
         }
 
         if (resolved)
             *resolved = false;
         return true;
     }
 
     if (!member) {
@@ -401,31 +398,28 @@ DefinePropertyIfFound(XPCCallContext& cc
     }
 
     // else...
 
     MOZ_ASSERT(member->IsAttribute(), "way broken!");
 
     propFlags |= JSPROP_GETTER;
     propFlags &= ~JSPROP_READONLY;
-    JSObject* funobj = funval.toObjectOrNull();
-    JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
-    JSNative setter;
+    RootedObject funobjGetter(ccx, funval.toObjectOrNull());
+    RootedObject funobjSetter(ccx);
     if (member->IsWritableAttribute()) {
         propFlags |= JSPROP_SETTER;
-        setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
-    } else {
-        setter = nullptr;
+        funobjSetter = funobjGetter;
     }
 
     AutoResolveName arn(ccx, id);
     if (resolved)
         *resolved = true;
 
-    return JS_DefinePropertyById(ccx, obj, id, getter, setter, propFlags);
+    return JS_DefinePropertyById(ccx, obj, id, funobjGetter, funobjSetter, propFlags);
 }
 
 /***************************************************************************/
 /***************************************************************************/
 
 static bool
 XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -288,22 +288,20 @@ public:
     static nsIPrincipal* gSystemPrincipal;
 };
 
 /***************************************************************************/
 
 class XPCRootSetElem
 {
 public:
-    XPCRootSetElem()
+    XPCRootSetElem() :
+        mNext(nullptr),
+        mSelfp(nullptr)
     {
-#ifdef DEBUG
-        mNext = nullptr;
-        mSelfp = nullptr;
-#endif
     }
 
     ~XPCRootSetElem()
     {
         MOZ_ASSERT(!mNext, "Must be unlinked");
         MOZ_ASSERT(!mSelfp, "Must be unlinked");
     }
 
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -416,47 +416,39 @@ TryResolvePropertyFromSpecs(JSContext* c
         // JS_GetPropertyById at the top of JSXrayTraits::resolveOwnProperty.
         //
         // Note also that the public-facing API here doesn't give us a way to
         // pass along JITInfo. It's probably ok though, since Xrays are already
         // pretty slow.
         desc.value().setUndefined();
         unsigned flags = psMatch->flags;
         if (psMatch->isAccessor()) {
-            RootedFunction getterObj(cx);
-            RootedFunction setterObj(cx);
             if (psMatch->isSelfHosted()) {
-                getterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.getter.selfHosted.funname, id, 0);
-                if (!getterObj)
+                JSFunction* getterFun = JS::GetSelfHostedFunction(cx, psMatch->accessors.getter.selfHosted.funname, id, 0);
+                if (!getterFun)
                     return false;
-                desc.setGetterObject(JS_GetFunctionObject(getterObj));
+                RootedObject getterObj(cx, JS_GetFunctionObject(getterFun));
+                RootedObject setterObj(cx);
                 if (psMatch->accessors.setter.selfHosted.funname) {
                     MOZ_ASSERT(flags & JSPROP_SETTER);
-                    setterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.setter.selfHosted.funname, id, 0);
-                    if (!setterObj)
+                    JSFunction* setterFun = JS::GetSelfHostedFunction(cx, psMatch->accessors.setter.selfHosted.funname, id, 0);
+                    if (!setterFun)
                         return false;
-                    desc.setSetterObject(JS_GetFunctionObject(setterObj));
+                    setterObj = JS_GetFunctionObject(setterFun);
                 }
+                if (!JS_DefinePropertyById(cx, holder, id, getterObj, setterObj, flags))
+                    return false;
             } else {
-                desc.setGetter(JS_CAST_NATIVE_TO(psMatch->accessors.getter.native.op,
-                                                 JSGetterOp));
-                desc.setSetter(JS_CAST_NATIVE_TO(psMatch->accessors.setter.native.op,
-                                                 JSSetterOp));
-            }
-            desc.setAttributes(flags);
-            if (!JS_DefinePropertyById(cx, holder, id,
-                                       JS_PROPERTYOP_GETTER(desc.getter()),
-                                       JS_PROPERTYOP_SETTER(desc.setter()),
-                                       // This particular descriptor, unlike most,
-                                       // actually stores JSNatives directly,
-                                       // since we just set it up.  Do NOT pass
-                                       // JSPROP_PROPOP_ACCESSORS here!
-                                       desc.attributes()))
-            {
-                return false;
+                if (!JS_DefinePropertyById(cx, holder, id,
+                                           psMatch->accessors.getter.native.op,
+                                           psMatch->accessors.setter.native.op,
+                                           flags))
+                {
+                    return false;
+                }
             }
         } else {
             RootedValue v(cx);
             if (!psMatch->getValue(cx, &v))
                 return false;
             if (!JS_DefinePropertyById(cx, holder, id, v, flags & ~JSPROP_INTERNAL_USE_BIT))
                 return false;
         }
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -391,11 +391,9 @@ nsLayoutStatics::Shutdown()
 
   DisplayItemClip::Shutdown();
 
   CacheObserver::Shutdown();
 
   PromiseDebugging::Shutdown();
 
   BlobURLProtocolHandler::RemoveDataEntries();
-
-  ClearSiteData::Shutdown();
 }
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -334,17 +334,18 @@ nsComputedDOMStyle::nsComputedDOMStyle(d
   // Should use aElement->OwnerDoc() instead.
   mDocumentWeak = do_GetWeakReference(aDocument);
   mElement = aElement;
   mPseudo = nsCSSPseudoElements::GetPseudoAtom(aPseudoElt);
 }
 
 nsComputedDOMStyle::~nsComputedDOMStyle()
 {
-  ClearComputedStyle();
+  MOZ_ASSERT(!mResolvedComputedStyle,
+             "Should have called ClearComputedStyle() during last release.");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle)
   tmp->ClearComputedStyle();  // remove observer before clearing mElement
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -370,18 +371,19 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_E
 
 // QueryInterface implementation for nsComputedDOMStyle
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration)
 
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsComputedDOMStyle)
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsComputedDOMStyle)
+NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
+  nsComputedDOMStyle, ClearComputedStyle())
 
 nsresult
 nsComputedDOMStyle::GetPropertyValue(const nsCSSPropertyID aPropID,
                                      nsAString& aValue)
 {
   // This is mostly to avoid code duplication with GetPropertyCSSValue(); if
   // perf ever becomes an issue here (doubtful), we can look into changing
   // this.
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -9338,17 +9338,21 @@ class CopyNonDefaultHeaderVisitor final 
 {
   nsCOMPtr<nsIHttpChannel> mTarget;
 
   ~CopyNonDefaultHeaderVisitor() = default;
 
   NS_IMETHOD
   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override
   {
-    return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
+    if (aValue.IsEmpty()) {
+      return mTarget->SetEmptyRequestHeader(aHeader);
+    } else {
+      return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
+    }
   }
 
 public:
   explicit CopyNonDefaultHeaderVisitor(nsIHttpChannel* aTarget)
     : mTarget(aTarget)
   {
     MOZ_DIAGNOSTIC_ASSERT(mTarget);
   }
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-30a4b03cd9d1
+NSS_3_38_RTM
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,22 +17,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.38" _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION "3.38" _NSS_CUSTOMIZED
 #define NSS_VMAJOR 3
 #define NSS_VMINOR 38
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
-#define NSS_BETA PR_TRUE
+#define NSS_BETA PR_FALSE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -12,16 +12,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.38" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION "3.38" SOFTOKEN_ECC_STRING
 #define SOFTOKEN_VMAJOR 3
 #define SOFTOKEN_VMINOR 38
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_TRUE
+#define SOFTOKEN_BETA PR_FALSE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.38 Beta"
+#define NSSUTIL_VERSION "3.38"
 #define NSSUTIL_VMAJOR 3
 #define NSSUTIL_VMINOR 38
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
-#define NSSUTIL_BETA PR_TRUE
+#define NSSUTIL_BETA PR_FALSE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/servo/components/style/counter_style/mod.rs
+++ b/servo/components/style/counter_style/mod.rs
@@ -4,18 +4,18 @@
 
 //! The [`@counter-style`][counter-style] at-rule.
 //!
 //! [counter-style]: https://drafts.csswg.org/css-counter-styles/
 
 use Atom;
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser};
 use cssparser::{CowRcStr, Parser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::{Parse, ParserContext};
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use std::mem;
 use std::num::Wrapping;
 use std::ops::Range;
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
@@ -68,42 +68,38 @@ pub fn parse_counter_style_name_definiti
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         } else {
             Ok(ident)
         }
     })
 }
 
 /// Parse the body (inside `{}`) of an @counter-style rule
-pub fn parse_counter_style_body<'i, 't, R>(
+pub fn parse_counter_style_body<'i, 't>(
     name: CustomIdent,
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser<'i, 't>,
     location: SourceLocation,
-) -> Result<CounterStyleRuleData, ParseError<'i>>
-where
-    R: ParseErrorReporter,
-{
+) -> Result<CounterStyleRuleData, ParseError<'i>> {
     let start = input.current_source_location();
     let mut rule = CounterStyleRuleData::empty(name, location);
     {
         let parser = CounterStyleRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
             if let Err((error, slice)) = declaration {
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(
                     slice,
                     error,
                 );
-                context.log_css_error(error_context, location, error)
+                context.log_css_error(location, error)
             }
         }
     }
     let error = match *rule.resolved_system() {
         ref system @ System::Cyclic |
         ref system @ System::Fixed { .. } |
         ref system @ System::Symbolic |
         ref system @ System::Alphabetic |
@@ -129,17 +125,17 @@ where
             Some(ContextualParseError::InvalidCounterStyleExtendsWithSymbols)
         },
         System::Extends(_) if rule.additive_symbols.is_some() => {
             Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols)
         },
         _ => None,
     };
     if let Some(error) = error {
-        context.log_css_error(error_context, start, error);
+        context.log_css_error(start, error);
         Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
     } else {
         Ok(rule)
     }
 }
 
 struct CounterStyleRuleParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -244,22 +244,8 @@ impl ParseErrorReporter for RustLogRepor
                 url.as_str(),
                 location.line,
                 location.column,
                 error
             )
         }
     }
 }
-
-/// Error reporter which silently forgets errors
-pub struct NullReporter;
-
-impl ParseErrorReporter for NullReporter {
-    fn report_error(
-        &self,
-        _url: &UrlExtraData,
-        _location: SourceLocation,
-        _error: ContextualParseError,
-    ) {
-        // do nothing
-    }
-}
--- a/servo/components/style/font_face.rs
+++ b/servo/components/style/font_face.rs
@@ -5,18 +5,18 @@
 //! The [`@font-face`][ff] at-rule.
 //!
 //! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule
 
 use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{CowRcStr, SourceLocation};
 #[cfg(feature = "gecko")]
 use cssparser::UnicodeRange;
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use properties::longhands::font_language_override;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError};
 use style_traits::{StyleParseErrorKind, ToCss};
@@ -181,37 +181,33 @@ impl ToCss for FontStyle {
             }
         }
     }
 }
 
 /// Parse the block inside a `@font-face` rule.
 ///
 /// Note that the prelude parsing code lives in the `stylesheets` module.
-pub fn parse_font_face_block<R>(
+pub fn parse_font_face_block(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
     location: SourceLocation,
-) -> FontFaceRuleData
-where
-    R: ParseErrorReporter,
-{
+) -> FontFaceRuleData {
     let mut rule = FontFaceRuleData::empty(location);
     {
         let parser = FontFaceRuleParser {
             context: context,
             rule: &mut rule,
         };
         let mut iter = DeclarationListParser::new(input, parser);
         while let Some(declaration) = iter.next() {
             if let Err((error, slice)) = declaration {
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);
-                context.log_css_error(error_context, location, error)
+                context.log_css_error(location, error)
             }
         }
     }
     rule
 }
 
 /// A @font-face rule that is known to have font-family and src declarations.
 #[cfg(feature = "servo")]
--- a/servo/components/style/media_queries/media_list.rs
+++ b/servo/components/style/media_queries/media_list.rs
@@ -4,18 +4,18 @@
 
 //! A media query list:
 //!
 //! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list
 
 use context::QuirksMode;
 use cssparser::{Delimiter, Parser};
 use cssparser::{ParserInput, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
-use parser::{ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::ParserContext;
 use super::{Device, MediaQuery, Qualifier};
 
 /// A type that encapsulates a media query list.
 #[css(comma)]
 #[derive(Clone, Debug, MallocSizeOf, ToCss)]
 pub struct MediaList {
     /// The list of media queries.
     #[css(iterable)]
@@ -25,42 +25,37 @@ pub struct MediaList {
 impl MediaList {
     /// Parse a media query list from CSS.
     ///
     /// Always returns a media query list. If any invalid media query is
     /// found, the media query list is only filled with the equivalent of
     /// "not all", see:
     ///
     /// <https://drafts.csswg.org/mediaqueries/#error-handling>
-    pub fn parse<R>(
+    pub fn parse(
         context: &ParserContext,
         input: &mut Parser,
-        error_reporter: &R,
-    ) -> MediaList
-    where
-        R: ParseErrorReporter,
-    {
+    ) -> Self {
         if input.is_exhausted() {
             return Self::empty();
         }
 
         let mut media_queries = vec![];
         loop {
             let start_position = input.position();
             match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {
                 Ok(mq) => {
                     media_queries.push(mq);
                 },
                 Err(err) => {
                     media_queries.push(MediaQuery::never_matching());
                     let location = err.location;
                     let error =
                         ContextualParseError::InvalidMediaRule(input.slice_from(start_position), err);
-                    let error_context = ParserErrorContext { error_reporter };
-                    context.log_css_error(&error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
 
             match input.next() {
                 Ok(&Token::Comma) => {},
                 Ok(_) => unreachable!(),
                 Err(_) => break,
             }
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -31,73 +31,73 @@ pub fn assert_parsing_mode_match() {
 
     check_parsing_modes! {
         ParsingMode_Default => ParsingMode::DEFAULT,
         ParsingMode_AllowUnitlessLength => ParsingMode::ALLOW_UNITLESS_LENGTH,
         ParsingMode_AllowAllNumericValues => ParsingMode::ALLOW_ALL_NUMERIC_VALUES,
     }
 }
 
-/// The context required to report a parse error.
-pub struct ParserErrorContext<'a, R: 'a> {
-    /// An error reporter to report syntax errors.
-    pub error_reporter: &'a R,
-}
-
 /// The data that the parser needs from outside in order to parse a stylesheet.
 pub struct ParserContext<'a> {
     /// The `Origin` of the stylesheet, whether it's a user, author or
     /// user-agent stylesheet.
     pub stylesheet_origin: Origin,
     /// The extra data we need for resolving url values.
     pub url_data: &'a UrlExtraData,
     /// The current rule type, if any.
     pub rule_type: Option<CssRuleType>,
     /// The mode to use when parsing.
     pub parsing_mode: ParsingMode,
     /// The quirks mode of this stylesheet.
     pub quirks_mode: QuirksMode,
+    /// The active error reporter, or none if error reporting is disabled.
+    error_reporter: Option<&'a ParseErrorReporter>,
     /// The currently active namespaces.
     pub namespaces: Option<&'a Namespaces>,
 }
 
 impl<'a> ParserContext<'a> {
     /// Create a parser context.
     #[inline]
     pub fn new(
         stylesheet_origin: Origin,
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
+        error_reporter: Option<&'a ParseErrorReporter>,
     ) -> Self {
         ParserContext {
             stylesheet_origin,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
+            error_reporter,
             namespaces: None,
         }
     }
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     #[inline]
     pub fn new_for_cssom(
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
+        error_reporter: Option<&'a ParseErrorReporter>,
     ) -> Self {
         Self::new(
             Origin::Author,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
+            error_reporter,
         )
     }
 
     /// Create a parser context based on a previous context, but with a modified rule type.
     #[inline]
     pub fn new_with_rule_type(
         context: &'a ParserContext,
         rule_type: CssRuleType,
@@ -105,16 +105,17 @@ impl<'a> ParserContext<'a> {
     ) -> ParserContext<'a> {
         ParserContext {
             stylesheet_origin: context.stylesheet_origin,
             url_data: context.url_data,
             rule_type: Some(rule_type),
             parsing_mode: context.parsing_mode,
             quirks_mode: context.quirks_mode,
             namespaces: Some(namespaces),
+            error_reporter: context.error_reporter,
         }
     }
 
     /// Whether we're in a @page rule.
     #[inline]
     pub fn in_page_rule(&self) -> bool {
         self.rule_type
             .map_or(false, |rule_type| rule_type == CssRuleType::Page)
@@ -122,31 +123,27 @@ impl<'a> ParserContext<'a> {
 
     /// Get the rule type, which assumes that one is available.
     pub fn rule_type(&self) -> CssRuleType {
         self.rule_type
             .expect("Rule type expected, but none was found.")
     }
 
     /// Record a CSS parse error with this context’s error reporting.
-    pub fn log_css_error<R>(
+    pub fn log_css_error(
         &self,
-        context: &ParserErrorContext<R>,
         location: SourceLocation,
         error: ContextualParseError,
-    ) where
-        R: ParseErrorReporter,
-    {
-        let location = SourceLocation {
-            line: location.line,
-            column: location.column,
+    ) {
+        let error_reporter = match self.error_reporter {
+            Some(r) => r,
+            None => return,
         };
-        context
-            .error_reporter
-            .report_error(self.url_data, location, error)
+
+        error_reporter.report_error(self.url_data, location, error)
     }
 
     /// Returns whether chrome-only rules should be parsed.
     pub fn chrome_rules_enabled(&self) -> bool {
         self.url_data.is_chrome() || self.stylesheet_origin == Origin::User
     }
 }
 
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -6,17 +6,17 @@
 
 #![deny(missing_docs)]
 
 use context::QuirksMode;
 use cssparser::{DeclarationListParser, parse_important, ParserInput, CowRcStr};
 use cssparser::{Parser, AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind};
 use custom_properties::CustomPropertiesBuilder;
 use error_reporting::{ParseErrorReporter, ContextualParseError};
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use properties::animated_properties::AnimationValue;
 use shared_lock::Locked;
 use smallbitvec::{self, SmallBitVec};
 use smallvec::SmallVec;
 use std::fmt::{self, Write};
 use std::iter::{DoubleEndedIterator, Zip};
 use std::slice::Iter;
 use str::{CssString, CssStringBorrow, CssStringWriter};
@@ -1072,73 +1072,73 @@ where
         dest.write_str(" !important")?;
     }
 
     dest.write_char(';')
 }
 
 /// A helper to parse the style attribute of an element, in order for this to be
 /// shared between Servo and Gecko.
-pub fn parse_style_attribute<R>(
+///
+/// Inline because we call this cross-crate.
+#[inline]
+pub fn parse_style_attribute(
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: &R,
+    error_reporter: Option<&ParseErrorReporter>,
     quirks_mode: QuirksMode,
-) -> PropertyDeclarationBlock
-where
-    R: ParseErrorReporter
-{
+) -> PropertyDeclarationBlock {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         quirks_mode,
+        error_reporter,
     );
 
-    let error_context = ParserErrorContext { error_reporter: error_reporter };
     let mut input = ParserInput::new(input);
-    parse_property_declaration_list(&context, &error_context, &mut Parser::new(&mut input))
+    parse_property_declaration_list(&context, &mut Parser::new(&mut input))
 }
 
 /// Parse a given property declaration. Can result in multiple
 /// `PropertyDeclaration`s when expanding a shorthand, for example.
 ///
 /// This does not attempt to parse !important at all.
-pub fn parse_one_declaration_into<R>(
+#[inline]
+pub fn parse_one_declaration_into(
     declarations: &mut SourcePropertyDeclaration,
     id: PropertyId,
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: &R,
+    error_reporter: Option<&ParseErrorReporter>,
     parsing_mode: ParsingMode,
     quirks_mode: QuirksMode
-) -> Result<(), ()>
-where
-    R: ParseErrorReporter
-{
+) -> Result<(), ()> {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         parsing_mode,
         quirks_mode,
+        error_reporter,
     );
 
     let mut input = ParserInput::new(input);
     let mut parser = Parser::new(&mut input);
     let start_position = parser.position();
     parser.parse_entirely(|parser| {
         PropertyDeclaration::parse_into(declarations, id, &context, parser)
     }).map_err(|err| {
         let location = err.location;
         let error = ContextualParseError::UnsupportedPropertyDeclaration(
-            parser.slice_from(start_position), err);
-        let error_context = ParserErrorContext { error_reporter: error_reporter };
-        context.log_css_error(&error_context, location, error);
+            parser.slice_from(start_position),
+            err,
+        );
+        context.log_css_error(location, error);
     })
 }
 
 /// A struct to parse property declarations.
 struct PropertyDeclarationParser<'a, 'b: 'a> {
     context: &'a ParserContext<'b>,
     declarations: &'a mut SourcePropertyDeclaration,
 }
@@ -1188,24 +1188,20 @@ impl<'a, 'b, 'i> DeclarationParser<'i> f
         input.expect_exhausted()?;
         Ok(importance)
     }
 }
 
 
 /// Parse a list of property declarations and return a property declaration
 /// block.
-pub fn parse_property_declaration_list<R>(
+pub fn parse_property_declaration_list(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
-) -> PropertyDeclarationBlock
-where
-    R: ParseErrorReporter
-{
+) -> PropertyDeclarationBlock {
     let mut declarations = SourcePropertyDeclaration::new();
     let mut block = PropertyDeclarationBlock::new();
     let parser = PropertyDeclarationParser {
         context: context,
         declarations: &mut declarations,
     };
     let mut iter = DeclarationListParser::new(input, parser);
     while let Some(declaration) = iter.next() {
@@ -1223,14 +1219,14 @@ where
                 // If the unrecognized property looks like a vendor-specific property,
                 // silently ignore it instead of polluting the error output.
                 if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownVendorProperty) = error.kind {
                     continue;
                 }
 
                 let location = error.location;
                 let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error);
-                context.log_css_error(error_context, location, error);
+                context.log_css_error(location, error);
             }
         }
     }
     block
 }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -1437,16 +1437,17 @@ impl UnparsedValue {
             //
             // FIXME(emilio): ParsingMode is slightly fishy...
             let context = ParserContext::new(
                 Origin::Author,
                 &self.url_data,
                 None,
                 ParsingMode::DEFAULT,
                 quirks_mode,
+                None,
             );
 
             let mut input = ParserInput::new(&css);
             Parser::new(&mut input).parse_entirely(|input| {
                 match self.from_shorthand {
                     None => longhand_id.parse_value(&context, input),
                     Some(ShorthandId::All) => {
                         // No need to parse the 'all' shorthand as anything other than a CSS-wide
--- a/servo/components/style/stylesheets/font_feature_values_rule.rs
+++ b/servo/components/style/stylesheets/font_feature_values_rule.rs
@@ -5,22 +5,22 @@
 //! The [`@font-feature-values`][font-feature-values] at-rule.
 //!
 //! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule
 
 use Atom;
 use cssparser::{AtRuleParser, AtRuleType, BasicParseErrorKind, CowRcStr};
 use cssparser::{DeclarationListParser, DeclarationParser, Parser};
 use cssparser::{QualifiedRuleParser, RuleListParser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 #[cfg(feature = "gecko")]
 use gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;
 #[cfg(feature = "gecko")]
 use gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray};
-use parser::{Parse, ParserContext, ParserErrorContext};
+use parser::{Parse, ParserContext};
 use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt::{self, Write};
 use str::CssStringWriter;
 use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
 use stylesheets::CssRuleType;
 use values::computed::font::FamilyName;
 use values::serialize_atom_identifier;
 
@@ -262,37 +262,34 @@ macro_rules! font_feature_values_blocks 
                     $(
                         $ident: vec![],
                     )*
                     source_location: location,
                 }
             }
 
             /// Parses a `FontFeatureValuesRule`.
-            pub fn parse<R>(context: &ParserContext,
-                            error_context: &ParserErrorContext<R>,
-                            input: &mut Parser,
-                            family_names: Vec<FamilyName>,
-                            location: SourceLocation)
-                            -> FontFeatureValuesRule
-                where R: ParseErrorReporter
-            {
+            pub fn parse(
+                context: &ParserContext,
+                input: &mut Parser,
+                family_names: Vec<FamilyName>,
+                location: SourceLocation,
+            ) -> Self {
                 let mut rule = FontFeatureValuesRule::new(family_names, location);
 
                 {
                     let mut iter = RuleListParser::new_for_nested_rule(input, FontFeatureValuesRuleParser {
                         context: context,
-                        error_context: error_context,
                         rule: &mut rule,
                     });
                     while let Some(result) = iter.next() {
                         if let Err((error, slice)) = result {
                             let location = error.location;
                             let error = ContextualParseError::UnsupportedRule(slice, error);
-                            context.log_css_error(error_context, location, error);
+                            context.log_css_error(location, error);
                         }
                     }
                 }
                 rule
             }
 
             /// Prints font family names.
             pub fn font_family_to_css<W>(
@@ -393,30 +390,29 @@ macro_rules! font_feature_values_blocks 
         }
 
         /// Parser for `FontFeatureValuesRule`. Parses all blocks
         /// <feature-type> {
         ///   <feature-value-declaration-list>
         /// }
         /// <feature-type> = @stylistic | @historical-forms | @styleset |
         /// @character-variant | @swash | @ornaments | @annotation
-        struct FontFeatureValuesRuleParser<'a, R: 'a> {
+        struct FontFeatureValuesRuleParser<'a> {
             context: &'a ParserContext<'a>,
-            error_context: &'a ParserErrorContext<'a, R>,
             rule: &'a mut FontFeatureValuesRule,
         }
 
         /// Default methods reject all qualified rules.
-        impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
+        impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
             type Prelude = ();
             type QualifiedRule = ();
             type Error = StyleParseErrorKind<'i>;
         }
 
-        impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a, R> {
+        impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {
             type PreludeNoBlock = ();
             type PreludeBlock = BlockType;
             type AtRule = ();
             type Error = StyleParseErrorKind<'i>;
 
             fn parse_prelude<'t>(&mut self,
                                  name: CowRcStr<'i>,
                                  input: &mut Parser<'i, 't>)
@@ -445,17 +441,17 @@ macro_rules! font_feature_values_blocks 
 
                             let mut iter = DeclarationListParser::new(input, parser);
                             while let Some(declaration) = iter.next() {
                                 if let Err((error, slice)) = declaration {
                                     let location = error.location;
                                     let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration(
                                         slice, error
                                     );
-                                    self.context.log_css_error(self.error_context, location, error);
+                                    self.context.log_css_error(location, error);
                                 }
                             }
                         },
                     )*
                 }
 
                 Ok(())
             }
--- a/servo/components/style/stylesheets/keyframes_rule.rs
+++ b/servo/components/style/stylesheets/keyframes_rule.rs
@@ -1,18 +1,18 @@
 /* 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/. */
 
 //! Keyframes: https://drafts.csswg.org/css-animations/#keyframes
 
 use cssparser::{AtRuleParser, CowRcStr, Parser, ParserInput, QualifiedRuleParser, RuleListParser};
 use cssparser::{parse_one_rule, DeclarationListParser, DeclarationParser, SourceLocation, Token};
-use error_reporting::{ContextualParseError, NullReporter, ParseErrorReporter};
-use parser::{ParserContext, ParserErrorContext};
+use error_reporting::ContextualParseError;
+use parser::ParserContext;
 use properties::{DeclarationSource, Importance, PropertyDeclaration};
 use properties::{LonghandId, PropertyDeclarationBlock, PropertyId};
 use properties::{PropertyDeclarationId, SourcePropertyDeclaration};
 use properties::LonghandIdSet;
 use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
 use shared_lock::{Locked, ToCssWithGuard};
@@ -206,36 +206,32 @@ impl ToCssWithGuard for Keyframe {
 impl Keyframe {
     /// Parse a CSS keyframe.
     pub fn parse<'i>(
         css: &'i str,
         parent_stylesheet_contents: &StylesheetContents,
         lock: &SharedRwLock,
     ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {
         let url_data = parent_stylesheet_contents.url_data.read();
-        let error_reporter = NullReporter;
         let namespaces = parent_stylesheet_contents.namespaces.read();
         let mut context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             Some(CssRuleType::Keyframe),
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
+            None,
         );
-        let error_context = ParserErrorContext {
-            error_reporter: &error_reporter,
-        };
         context.namespaces = Some(&*namespaces);
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut declarations = SourcePropertyDeclaration::new();
         let mut rule_parser = KeyframeListParser {
             context: &context,
-            error_context: &error_context,
             shared_lock: &lock,
             declarations: &mut declarations,
         };
         parse_one_rule(&mut input, &mut rule_parser)
     }
 }
 
 impl DeepCloneWithLock for Keyframe {
@@ -472,65 +468,59 @@ impl KeyframesAnimation {
 /// Parses a keyframes list, like:
 /// 0%, 50% {
 ///     width: 50%;
 /// }
 ///
 /// 40%, 60%, 100% {
 ///     width: 100%;
 /// }
-struct KeyframeListParser<'a, R: 'a> {
+struct KeyframeListParser<'a> {
     context: &'a ParserContext<'a>,
-    error_context: &'a ParserErrorContext<'a, R>,
     shared_lock: &'a SharedRwLock,
     declarations: &'a mut SourcePropertyDeclaration,
 }
 
 /// Parses a keyframe list from CSS input.
-pub fn parse_keyframe_list<R>(
+pub fn parse_keyframe_list(
     context: &ParserContext,
-    error_context: &ParserErrorContext<R>,
     input: &mut Parser,
     shared_lock: &SharedRwLock,
-) -> Vec<Arc<Locked<Keyframe>>>
-where
-    R: ParseErrorReporter,
-{
+) -> Vec<Arc<Locked<Keyframe>>> {
     debug_assert!(
         context.namespaces.is_some(),
         "Parsing a keyframe list from a context without namespaces?"
     );
 
     let mut declarations = SourcePropertyDeclaration::new();
     RuleListParser::new_for_nested_rule(
         input,
         KeyframeListParser {
             context: context,
-            error_context: error_context,
             shared_lock: shared_lock,
             declarations: &mut declarations,
         },
     ).filter_map(Result::ok)
         .collect()
 }
 
-impl<'a, 'i, R> AtRuleParser<'i> for KeyframeListParser<'a, R> {
+impl<'a, 'i> AtRuleParser<'i> for KeyframeListParser<'a> {
     type PreludeNoBlock = ();
     type PreludeBlock = ();
     type AtRule = Arc<Locked<Keyframe>>;
     type Error = StyleParseErrorKind<'i>;
 }
 
 /// A wrapper to wraps the KeyframeSelector with its source location
 struct KeyframeSelectorParserPrelude {
     selector: KeyframeSelector,
     source_location: SourceLocation,
 }
 
-impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for KeyframeListParser<'a, R> {
+impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
     type Prelude = KeyframeSelectorParserPrelude;
     type QualifiedRule = Arc<Locked<Keyframe>>;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self::Prelude, ParseError<'i>> {
@@ -542,18 +532,17 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
                 source_location: start_location,
             }),
             Err(e) => {
                 let location = e.location;
                 let error = ContextualParseError::InvalidKeyframeRule(
                     input.slice_from(start_position),
                     e.clone(),
                 );
-                self.context
-                    .log_css_error(self.error_context, location, error);
+                self.context.log_css_error(location, error);
                 Err(e)
             },
         }
     }
 
     fn parse_block<'t>(
         &mut self,
         prelude: Self::Prelude,
@@ -580,17 +569,17 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
                         DeclarationSource::Parsing,
                     );
                 },
                 Err((error, slice)) => {
                     iter.parser.declarations.clear();
                     let location = error.location;
                     let error =
                         ContextualParseError::UnsupportedKeyframePropertyDeclaration(slice, error);
-                    context.log_css_error(self.error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
             // `parse_important` is not called here, `!important` is not allowed in keyframe blocks.
         }
         Ok(Arc::new(self.shared_lock.wrap(Keyframe {
             selector: prelude.selector,
             block: Arc::new(self.shared_lock.wrap(block)),
             source_location: prelude.source_location,
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -19,20 +19,19 @@ mod rule_list;
 mod rule_parser;
 mod rules_iterator;
 mod style_rule;
 mod stylesheet;
 pub mod supports_rule;
 pub mod viewport_rule;
 
 use cssparser::{parse_one_rule, Parser, ParserInput};
-use error_reporting::NullReporter;
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
 use shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
 use std::fmt;
 use str::CssStringWriter;
 use style_traits::ParsingMode;
 
 pub use self::counter_style_rule::CounterStyleRule;
@@ -223,37 +222,34 @@ impl CssRule {
         css: &str,
         insert_rule_context: InsertRuleContext,
         parent_stylesheet_contents: &StylesheetContents,
         shared_lock: &SharedRwLock,
         state: State,
         loader: Option<&StylesheetLoader>,
     ) -> Result<Self, RulesMutateError> {
         let url_data = parent_stylesheet_contents.url_data.read();
-        let error_reporter = NullReporter;
         let context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             None,
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
+            None,
         );
 
         let mut input = ParserInput::new(css);
         let mut input = Parser::new(&mut input);
 
         let mut guard = parent_stylesheet_contents.namespaces.write();
 
         // nested rules are in the body state
         let mut rule_parser = TopLevelRuleParser {
             stylesheet_origin: parent_stylesheet_contents.origin,
             context,
-            error_context: ParserErrorContext {
-                error_reporter: &error_reporter,
-            },
             shared_lock: &shared_lock,
             loader,
             state,
             dom_error: None,
             namespaces: &mut *guard,
             insert_rule_context: Some(insert_rule_context),
         };
 
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -3,20 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Parsing of the stylesheet contents.
 
 use {Namespace, Prefix};
 use counter_style::{parse_counter_style_body, parse_counter_style_name_definition};
 use cssparser::{AtRuleParser, AtRuleType, Parser, QualifiedRuleParser, RuleListParser};
 use cssparser::{BasicParseError, BasicParseErrorKind, CowRcStr, SourceLocation};
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 use font_face::parse_font_face_block;
 use media_queries::MediaList;
-use parser::{Parse, ParserContext, ParserErrorContext};
+use parser::{Parse, ParserContext};
 use properties::parse_property_declaration_list;
 use selector_parser::{SelectorImpl, SelectorParser};
 use selectors::SelectorList;
 use servo_arc::Arc;
 use shared_lock::{Locked, SharedRwLock};
 use str::starts_with_ignore_ascii_case;
 use style_traits::{ParseError, StyleParseErrorKind};
 use stylesheets::{CssRule, CssRuleType, CssRules, Origin, RulesMutateError, StylesheetLoader};
@@ -35,51 +35,48 @@ use values::computed::font::FamilyName;
 pub struct InsertRuleContext<'a> {
     /// The rule list we're about to insert into.
     pub rule_list: &'a [CssRule],
     /// The index we're about to get inserted at.
     pub index: usize,
 }
 
 /// The parser for the top-level rules in a stylesheet.
-pub struct TopLevelRuleParser<'a, R: 'a> {
+pub struct TopLevelRuleParser<'a> {
     /// The origin of the stylesheet we're parsing.
     pub stylesheet_origin: Origin,
     /// A reference to the lock we need to use to create rules.
     pub shared_lock: &'a SharedRwLock,
     /// A reference to a stylesheet loader if applicable, for `@import` rules.
     pub loader: Option<&'a StylesheetLoader>,
     /// The top-level parser context.
     ///
     /// This won't contain any namespaces, and only nested parsers created with
     /// `ParserContext::new_with_rule_type` will.
     pub context: ParserContext<'a>,
-    /// The context required for reporting parse errors.
-    pub error_context: ParserErrorContext<'a, R>,
     /// The current state of the parser.
     pub state: State,
     /// Whether we have tried to parse was invalid due to being in the wrong
     /// place (e.g. an @import rule was found while in the `Body` state). Reset
     /// to `false` when `take_had_hierarchy_error` is called.
     pub dom_error: Option<RulesMutateError>,
     /// The namespace map we use for parsing. Needs to start as `Some()`, and
     /// will be taken out after parsing namespace rules, and that reference will
     /// be moved to `ParserContext`.
     pub namespaces: &'a mut Namespaces,
     /// The info we need insert a rule in a list.
     pub insert_rule_context: Option<InsertRuleContext<'a>>,
 }
 
-impl<'b, R> TopLevelRuleParser<'b, R> {
-    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b, R> {
+impl<'b> TopLevelRuleParser<'b> {
+    fn nested<'a: 'b>(&'a self) -> NestedRuleParser<'a, 'b> {
         NestedRuleParser {
             stylesheet_origin: self.stylesheet_origin,
             shared_lock: self.shared_lock,
             context: &self.context,
-            error_context: &self.error_context,
             namespaces: &self.namespaces,
         }
     }
 
     /// Returns the current state of the parser.
     pub fn state(&self) -> State {
         self.state
     }
@@ -171,17 +168,17 @@ pub enum AtRuleBlockPrelude {
 /// A rule prelude for at-rule without block.
 pub enum AtRuleNonBlockPrelude {
     /// A @import rule prelude.
     Import(CssUrl, Arc<Locked<MediaList>>, SourceLocation),
     /// A @namespace rule prelude.
     Namespace(Option<Prefix>, Namespace, SourceLocation),
 }
 
-impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a, R> {
+impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
@@ -192,21 +189,17 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
             "import" => {
                 if !self.check_state(State::Imports) {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
                 }
 
                 let url_string = input.expect_url_or_string()?.as_ref().to_owned();
                 let url = CssUrl::parse_from_string(url_string, &self.context);
 
-                let media = MediaList::parse(
-                    &self.context,
-                    input,
-                    self.error_context.error_reporter,
-                );
+                let media = MediaList::parse(&self.context, input);
                 let media = Arc::new(self.shared_lock.wrap(media));
 
                 let prelude = AtRuleNonBlockPrelude::Import(url, media, location);
                 return Ok(AtRuleType::WithoutBlock(prelude));
             },
             "namespace" => {
                 if !self.check_state(State::Namespaces) {
                     return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
@@ -291,17 +284,17 @@ impl<'a, 'i, R: ParseErrorReporter> AtRu
     }
 }
 
 pub struct QualifiedRuleParserPrelude {
     selectors: SelectorList<SelectorImpl>,
     source_location: SourceLocation,
 }
 
-impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, R> {
+impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     #[inline]
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
@@ -322,77 +315,74 @@ impl<'a, 'i, R: ParseErrorReporter> Qual
         QualifiedRuleParser::parse_block(&mut self.nested(), prelude, input).map(|result| {
             self.state = State::Body;
             result
         })
     }
 }
 
 #[derive(Clone)] // shallow, relatively cheap .clone
-struct NestedRuleParser<'a, 'b: 'a, R: 'b> {
+struct NestedRuleParser<'a, 'b: 'a> {
     stylesheet_origin: Origin,
     shared_lock: &'a SharedRwLock,
     context: &'a ParserContext<'b>,
-    error_context: &'a ParserErrorContext<'b, R>,
     namespaces: &'a Namespaces,
 }
 
-impl<'a, 'b, R: ParseErrorReporter> NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b> NestedRuleParser<'a, 'b> {
     fn parse_nested_rules(
         &mut self,
         input: &mut Parser,
         rule_type: CssRuleType,
     ) -> Arc<Locked<CssRules>> {
-        let context = ParserContext::new_with_rule_type(self.context, rule_type, self.namespaces);
+        let context = ParserContext::new_with_rule_type(
+            self.context,
+            rule_type,
+            self.namespaces,
+        );
 
         let nested_parser = NestedRuleParser {
             stylesheet_origin: self.stylesheet_origin,
             shared_lock: self.shared_lock,
             context: &context,
-            error_context: &self.error_context,
             namespaces: self.namespaces,
         };
 
         let mut iter = RuleListParser::new_for_nested_rule(input, nested_parser);
         let mut rules = Vec::new();
         while let Some(result) = iter.next() {
             match result {
                 Ok(rule) => rules.push(rule),
                 Err((error, slice)) => {
                     let location = error.location;
                     let error = ContextualParseError::InvalidRule(slice, error);
-                    self.context
-                        .log_css_error(self.error_context, location, error);
+                    self.context.log_css_error(location, error);
                 },
             }
         }
         CssRules::new(rules, self.shared_lock)
     }
 }
 
-impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
     type PreludeNoBlock = AtRuleNonBlockPrelude;
     type PreludeBlock = AtRuleBlockPrelude;
     type AtRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         name: CowRcStr<'i>,
         input: &mut Parser<'i, 't>,
     ) -> Result<AtRuleType<AtRuleNonBlockPrelude, AtRuleBlockPrelude>, ParseError<'i>> {
         let location = input.current_source_location();
 
         match_ignore_ascii_case! { &*name,
             "media" => {
-                let media_queries = MediaList::parse(
-                    self.context,
-                    input,
-                    self.error_context.error_reporter,
-                );
+                let media_queries = MediaList::parse(self.context, input);
                 let arc = Arc::new(self.shared_lock.wrap(media_queries));
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Media(arc, location)))
             },
             "supports" => {
                 let cond = SupportsCondition::parse(input)?;
                 Ok(AtRuleType::WithBlock(AtRuleBlockPrelude::Supports(cond, location)))
             },
             "font-face" => {
@@ -468,30 +458,29 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
             AtRuleBlockPrelude::FontFace(location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::FontFace,
                     self.namespaces,
                 );
 
                 Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
-                    parse_font_face_block(&context, self.error_context, input, location).into(),
+                    parse_font_face_block(&context, input, location).into(),
                 ))))
             },
             AtRuleBlockPrelude::FontFeatureValues(family_names, location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::FontFeatureValues,
                     self.namespaces,
                 );
 
                 Ok(CssRule::FontFeatureValues(Arc::new(self.shared_lock.wrap(
                     FontFeatureValuesRule::parse(
                         &context,
-                        self.error_context,
                         input,
                         family_names,
                         location,
                     ),
                 ))))
             },
             AtRuleBlockPrelude::CounterStyle(name, location) => {
                 let context = ParserContext::new_with_rule_type(
@@ -500,17 +489,16 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
                     self.namespaces,
                 );
 
                 Ok(CssRule::CounterStyle(Arc::new(
                     self.shared_lock.wrap(
                         parse_counter_style_body(
                             name,
                             &context,
-                            self.error_context,
                             input,
                             location,
                         )?.into(),
                     ),
                 )))
             },
             AtRuleBlockPrelude::Media(media_queries, source_location) => {
                 Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
@@ -539,49 +527,48 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
             AtRuleBlockPrelude::Viewport => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Viewport,
                     self.namespaces,
                 );
 
                 Ok(CssRule::Viewport(Arc::new(self.shared_lock.wrap(
-                    ViewportRule::parse(&context, self.error_context, input)?,
+                    ViewportRule::parse(&context, input)?,
                 ))))
             },
             AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Keyframes,
                     self.namespaces,
                 );
 
                 Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(
                     KeyframesRule {
                         name,
                         keyframes: parse_keyframe_list(
                             &context,
-                            self.error_context,
                             input,
                             self.shared_lock,
                         ),
                         vendor_prefix,
                         source_location,
                     },
                 ))))
             },
             AtRuleBlockPrelude::Page(source_location) => {
                 let context = ParserContext::new_with_rule_type(
                     self.context,
                     CssRuleType::Page,
                     self.namespaces,
                 );
 
                 let declarations =
-                    parse_property_declaration_list(&context, self.error_context, input);
+                    parse_property_declaration_list(&context, input);
                 Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
                     block: Arc::new(self.shared_lock.wrap(declarations)),
                     source_location,
                 }))))
             },
             AtRuleBlockPrelude::Document(condition, source_location) => {
                 if !cfg!(feature = "gecko") {
                     unreachable!()
@@ -593,17 +580,17 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
                         source_location,
                     },
                 ))))
             },
         }
     }
 }
 
-impl<'a, 'b, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, R> {
+impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
     type Prelude = QualifiedRuleParserPrelude;
     type QualifiedRule = CssRule;
     type Error = StyleParseErrorKind<'i>;
 
     fn parse_prelude<'t>(
         &mut self,
         input: &mut Parser<'i, 't>,
     ) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
@@ -622,16 +609,16 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> 
     fn parse_block<'t>(
         &mut self,
         prelude: QualifiedRuleParserPrelude,
         input: &mut Parser<'i, 't>,
     ) -> Result<CssRule, ParseError<'i>> {
         let context =
             ParserContext::new_with_rule_type(self.context, CssRuleType::Style, self.namespaces);
 
-        let declarations = parse_property_declaration_list(&context, self.error_context, input);
+        let declarations = parse_property_declaration_list(&context, input);
         Ok(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule {
             selectors: prelude.selectors,
             block: Arc::new(self.shared_lock.wrap(declarations)),
             source_location: prelude.source_location,
         }))))
     }
 }
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -8,17 +8,17 @@ use cssparser::{Parser, ParserInput, Rul
 use error_reporting::{ContextualParseError, ParseErrorReporter};
 use fallible::FallibleVec;
 use fnv::FnvHashMap;
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 #[cfg(feature = "gecko")]
 use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
 use media_queries::{Device, MediaList};
 use parking_lot::RwLock;
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use servo_arc::Arc;
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
 use std::mem;
 use std::sync::atomic::{AtomicBool, Ordering};
 use style_traits::ParsingMode;
 use stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
 use stylesheets::loader::StylesheetLoader;
 use stylesheets::rule_parser::{State, TopLevelRuleParser};
@@ -64,23 +64,23 @@ pub struct StylesheetContents {
     pub source_map_url: RwLock<Option<String>>,
     /// This stylesheet's source URL.
     pub source_url: RwLock<Option<String>>,
 }
 
 impl StylesheetContents {
     /// Parse a given CSS string, with a given url-data, origin, and
     /// quirks mode.
-    pub fn from_str<R: ParseErrorReporter>(
+    pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> Self {
         let namespaces = RwLock::new(Namespaces::default());
         let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
             css,
             &url_data,
             origin,
@@ -132,17 +132,17 @@ impl DeepCloneWithLock for StylesheetCon
 
         Self {
             rules: Arc::new(lock.wrap(rules)),
             quirks_mode: self.quirks_mode,
             origin: self.origin,
             url_data: RwLock::new((*self.url_data.read()).clone()),
             namespaces: RwLock::new((*self.namespaces.read()).clone()),
             source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
-            source_url: RwLock::new((*self.source_map_url.read()).clone()),
+            source_url: RwLock::new((*self.source_url.read()).clone()),
         }
     }
 }
 
 /// The structure servo uses to represent a stylesheet.
 #[derive(Debug)]
 pub struct Stylesheet {
     /// The contents of this stylesheet.
@@ -301,28 +301,26 @@ impl StylesheetInDocument for DocumentSt
     #[inline]
     fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
         self.0.rules(guard)
     }
 }
 
 impl Stylesheet {
     /// Updates an empty stylesheet from a given string of text.
-    pub fn update_from_str<R>(
+    pub fn update_from_str(
         existing: &Stylesheet,
         css: &str,
         url_data: UrlExtraData,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         line_number_offset: u32,
-    ) where
-        R: ParseErrorReporter,
-    {
+    ) {
         let namespaces = RwLock::new(Namespaces::default());
-        let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
+        let (rules, source_map_url, source_url) = Self::parse_rules(
             css,
             &url_data,
             existing.contents.origin,
             &mut *namespaces.write(),
             &existing.shared_lock,
             stylesheet_loader,
             error_reporter,
             existing.contents.quirks_mode,
@@ -337,41 +335,45 @@ impl Stylesheet {
 
         // Acquire the lock *after* parsing, to minimize the exclusive section.
         let mut guard = existing.shared_lock.write();
         *existing.contents.rules.write_with(&mut guard) = CssRules(rules);
         *existing.contents.source_map_url.write() = source_map_url;
         *existing.contents.source_url.write() = source_url;
     }
 
-    fn parse_rules<R: ParseErrorReporter>(
+    fn parse_rules(
         css: &str,
         url_data: &UrlExtraData,
         origin: Origin,
         namespaces: &mut Namespaces,
         shared_lock: &SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> (Vec<CssRule>, Option<String>, Option<String>) {
         let mut rules = Vec::new();
         let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
         let mut input = Parser::new(&mut input);
 
-        let context = ParserContext::new(origin, url_data, None, ParsingMode::DEFAULT, quirks_mode);
-
-        let error_context = ParserErrorContext { error_reporter };
+        let context = ParserContext::new(
+            origin,
+            url_data,
+            None,
+            ParsingMode::DEFAULT,
+            quirks_mode,
+            error_reporter,
+        );
 
         let rule_parser = TopLevelRuleParser {
             stylesheet_origin: origin,
             shared_lock,
             loader: stylesheet_loader,
             context,
-            error_context,
             state: State::Start,
             dom_error: None,
             insert_rule_context: None,
             namespaces,
         };
 
         {
             let mut iter = RuleListParser::new_for_stylesheet(&mut input, rule_parser);
@@ -385,17 +387,16 @@ impl Stylesheet {
                         if rules.try_push(rule).is_err() {
                             break;
                         }
                     },
                     Err((error, slice)) => {
                         let location = error.location;
                         let error = ContextualParseError::InvalidRule(slice, error);
                         iter.parser.context.log_css_error(
-                            &iter.parser.error_context,
                             location,
                             error,
                         );
                     },
                 }
             }
         }
 
@@ -404,27 +405,27 @@ impl Stylesheet {
         (rules, source_map_url, source_url)
     }
 
     /// Creates an empty stylesheet and parses it with a given base url, origin
     /// and media.
     ///
     /// Effectively creates a new stylesheet and forwards the hard work to
     /// `Stylesheet::update_from_str`.
-    pub fn from_str<R: ParseErrorReporter>(
+    pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         media: Arc<Locked<MediaList>>,
         shared_lock: SharedRwLock,
         stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: &R,
+        error_reporter: Option<&ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
-    ) -> Stylesheet {
+    ) -> Self {
         let contents = StylesheetContents::from_str(
             css,
             url_data,
             origin,
             &shared_lock,
             stylesheet_loader,
             error_reporter,
             quirks_mode,
--- a/servo/components/style/stylesheets/viewport_rule.rs
+++ b/servo/components/style/stylesheets/viewport_rule.rs
@@ -6,21 +6,21 @@
 //!
 //! [at]: https://drafts.csswg.org/css-device-adapt/#atviewport-rule
 //! [meta]: https://drafts.csswg.org/css-device-adapt/#viewport-meta
 
 use app_units::Au;
 use context::QuirksMode;
 use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
 use cssparser::CowRcStr;
-use error_reporting::{ContextualParseError, ParseErrorReporter};
+use error_reporting::ContextualParseError;
 use euclid::TypedSize2D;
 use font_metrics::get_metrics_provider_for_product;
 use media_queries::Device;
-use parser::{ParserContext, ParserErrorContext};
+use parser::ParserContext;
 use properties::StyleBuilder;
 use rule_cache::RuleCacheConditions;
 use selectors::parser::SelectorParseErrorKind;
 use shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard};
 use std::borrow::Cow;
 use std::cell::RefCell;
 use std::fmt::{self, Write};
 use std::iter::Enumerate;
@@ -350,42 +350,40 @@ const SEPARATOR: &'static [char] = &['\x
 
 #[inline]
 fn is_whitespace_separator_or_equals(c: &char) -> bool {
     WHITESPACE.contains(c) || SEPARATOR.contains(c) || *c == '='
 }
 
 impl ViewportRule {
     /// Parse a single @viewport rule.
-    pub fn parse<'i, 't, R>(
+    ///
+    /// TODO(emilio): This could use the `Parse` trait now.
+    pub fn parse<'i, 't>(
         context: &ParserContext,
-        error_context: &ParserErrorContext<R>,
         input: &mut Parser<'i, 't>,
-    ) -> Result<Self, ParseError<'i>>
-    where
-        R: ParseErrorReporter,
-    {
-        let parser = ViewportRuleParser { context: context };
+    ) -> Result<Self, ParseError<'i>> {
+        let parser = ViewportRuleParser { context };
 
         let mut cascade = Cascade::new();
         let mut parser = DeclarationListParser::new(input, parser);
         while let Some(result) = parser.next() {
             match result {
                 Ok(declarations) => {
                     for declarations in declarations {
                         cascade.add(Cow::Owned(declarations))
                     }
                 },
                 Err((error, slice)) => {
                     let location = error.location;
                     let error = ContextualParseError::UnsupportedViewportDescriptorDeclaration(
                         slice,
                         error,
                     );
-                    context.log_css_error(error_context, location, error);
+                    context.log_css_error(location, error);
                 },
             }
         }
         Ok(ViewportRule {
             declarations: cascade.finish(),
         })
     }
 }
--- a/servo/ports/geckolib/error_reporter.rs
+++ b/servo/ports/geckolib/error_reporter.rs
@@ -4,55 +4,57 @@
 
 //! Wrapper around Gecko's CSS error reporting mechanism.
 
 #![allow(unsafe_code)]
 
 use cssparser::{CowRcStr, serialize_identifier, ToCss};
 use cssparser::{SourceLocation, ParseError, ParseErrorKind, Token, BasicParseErrorKind};
 use selectors::parser::SelectorParseErrorKind;
-use std::cell::Cell;
 use std::ffi::CStr;
 use std::ptr;
 use style::error_reporting::{ParseErrorReporter, ContextualParseError};
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::structs::{Loader, StyleSheet as DomStyleSheet, nsIURI};
 use style::gecko_bindings::structs::URLExtraData as RawUrlExtraData;
 use style::stylesheets::UrlExtraData;
 use style_traits::{StyleParseErrorKind, ValueParseErrorKind};
 
 pub type ErrorKind<'i> = ParseErrorKind<'i, StyleParseErrorKind<'i>>;
 
 /// An error reporter with all the data we need to report errors.
 pub struct ErrorReporter {
     sheet: *const DomStyleSheet,
     loader: *const Loader,
     uri: *mut nsIURI,
-    cached_error_reporting_enabled: Cell<Option<bool>>,
 }
 
 impl ErrorReporter {
-    /// Create a new instance of the Gecko error reporter.
+    /// Create a new instance of the Gecko error reporter, if error reporting is
+    /// enabled.
     pub fn new(
         sheet: *mut DomStyleSheet,
         loader: *mut Loader,
         extra_data: *mut RawUrlExtraData,
-    ) -> Self {
+    ) -> Option<Self> {
+        if !Self::reporting_enabled(sheet, loader) {
+            return None;
+        }
+
         let uri = unsafe {
             extra_data.as_ref()
                 .map(|d| d.mBaseURI.raw::<nsIURI>())
                 .unwrap_or(ptr::null_mut())
         };
 
-        ErrorReporter {
+        Some(ErrorReporter {
             sheet,
             loader,
             uri,
-            cached_error_reporting_enabled: Cell::new(None),
-        }
+        })
     }
 }
 
 enum ErrorString<'a> {
     Snippet(CowRcStr<'a>),
     Ident(CowRcStr<'a>),
     UnexpectedToken(Token<'a>),
 }
@@ -388,31 +390,25 @@ impl<'a> ErrorHelpers<'a> for Contextual
                 }
             }
         };
         (None, msg, action)
     }
 }
 
 impl ErrorReporter {
-    fn reporting_enabled(&self) -> bool {
-        if let Some(enabled) = self.cached_error_reporting_enabled.get() {
-            return enabled;
-        }
-        let enabled = unsafe {
-            bindings::Gecko_ErrorReportingEnabled(self.sheet, self.loader)
-        };
-        self.cached_error_reporting_enabled.set(Some(enabled));
-        enabled
+    fn reporting_enabled(
+        sheet: *const DomStyleSheet,
+        loader: *const Loader,
+    ) -> bool {
+        unsafe { bindings::Gecko_ErrorReportingEnabled(sheet, loader) }
     }
 
     pub fn report(&self, location: SourceLocation, error: ContextualParseError) {
-        if !self.reporting_enabled() {
-            return;
-        }
+        debug_assert!(Self::reporting_enabled(self.sheet, self.loader));
 
         let (pre, name, action) = error.to_gecko_message();
         let suffix = match action {
             Action::Nothing => ptr::null(),
             Action::Skip => cstr!("PEDeclSkipped").as_ptr(),
             Action::Drop => cstr!("PEDeclDropped").as_ptr(),
         };
         let params = error.error_params();
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -21,17 +21,17 @@ use style::applicable_declarations::Appl
 use style::author_styles::AuthorStyles;
 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
 use style::context::ThreadLocalStyleContext;
 use style::counter_style;
 use style::data::{ElementStyles, self};
 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
 use style::driver;
 use style::element_state::{DocumentState, ElementState};
-use style::error_reporting::{ContextualParseError, NullReporter, ParseErrorReporter};
+use style::error_reporting::{ContextualParseError, ParseErrorReporter};
 use style::font_metrics::{FontMetricsProvider, get_metrics_provider_for_product};
 use style::gecko::data::{GeckoStyleSheet, PerDocumentStyleData, PerDocumentStyleDataImpl};
 use style::gecko::global_style_data::{GLOBAL_STYLE_DATA, GlobalStyleData, STYLE_THREAD_POOL};
 use style::gecko::restyle_damage::GeckoRestyleDamage;
 use style::gecko::selector_parser::{NonTSPseudoClass, PseudoElement};
 use style::gecko::traversal::RecalcStyleOnly;
 use style::gecko::wrapper::{GeckoElement, GeckoNode};
 use style::gecko_bindings::bindings;
@@ -1150,17 +1150,17 @@ pub extern "C" fn Servo_StyleSheet_Empty
     let shared_lock = &global_style_data.shared_lock;
     Arc::new(
         StylesheetContents::from_str(
             "",
             unsafe { dummy_url_data() }.clone(),
             origin,
             shared_lock,
             /* loader = */ None,
-            &NullReporter,
+            None,
             QuirksMode::NoQuirks,
             0
         )
     ).into_strong()
 }
 
 /// Note: The load_data corresponds to this sheet, and is passed as the parent
 /// load data for child sheet loads. It may be null for certain cases where we
@@ -1191,20 +1191,25 @@ pub extern "C" fn Servo_StyleSheet_FromU
     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
     let loader: Option<&StyleStylesheetLoader> = match loader {
         None => None,
         Some(ref s) => Some(s),
     };
 
 
     Arc::new(StylesheetContents::from_str(
-        input, url_data.clone(), mode_to_origin(mode),
-        &global_style_data.shared_lock, loader, &reporter,
-        quirks_mode.into(), line_number_offset)
-    ).into_strong()
+        input,
+        url_data.clone(),
+        mode_to_origin(mode),
+        &global_style_data.shared_lock,
+        loader,
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        quirks_mode.into(),
+        line_number_offset,
+    )).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
     load_data: *mut SheetLoadDataHolder,
     extra_data: *mut URLExtraData,
     bytes: *const nsACString,
     mode: SheetParsingMode,
@@ -2510,16 +2515,17 @@ pub unsafe extern "C" fn Servo_FontFaceR
     let mut parser = Parser::new(&mut input);
     let url_data = RefPtr::from_ptr_ref(&data);
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     write_locked_arc(rule, |rule: &mut FontFaceRule| {
         macro_rules! to_css_text {
             (
                 valid: [$($v_enum_name:ident => $field:ident,)*]
                 invalid: [$($i_enum_name:ident,)*]
             ) => {
@@ -2719,16 +2725,17 @@ macro_rules! counter_style_descriptors {
             let mut parser = Parser::new(&mut input);
             let url_data = dummy_url_data();
             let context = ParserContext::new(
                 Origin::Author,
                 url_data,
                 Some(CssRuleType::CounterStyle),
                 ParsingMode::DEFAULT,
                 QuirksMode::NoQuirks,
+                None,
             );
 
             write_locked_arc(rule, |rule: &mut CounterStyleRule| {
                 match desc {
                     $(nsCSSCounterDesc::$desc => {
                         match parser.parse_entirely(|i| Parse::parse(&context, i)) {
                             Ok(value) => rule.$setter(value),
                             Err(_) => false,
@@ -3201,28 +3208,25 @@ pub extern "C" fn Servo_StyleSet_Drop(da
 #[no_mangle]
 pub unsafe extern "C" fn Servo_StyleSet_CompatModeChanged(raw_data: RawServoStyleSetBorrowed) {
     let mut data = PerDocumentStyleData::from_ffi(raw_data).borrow_mut();
     let doc =
         &*data.stylist.device().pres_context().mDocument.raw::<nsIDocument>();
     data.stylist.set_quirks_mode(QuirksMode::from(doc.mCompatMode));
 }
 
-fn parse_property_into<R>(
+fn parse_property_into(
     declarations: &mut SourcePropertyDeclaration,
     property_id: PropertyId,
     value: *const nsACString,
     data: *mut URLExtraData,
     parsing_mode: structs::ParsingMode,
     quirks_mode: QuirksMode,
-    reporter: &R
-) -> Result<(), ()>
-where
-    R: ParseErrorReporter
-{
+    reporter: Option<&ParseErrorReporter>,
+) -> Result<(), ()> {
     use style_traits::ParsingMode;
     let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let parsing_mode = ParsingMode::from_bits_truncate(parsing_mode);
 
     parse_one_declaration_into(
         declarations,
         property_id,
@@ -3241,18 +3245,27 @@ pub extern "C" fn Servo_ParseProperty(
     parsing_mode: structs::ParsingMode,
     quirks_mode: nsCompatibility,
     loader: *mut Loader,
 ) -> RawServoDeclarationBlockStrong {
     let id = get_property_id_from_nscsspropertyid!(property,
                                                    RawServoDeclarationBlockStrong::null());
     let mut declarations = SourcePropertyDeclaration::new();
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
-    match parse_property_into(&mut declarations, id, value, data,
-                              parsing_mode, quirks_mode.into(), &reporter) {
+    let result = parse_property_into(
+        &mut declarations,
+        id,
+        value,
+        data,
+        parsing_mode,
+        quirks_mode.into(),
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+    );
+
+    match result {
         Ok(()) => {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let mut block = PropertyDeclarationBlock::new();
             block.extend(
                 declarations.drain(),
                 Importance::Normal,
                 DeclarationSource::CssOm,
             );
@@ -3273,16 +3286,17 @@ pub extern "C" fn Servo_ParseEasing(
     // FIXME Dummy URL data would work fine here.
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     let easing = unsafe { (*easing).to_string() };
     let mut input = ParserInput::new(&easing);
     let mut parser = Parser::new(&mut input);
     let result =
         parser.parse_entirely(|p| transition_timing_function::single_value::parse(&context, p));
     match result {
         Ok(parsed_easing) => {
@@ -3363,17 +3377,17 @@ pub extern "C" fn Servo_ParseStyleAttrib
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let value = unsafe { data.as_ref().unwrap().as_str_unchecked() };
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     Arc::new(global_style_data.shared_lock.wrap(
         parse_style_attribute(
             value,
             url_data,
-            &reporter,
+            reporter.as_ref().map(|r| r as &ParseErrorReporter),
             quirks_mode.into(),
         )
     )).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> RawServoDeclarationBlockStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
@@ -3548,17 +3562,17 @@ fn set_property(
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
     let result = parse_property_into(
         &mut source_declarations,
         property_id,
         value,
         data,
         parsing_mode,
         quirks_mode,
-        &reporter,
+        reporter.as_ref().map(|r| r as &ParseErrorReporter),
     );
 
     if result.is_err() {
         return false;
     }
 
     before_change_closure.invoke();
 
@@ -3752,24 +3766,22 @@ pub unsafe extern "C" fn Servo_MediaList
     };
 
     let context = ParserContext::new(
         origin,
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        // TODO(emilio): Looks like error reporting could be useful here?
+        None,
     );
 
     write_locked_arc(list, |list: &mut MediaList| {
-        *list = MediaList::parse(
-            &context,
-            &mut parser,
-            &NullReporter,
-        );
+        *list = MediaList::parse(&context, &mut parser);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_GetLength(list: RawServoMediaListBorrowed) -> u32 {
     read_locked_arc(list, |list: &MediaList| list.media_queries.len() as u32)
 }
 
@@ -3796,16 +3808,17 @@ pub extern "C" fn Servo_MediaList_Append
 ) {
     let new_medium = unsafe { new_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| {
         list.append_medium(&context, new_medium);
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_MediaList_DeleteMedium(
@@ -3814,16 +3827,17 @@ pub extern "C" fn Servo_MediaList_Delete
 ) -> bool {
     let old_medium = unsafe { old_medium.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new_for_cssom(
         url_data,
         Some(CssRuleType::Media),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     write_locked_arc(list, |list: &mut MediaList| list.delete_medium(&context, old_medium))
 }
 
 macro_rules! get_longhand_from_id {
     ($id:expr) => {
         match PropertyId::from_nscsspropertyid($id) {
             Ok(PropertyId::Longhand(long)) => long,
@@ -4205,16 +4219,17 @@ pub extern "C" fn Servo_DeclarationBlock
     let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
     let string = unsafe { (*value).to_string() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
     let url = SpecifiedImageUrl::parse_from_string(string.into(), &context);
     let decl = PropertyDeclaration::BackgroundImage(BackgroundImage(
         vec![Either::Second(Image::Url(url))]
     ));
     write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
         decls.push(decl, Importance::Normal, DeclarationSource::CssOm);
     });
@@ -4245,17 +4260,17 @@ pub unsafe extern "C" fn Servo_CSSSuppor
     let mut declarations = SourcePropertyDeclaration::new();
     parse_property_into(
         &mut declarations,
         id,
         value,
         DUMMY_URL_DATA,
         structs::ParsingMode_Default,
         QuirksMode::NoQuirks,
-        &NullReporter,
+        None,
     ).is_ok()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_CSSSupports(cond: *const nsACString) -> bool {
     let condition = unsafe { cond.as_ref().unwrap().as_str_unchecked() };
     let mut input = ParserInput::new(&condition);
     let mut input = Parser::new(&mut input);
@@ -4264,16 +4279,17 @@ pub extern "C" fn Servo_CSSSupports(cond
         let url_data = unsafe { dummy_url_data() };
         // NOTE(emilio): The supports API is not associated to any stylesheet,
         // so the fact that there are no namespace map here is fine.
         let context = ParserContext::new_for_cssom(
             url_data,
             Some(CssRuleType::Style),
             ParsingMode::DEFAULT,
             QuirksMode::NoQuirks,
+            None,
         );
 
         cond.eval(&context)
     } else {
         false
     }
 }
 
@@ -5323,40 +5339,41 @@ pub unsafe extern "C" fn Servo_SelectorL
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: RawServoSelectorListOwned) {
     let _ = list.into_box::<::selectors::SelectorList<SelectorImpl>>();
 }
 
 fn parse_color(
     value: &str,
-    error_reporter: Option<&ErrorReporter>,
+    error_reporter: Option<&ParseErrorReporter>,
 ) -> Result<specified::Color, ()> {
     let mut input = ParserInput::new(value);
     let mut parser = Parser::new(&mut input);
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        error_reporter,
     );
 
     let start_position = parser.position();
     parser.parse_entirely(|i| specified::Color::parse(&context, i)).map_err(|err| {
-        if let Some(error_reporter) = error_reporter {
+        if error_reporter.is_some() {
             match err.kind {
                 ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) => {
                     let location = err.location.clone();
                     let error = ContextualParseError::UnsupportedValue(
                         parser.slice_from(start_position),
                         err,
                     );
-                    error_reporter.report(location, error);
+                    context.log_css_error(location, error);
                 }
                 // Ignore other kinds of errors that might be reported, such as
                 // ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken),
                 // since Gecko doesn't report those to the error console.
                 _ => {}
             }
         }
     })
@@ -5380,22 +5397,22 @@ pub extern "C" fn Servo_ComputeColor(
     loader: *mut Loader,
 ) -> bool {
     use style::gecko;
 
     let current_color = gecko::values::convert_nscolor_to_rgba(current_color);
     let value = unsafe { (*value).to_string() };
     let result_color = unsafe { result_color.as_mut().unwrap() };
 
-    let reporter = unsafe { loader.as_mut() }.map(|loader| {
+    let reporter = unsafe { loader.as_mut() }.and_then(|loader| {
         // Make an ErrorReporter that will report errors as being "from DOM".
         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
     });
 
-    match parse_color(&value, reporter.as_ref()) {
+    match parse_color(&value, reporter.as_ref().map(|r| r as &ParseErrorReporter)) {
         Ok(specified_color) => {
             let computed_color = match raw_data {
                 Some(raw_data) => {
                     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
                     let device = data.stylist.device();
                     let quirks_mode = data.stylist.quirks_mode();
                     Context::for_media_query_evaluation(device, quirks_mode, |context| {
                         specified_color.to_computed_color(Some(&context))
@@ -5437,16 +5454,17 @@ pub unsafe extern "C" fn Servo_Intersect
 
     let url_data = dummy_url_data();
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     let margin = parser.parse_entirely(|p| {
         IntersectionObserverRootMargin::parse(&context, p)
     });
     match margin {
         Ok(margin) => {
             margin.0.to_gecko_rect(result);
@@ -5479,17 +5497,18 @@ pub extern "C" fn Servo_ParseTransformIn
     let string = unsafe { (*value).to_string() };
     let mut input = ParserInput::new(&string);
     let mut parser = Parser::new(&mut input);
     let context = ParserContext::new(
         Origin::Author,
         unsafe { dummy_url_data() },
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
-        QuirksMode::NoQuirks
+        QuirksMode::NoQuirks,
+        None,
     );
 
     let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
         Ok(t) => t,
         Err(..) => return false,
     };
 
     let (m, is_3d) = match transform.to_transform_3d_matrix(None) {
@@ -5522,16 +5541,17 @@ pub extern "C" fn Servo_ParseFontShortha
     let mut parser = Parser::new(&mut input);
     let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     let font = match parser.parse_entirely(|f| font::parse_value(&context, f)) {
         Ok(f) => f,
         Err(..) => return false,
     };
 
     // The system font is not acceptable, so we return false.
@@ -5579,16 +5599,17 @@ pub unsafe extern "C" fn Servo_SourceSiz
     let mut parser = Parser::new(&mut input);
 
     let context = ParserContext::new(
         Origin::Author,
         dummy_url_data(),
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
+        None,
     );
 
     // NB: Intentionally not calling parse_entirely.
     let list = SourceSizeList::parse(&context, &mut parser);
     Box::into_raw(Box::new(list)) as *mut _
 }
 
 #[no_mangle]
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use cssparser::SourceLocation;
 use nsstring::nsCString;
 use servo_arc::Arc;
 use style::context::QuirksMode;
-use style::error_reporting::NullReporter;
 use style::gecko::data::GeckoStyleSheet;
 use style::gecko::global_style_data::GLOBAL_STYLE_DATA;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{StyleSheet as DomStyleSheet, SheetLoadData, SheetLoadDataHolder};
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
@@ -99,20 +98,25 @@ impl AsyncStylesheetParser {
     pub fn parse(self) {
         let global_style_data = &*GLOBAL_STYLE_DATA;
         let input: &str = unsafe { (*self.bytes).as_str_unchecked() };
 
         // Note: Parallel CSS parsing doesn't report CSS errors. When errors
         // are being logged, Gecko prevents the parallel parsing path from
         // running.
         let sheet = Arc::new(StylesheetContents::from_str(
-            input, self.extra_data.clone(), self.origin,
-            &global_style_data.shared_lock, Some(&self), &NullReporter,
-            self.quirks_mode.into(), self.line_number_offset)
-        );
+            input,
+            self.extra_data.clone(),
+            self.origin,
+            &global_style_data.shared_lock,
+            Some(&self),
+            None,
+            self.quirks_mode.into(),
+            self.line_number_offset,
+        ));
 
         unsafe {
             bindings::Gecko_StyleSheet_FinishAsyncParse(self.load_data.get(), sheet.into_strong());
         }
     }
 }
 
 impl StyleStylesheetLoader for AsyncStylesheetParser {
--- a/testing/web-platform/tests/custom-elements/Document-createElement.html
+++ b/testing/web-platform/tests/custom-elements/Document-createElement.html
@@ -358,16 +358,41 @@ test(function () {
 
     exceptionToThrow = false;
     var instance = document.createElement('throw-custom-element');
     assert_true(instance instanceof ThrowCustomElement);
     assert_equals(instance.localName, 'throw-custom-element');
 
 }, 'document.createElement must report an exception thrown by a custom element constructor');
 
+test(function () {
+    var exceptionToThrow = {name: 'exception thrown by a custom constructor'};
+    class ThrowCustomBuiltinElement extends HTMLDivElement {
+        constructor()
+        {
+            super();
+            if (exceptionToThrow)
+                throw exceptionToThrow;
+        }
+    };
+    customElements.define('throw-custom-builtin-element', ThrowCustomBuiltinElement, { extends: 'div' });
+
+    assert_throws(exceptionToThrow, function () { new ThrowCustomBuiltinElement; });
+    var instance;
+    assert_reports(exceptionToThrow, function () { instance = document.createElement('div', { is: 'throw-custom-builtin-element' }); });
+    assert_equals(instance.localName, 'div');
+    assert_true(instance instanceof HTMLDivElement);
+
+    exceptionToThrow = false;
+    var instance = document.createElement('div', { is: 'throw-custom-builtin-element' });
+    assert_true(instance instanceof ThrowCustomBuiltinElement);
+    assert_equals(instance.localName, 'div');
+
+}, 'document.createElement must report an exception thrown by a custom built-in element constructor');
+
 test(() => {
   class MyElement extends HTMLDivElement {}
 
   // createElement with unknown 'is' should not throw.
   // https://github.com/w3c/webcomponents/issues/608
   let div = document.createElement('div', { is: 'my-div' });
   assert_false(div instanceof MyElement);
   assert_false(div.hasAttribute('is'));
rename from toolkit/forgetaboutsite/ServiceWorkerCleanUp.jsm
rename to toolkit/components/cleardata/ServiceWorkerCleanUp.jsm
--- a/toolkit/components/cleardata/moz.build
+++ b/toolkit/components/cleardata/moz.build
@@ -10,16 +10,20 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'toolkit_cleardata'
 
 EXTRA_COMPONENTS += [
     'ClearDataService.js',
     'ClearDataService.manifest',
 ]
 
+EXTRA_JS_MODULES += [
+    'ServiceWorkerCleanUp.jsm',
+]
+
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Data Sanitization')
 
 FINAL_LIBRARY = 'xul'
--- a/toolkit/components/clearsitedata/ClearSiteData.cpp
+++ b/toolkit/components/clearsitedata/ClearSiteData.cpp
@@ -130,16 +130,17 @@ ClearSiteData::Initialize()
   RefPtr<ClearSiteData> service = new ClearSiteData();
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (NS_WARN_IF(!obs)) {
     return;
   }
 
   obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
+  obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
   gClearSiteData = service;
 }
 
 /* static */ void
 ClearSiteData::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -151,25 +152,31 @@ ClearSiteData::Shutdown()
   gClearSiteData = nullptr;
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (NS_WARN_IF(!obs)) {
     return;
   }
 
   obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
+  obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 }
 
 ClearSiteData::ClearSiteData() = default;
 ClearSiteData::~ClearSiteData() = default;
 
 NS_IMETHODIMP
 ClearSiteData::Observe(nsISupports* aSubject, const char* aTopic,
                        const char16_t* aData)
 {
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    Shutdown();
+    return NS_OK;
+  }
+
   MOZ_ASSERT(!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC));
 
   // Pref disabled.
   if (!StaticPrefs::dom_clearSiteData_enabled()) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
--- a/toolkit/components/clearsitedata/ClearSiteData.h
+++ b/toolkit/components/clearsitedata/ClearSiteData.h
@@ -20,23 +20,23 @@ class ClearSiteData final : public nsIOb
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   static void
   Initialize();
 
-  static void
-  Shutdown();
-
 private:
   ClearSiteData();
   ~ClearSiteData();
 
+  static void
+  Shutdown();
+
   class PendingCleanupHolder;
 
   // Starts the cleanup if the channel contains the Clear-Site-Data header and
   // if the URI is secure.
   void
   ClearDataFromChannel(nsIHttpChannel* aChannel);
 
   // This method checks if the protocol handler of the URI has the
--- a/toolkit/forgetaboutsite/moz.build
+++ b/toolkit/forgetaboutsite/moz.build
@@ -4,13 +4,12 @@
 # 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/.
 
 BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'ForgetAboutSite.jsm',
-    'ServiceWorkerCleanUp.jsm',
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'Data Sanitization')
--- a/xpcom/string/nsStringIterator.h
+++ b/xpcom/string/nsStringIterator.h
@@ -35,16 +35,19 @@ private:
   // extra baggage.  we should remove mStart and mEnd at some point.
 
   const CharT* mStart;
   const CharT* mEnd;
   const CharT* mPosition;
 
 public:
   nsReadingIterator()
+    : mStart(nullptr)
+    , mEnd(nullptr)
+    , mPosition(nullptr)
   {
   }
   // nsReadingIterator( const nsReadingIterator<CharT>& );                    // auto-generated copy-constructor OK
   // nsReadingIterator<CharT>& operator=( const nsReadingIterator<CharT>& );  // auto-generated copy-assignment operator OK
 
   pointer get() const
   {
     return mPosition;
@@ -137,16 +140,19 @@ private:
   // extra baggage.  we should remove mStart and mEnd at some point.
 
   CharT* mStart;
   CharT* mEnd;
   CharT* mPosition;
 
 public:
   nsWritingIterator()
+    : mStart(nullptr)
+    , mEnd(nullptr)
+    , mPosition(nullptr)
   {
   }
   // nsWritingIterator( const nsWritingIterator<CharT>& );                    // auto-generated copy-constructor OK
   // nsWritingIterator<CharT>& operator=( const nsWritingIterator<CharT>& );  // auto-generated copy-assignment operator OK
 
   pointer get() const
   {
     return mPosition;