Merge mozilla-central to autoland. a=merge CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Fri, 22 Jun 2018 00:51:57 +0300
changeset 477550 dbafc571a6a8d2426e179698f9038a1a3379fa8c
parent 477549 301f7d8a5ba2f6cf0bba236647eb929dd00c6408 (current diff)
parent 477516 27e90ec610a4c8f6b2a73d79cb1a4df38e822e6a (diff)
child 477551 cea130a07d080dfa813697d64bca97c9a08a5809
push id9385
push userdluca@mozilla.com
push dateFri, 22 Jun 2018 15:47:18 +0000
treeherdermozilla-beta@82a9a1027e2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge CLOSED TREE
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/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -182,18 +182,23 @@ LifoAlloc::newChunkWithCapacity(size_t n
             return nullptr;
         }
 
         chunkSize = RoundUpPow2(allocSizeWithCanaries);
     } else {
         chunkSize = defaultChunkSize_;
     }
 
+    bool protect = false;
+#ifdef LIFO_CHUNK_PROTECT
+    protect = protect_;
+#endif
+
     // Create a new BumpChunk, and allocate space for it.
-    BumpChunk result = detail::BumpChunk::newWithCapacity(chunkSize, protect_);
+    BumpChunk result = detail::BumpChunk::newWithCapacity(chunkSize, protect);
     if (!result)
         return nullptr;
     MOZ_ASSERT(result->computedSizeOfIncludingThis() == chunkSize);
     return result;
 }
 
 bool
 LifoAlloc::getOrCreateChunk(size_t n)
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -222,16 +222,19 @@ class BumpChunk : public SingleLinkedLis
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     // Magic number used to check against poisoned values.
     const uintptr_t magic_ : 24;
     static constexpr uintptr_t magicNumber = uintptr_t(0x4c6966);
 #endif
 
 #if defined(DEBUG) || defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
 # define LIFO_CHUNK_PROTECT 1
+#endif
+
+#ifdef LIFO_CHUNK_PROTECT
     // Constant used to know if the current chunk should be protected. This is
     // mainly use to prevent dead-lock in the MemoryProtectionExceptionHandler
     // methods.
     const uintptr_t protect_ : 1;
 #endif
 
     // Poison the memory with memset, in order to catch errors due to
     // use-after-free, with undefinedChunkMemory pattern, or to catch
--- 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/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -3,17 +3,17 @@ linux64/opt:
     index:
         product: firefox
         job-name: linux64-opt
     treeherder:
         platform: linux64/opt
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        max-run-time: 3600
+        max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build check-test update]
         config:
             - builds/releng_base_firefox.py
             - builds/releng_base_linux_64_builds.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
@@ -244,17 +244,17 @@ linux64-base-toolchains/opt:
     index:
         product: firefox
         job-name: linux64-base-toolchains-opt
     treeherder:
         platform: linux64/opt
         symbol: Bb
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        max-run-time: 3600
+        max-run-time: 7200
         env:
             PERFHERDER_EXTRA_OPTIONS: base-toolchains
     run:
         using: mozharness
         actions: [get-secrets build check-test update]
         config:
             - builds/releng_base_firefox.py
             - builds/releng_base_linux_64_builds.py
@@ -304,17 +304,17 @@ linux/opt:
         product: firefox
         job-name: linux-opt
     treeherder:
         platform: linux32/opt
         symbol: B
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
         docker-image: {in-tree: debian7-i386-build}
-        max-run-time: 3600
+        max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build check-test update]
         config:
             - builds/releng_base_firefox.py
             - builds/releng_base_linux_32_builds.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
--- 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;