Backed out 8 changesets (bug 1453011, bug 1452981, bug 1146316) For xpcshell and mochitest failures on multiple files. CLOSED TREE
authorDorel Luca <dluca@mozilla.com>
Fri, 20 Apr 2018 21:40:24 +0300
changeset 468315 30ed797c2454b1f5f259f1c26f85bd7a62380ef5
parent 468314 27df0b6428b644d3a28092ccc6251d2fcfe655f0
child 468316 5cc5665abf82c6b5480145a17e1927ae35118cdd
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1453011, 1452981, 1146316
milestone61.0a1
backs out033299f2733900eb0da9b5b4d814aea39ce552d4
4464997475c15a2db0040658981b6ef1056447c6
cae243fb2a3c6ff03072e36ad0dd0dd2e599e74d
adf56a83131b3a97bfdce574973a8b03959882c9
80abe3305b24b7f2c251ac973a287275a488428f
02178545f255e96f95540dba7668c6a7dda079fd
719f7596c208eab1f2c8a1a250f1a6d2a7304d65
1316c78daeb6fd1883b3c1688c2591ee02c4d3e5
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
Backed out 8 changesets (bug 1453011, bug 1452981, bug 1146316) For xpcshell and mochitest failures on multiple files. CLOSED TREE Backed out changeset 033299f27339 (bug 1453011) Backed out changeset 4464997475c1 (bug 1453011) Backed out changeset cae243fb2a3c (bug 1453011) Backed out changeset adf56a83131b (bug 1453011) Backed out changeset 80abe3305b24 (bug 1452981) Backed out changeset 02178545f255 (bug 1452981) Backed out changeset 719f7596c208 (bug 1146316) Backed out changeset 1316c78daeb6 (bug 1146316)
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/moz.build
dom/base/nsIDOMClassInfo.h
dom/base/nsINode.h
dom/base/nsRange.h
dom/base/nsWrapperCache.cpp
dom/base/nsWrapperCache.h
dom/battery/BatteryManager.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/ToJSValue.h
dom/events/Event.h
dom/html/nsGenericHTMLElement.h
dom/html/nsHTMLDocument.cpp
dom/html/nsHTMLDocument.h
dom/network/Connection.cpp
js/xpconnect/idl/nsIXPCScriptable.idl
js/xpconnect/public/xpc_map_end.h
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/SandboxPrivate.h
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
js/xpconnect/src/moz.build
js/xpconnect/src/qsObjectHelper.h
js/xpconnect/src/xpcObjectHelper.h
js/xpconnect/src/xpcprivate.h
js/xpconnect/wrappers/XrayWrapper.cpp
xpcom/base/nsISupportsImpl.h
xpcom/components/nsIClassInfo.idl
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -108,50 +108,52 @@ WindowNamedPropertiesHandler::getOwnProp
   nsGlobalWindowInner* win = xpc::WindowOrNull(global);
   if (win->Length() > 0) {
     nsCOMPtr<nsPIDOMWindowOuter> childWin = win->GetChildWindow(str);
     if (childWin && ShouldExposeChildWindow(str, childWin)) {
       // We found a subframe of the right name. Shadowing via |var foo| in
       // global scope is still allowed, since |var| only looks up |own|
       // properties. But unqualified shadowing will fail, per-spec.
       JS::Rooted<JS::Value> v(aCx);
-      if (!ToJSValue(aCx, nsGlobalWindowOuter::Cast(childWin), &v)) {
+      if (!WrapObject(aCx, childWin, &v)) {
         return false;
       }
       FillPropertyDescriptor(aDesc, aProxy, 0, v);
       return true;
     }
   }
 
   // The rest of this function is for HTML documents only.
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
   if (!htmlDoc) {
     return true;
   }
   nsHTMLDocument* document = static_cast<nsHTMLDocument*>(htmlDoc.get());
 
-  JS::Rooted<JS::Value> v(aCx);
   Element* element = document->GetElementById(str);
   if (element) {
-    if (!ToJSValue(aCx, element, &v)) {
+    JS::Rooted<JS::Value> v(aCx);
+    if (!WrapObject(aCx, element, &v)) {
       return false;
     }
     FillPropertyDescriptor(aDesc, aProxy, 0, v);
     return true;
   }
 
-  ErrorResult rv;
-  bool found = document->ResolveName(aCx, str, &v, rv);
-  if (rv.MaybeSetPendingException(aCx)) {
+  nsWrapperCache* cache;
+  nsISupports* result = document->ResolveName(str, &cache);
+  if (!result) {
+    return true;
+  }
+
+  JS::Rooted<JS::Value> v(aCx);
+  if (!WrapObject(aCx, result, cache, nullptr, &v)) {
     return false;
   }
-
-  if (found) {
-    FillPropertyDescriptor(aDesc, aProxy, 0, v);
-  }
+  FillPropertyDescriptor(aDesc, aProxy, 0, v);
   return true;
 }
 
 bool
 WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
                                              JS::Handle<JSObject*> aProxy,
                                              JS::Handle<jsid> aId,
                                              JS::Handle<JS::PropertyDescriptor> aDesc,
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -78,16 +78,17 @@ EXPORTS += [
     'nsIContent.h',
     'nsIContentInlines.h',
     'nsIContentIterator.h',
     'nsIContentSerializer.h',
     'nsIdentifierMapEntry.h',
     'nsIDocument.h',
     'nsIDocumentInlines.h',
     'nsIDocumentObserver.h',
+    'nsIDOMClassInfo.h',
     'nsIGlobalObject.h',
     'nsImageLoadingContent.h',
     'nsIMutationObserver.h',
     'nsINode.h',
     'nsINodeList.h',
     'nsIScriptContext.h',
     'nsIScriptGlobalObject.h',
     'nsIScriptObjectPrincipal.h',
new file mode 100644
--- /dev/null
+++ b/dom/base/nsIDOMClassInfo.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsIDOMClassInfo_h___
+#define nsIDOMClassInfo_h___
+
+#include "nsIXPCScriptable.h"
+
+#define DOM_BASE_SCRIPTABLE_FLAGS                                          \
+  (XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |                             \
+   XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |                             \
+   XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE |                           \
+   XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE |                       \
+   XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
+
+#define DEFAULT_SCRIPTABLE_FLAGS                                           \
+  (DOM_BASE_SCRIPTABLE_FLAGS |                                             \
+   XPC_SCRIPTABLE_WANT_RESOLVE |                                           \
+   XPC_SCRIPTABLE_WANT_PRECREATE)
+
+#define DOM_DEFAULT_SCRIPTABLE_FLAGS                                       \
+  (DEFAULT_SCRIPTABLE_FLAGS |                                              \
+   XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |                              \
+   XPC_SCRIPTABLE_CLASSINFO_INTERFACES_ONLY)
+
+#endif /* nsIDOMClassInfo_h___ */
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -2137,9 +2137,15 @@ inline nsINode* NODE_FROM(C& aContent, D
 NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID)
 
 inline nsISupports*
 ToSupports(nsINode* aPointer)
 {
   return aPointer;
 }
 
+inline nsISupports*
+ToCanonicalSupports(nsINode* aPointer)
+{
+  return aPointer;
+}
+
 #endif /* nsINode_h___ */
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -582,14 +582,20 @@ protected:
 
   bool mIsPositioned : 1;
   bool mMaySpanAnonymousSubtrees : 1;
   bool mIsGenerated : 1;
   bool mCalledByJS : 1;
 };
 
 inline nsISupports*
+ToCanonicalSupports(nsRange* aRange)
+{
+  return static_cast<nsIDOMRange*>(aRange);
+}
+
+inline nsISupports*
 ToSupports(nsRange* aRange)
 {
   return static_cast<nsIDOMRange*>(aRange);
 }
 
 #endif /* nsRange_h___ */
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -33,17 +33,17 @@ nsWrapperCache::HoldJSObjects(void* aScr
     CycleCollectedJSRuntime::Get()->NurseryWrapperPreserved(mWrapper);
   }
 }
 
 void
 nsWrapperCache::SetWrapperJSObject(JSObject* aWrapper)
 {
   mWrapper = aWrapper;
-  UnsetWrapperFlags(kWrapperFlagsMask);
+  UnsetWrapperFlags(kWrapperFlagsMask & ~WRAPPER_IS_NOT_DOM_BINDING);
 
   if (aWrapper && !JS::ObjectIsTenured(aWrapper)) {
     CycleCollectedJSRuntime::Get()->NurseryWrapperAdded(this);
   }
 }
 
 void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -58,18 +58,23 @@ static_assert(sizeof(void*) == 4, "Only 
  * a wrapper cache by calling QueryInterface on it. Note that this breaks XPCOM
  * rules a bit (this object doesn't derive from nsISupports).
  *
  * The cache can store objects other than wrappers. We allow wrappers to use a
  * separate JSObject to store their state (mostly expandos). If the wrapper is
  * collected and we want to preserve this state we actually store the state
  * object in the cache.
  *
- * The cache can store 3 types of objects: a DOM binding object (regular JS object or
- * proxy), an nsOuterWindowProxy or an XPCWrappedNative wrapper.
+ * The cache can store 2 types of objects:
+ *
+ *  If WRAPPER_IS_NOT_DOM_BINDING is set (IsDOMBinding() returns false):
+ *    - the JSObject of an XPCWrappedNative wrapper
+ *
+ *  If WRAPPER_IS_NOT_DOM_BINDING is not set (IsDOMBinding() returns true):
+ *    - a DOM binding object (regular JS object or proxy)
  *
  * The finalizer for the wrapper clears the cache.
  *
  * A compacting GC can move the wrapper object. Pointers to moved objects are
  * usually found and updated by tracing the heap, however non-preserved wrappers
  * are weak references and are not traced, so another approach is
  * necessary. Instead a class hook (objectMovedOp) is provided that is called
  * when an object is moved and is responsible for ensuring pointers are
@@ -187,16 +192,21 @@ public:
     }
   }
 
   bool PreservingWrapper() const
   {
     return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
   }
 
+  bool IsDOMBinding() const
+  {
+    return !HasWrapperFlag(WRAPPER_IS_NOT_DOM_BINDING);
+  }
+
   /**
    * Wrap the object corresponding to this wrapper cache. If non-null is
    * returned, the object has already been stored in the wrapper cache.
    */
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) = 0;
 
   /**
    * Returns true if the object has a wrapper that is known live from the point
@@ -326,16 +336,29 @@ protected:
     if (mWrapper) {
       // Set the pointer to a value that will cause a crash if it is
       // dereferenced.
       mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
 private:
+  // Friend declarations for things that need to be able to call
+  // SetIsNotDOMBinding().  The goal is to get rid of all of these, and
+  // SetIsNotDOMBinding() too.  Once that's done, we can remove the
+  // couldBeDOMBinding bits in DoGetOrCreateDOMReflector, as well as any other
+  // consumers of IsDOMBinding().
+  friend class SandboxPrivate;
+  void SetIsNotDOMBinding()
+  {
+    MOZ_ASSERT(!mWrapper && !(GetWrapperFlags() & ~WRAPPER_IS_NOT_DOM_BINDING),
+               "This flag should be set before creating any wrappers.");
+    SetWrapperFlags(WRAPPER_IS_NOT_DOM_BINDING);
+  }
+
   void SetWrapperJSObject(JSObject* aWrapper);
 
   FlagsType GetWrapperFlags() const
   {
     return mFlags & kWrapperFlagsMask;
   }
 
   bool HasWrapperFlag(FlagsType aFlag) const
@@ -373,27 +396,33 @@ private:
    * causes between the native object and the JS object, so it is important that
    * any native object that supports preserving of its wrapper
    * traces/traverses/unlinks the cached JS object (see
    * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER and
    * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
    */
   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
 
-  enum { kWrapperFlagsMask = WRAPPER_BIT_PRESERVED };
+  /**
+   * If this bit is set then the wrapper for the native object is not a DOM
+   * binding.
+   */
+  enum { WRAPPER_IS_NOT_DOM_BINDING = 1 << 1 };
+
+  enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_NOT_DOM_BINDING) };
 
   JSObject* mWrapper;
   FlagsType mFlags;
 protected:
 #ifdef BOOL_FLAGS_ON_WRAPPER_CACHE
   uint32_t mBoolFlags;
 #endif
 };
 
-enum { WRAPPER_CACHE_FLAGS_BITS_USED = 1 };
+enum { WRAPPER_CACHE_FLAGS_BITS_USED = 2 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY                                 \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
     *aInstancePtr = static_cast<nsWrapperCache*>(this);                       \
     return NS_OK;                                                             \
   }
--- a/dom/battery/BatteryManager.cpp
+++ b/dom/battery/BatteryManager.cpp
@@ -8,16 +8,17 @@
 #include <limits>
 #include "BatteryManager.h"
 #include "Constants.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/Hal.h"
 #include "mozilla/dom/BatteryManagerBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
+#include "nsIDOMClassInfo.h"
 #include "nsIDocument.h"
 
 /**
  * We have to use macros here because our leak analysis tool things we are
  * leaking strings when we have |static const nsString|. Sad :(
  */
 #define LEVELCHANGE_EVENT_NAME           NS_LITERAL_STRING("levelchange")
 #define CHARGINGCHANGE_EVENT_NAME        NS_LITERAL_STRING("chargingchange")
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1020,47 +1020,44 @@ CreateInterfaceObjects(JSContext* cx, JS
         *protoCache = nullptr;
       }
       return;
     }
     *constructorCache = interface;
   }
 }
 
-// Only set aAllowNativeWrapper to false if you really know you need it; if in
-// doubt use true. Setting it to false disables security wrappers.
-static bool
+bool
 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
                                          JS::Handle<JSObject*> aScope,
                                          JS::MutableHandle<JS::Value> aRetval,
                                          xpcObjectHelper& aHelper,
                                          const nsIID* aIID,
                                          bool aAllowNativeWrapper)
 {
   js::AssertSameCompartment(aCx, aScope);
   nsresult rv;
   // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
   // on all threads.
   nsWrapperCache *cache = aHelper.GetWrapperCache();
 
-  if (cache) {
+  if (cache && cache->IsDOMBinding()) {
       JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
       if (!obj) {
         obj = cache->WrapObject(aCx, nullptr);
-        if (!obj) {
-          return Throw(aCx, NS_ERROR_UNEXPECTED);
-        }
       }
 
-      if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
+      if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
         return false;
       }
 
-      aRetval.setObject(*obj);
-      return true;
+      if (obj) {
+        aRetval.setObject(*obj);
+        return true;
+      }
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!XPCConvert::NativeInterface2JSObject(aRetval, aHelper, aIID,
                                             aAllowNativeWrapper, &rv)) {
     // I can't tell if NativeInterface2JSObject throws JS exceptions
     // or not.  This is a sloppy stab at the right semantics; the
@@ -1098,17 +1095,17 @@ TryPreserveWrapper(JSObject* obj)
 bool
 InstanceClassHasProtoAtDepth(const js::Class* clasp,
                              uint32_t protoID, uint32_t depth)
 {
   const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
   return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
 }
 
-// Only set allowNativeWrapper to false if you really know you need it; if in
+// Only set allowNativeWrapper to false if you really know you need it, if in
 // doubt use true. Setting it to false disables security wrappers.
 bool
 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
                    xpcObjectHelper& helper, const nsIID* iid,
                    bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
 {
   if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
                                                 allowNativeWrapper)) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -30,17 +30,17 @@
 #include "mozilla/Likely.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsAutoPtr.h"
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsISupportsImpl.h"
-#include "xpcObjectHelper.h"
+#include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
 #include "mozilla/dom/FakeString.h"
 
 #include "nsWrapperCacheInlines.h"
 
 class nsGenericHTMLElement;
 class nsIJSID;
@@ -864,16 +864,28 @@ CheckWrapperCacheCast<T, true>
 {
   static bool Check()
   {
     return true;
   }
 };
 #endif
 
+MOZ_ALWAYS_INLINE bool
+CouldBeDOMBinding(void*)
+{
+  return true;
+}
+
+MOZ_ALWAYS_INLINE bool
+CouldBeDOMBinding(nsWrapperCache* aCache)
+{
+  return aCache->IsDOMBinding();
+}
+
 inline bool
 TryToOuterize(JS::MutableHandle<JS::Value> rval)
 {
   if (js::IsWindow(&rval.toObject())) {
     JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
     MOZ_ASSERT(obj);
     rval.set(JS::ObjectValue(*obj));
   }
@@ -1053,25 +1065,33 @@ AssertReflectorHasGivenProto(JSContext* 
 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
 MOZ_ALWAYS_INLINE bool
 DoGetOrCreateDOMReflector(JSContext* cx, T* value,
                           JS::Handle<JSObject*> givenProto,
                           JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(value);
   MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
+  // We can get rid of this when we remove support for
+  // nsWrapperCache::SetIsNotDOMBinding.
+  bool couldBeDOMBinding = CouldBeDOMBinding(value);
   JSObject* obj = value->GetWrapper();
   if (obj) {
 #ifdef DEBUG
     AssertReflectorHasGivenProto(cx, obj, givenProto);
     // Have to reget obj because AssertReflectorHasGivenProto can
     // trigger gc so the pointer may now be invalid.
     obj = value->GetWrapper();
 #endif
   } else {
+    // Inline this here while we have non-dom objects in wrapper caches.
+    if (!couldBeDOMBinding) {
+      return false;
+    }
+
     obj = value->WrapObject(cx, givenProto);
     if (!obj) {
       // At this point, obj is null, so just return false.
       // Callers seem to be testing JS_IsExceptionPending(cx) to
       // figure out whether WrapObject() threw.
       return false;
     }
 
@@ -1095,17 +1115,19 @@ DoGetOrCreateDOMReflector(JSContext* cx,
     MOZ_ASSERT(clasp, "What happened here?");
     MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, (IsBaseOf<nsISupports, T>::value));
     MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
   }
 #endif
 
   rval.set(JS::ObjectValue(*obj));
 
-  if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
+  bool sameCompartment =
+    js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
+  if (sameCompartment && couldBeDOMBinding) {
     return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
   }
 
   if (wrapBehavior == eDontWrapIntoContextCompartment) {
     if (TypeNeedsOuterization<T>::value) {
       JSAutoCompartment ac(cx, obj);
       return TryToOuterize(rval);
     }
@@ -1274,16 +1296,75 @@ WrapNewBindingNonWrapperCachedObject(JSC
                                      T& value,
                                      JS::MutableHandle<JS::Value> rval,
                                      JS::Handle<JSObject*> givenProto = nullptr)
 {
   return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
                                               givenProto);
 }
 
+// Only set allowNativeWrapper to false if you really know you need it, if in
+// doubt use true. Setting it to false disables security wrappers.
+bool
+NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
+                                         JS::Handle<JSObject*> aScope,
+                                         JS::MutableHandle<JS::Value> aRetval,
+                                         xpcObjectHelper& aHelper,
+                                         const nsIID* aIID,
+                                         bool aAllowNativeWrapper);
+
+/**
+ * A method to handle new-binding wrap failure, by possibly falling back to
+ * wrapping as a non-new-binding object.
+ */
+template <class T>
+MOZ_ALWAYS_INLINE bool
+HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
+                                T* value, JS::MutableHandle<JS::Value> rval)
+{
+  if (JS_IsExceptionPending(cx)) {
+    return false;
+  }
+
+  qsObjectHelper helper(value, GetWrapperCache(value));
+  return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
+                                                  helper, nullptr, true);
+}
+
+// Helper for calling HandleNewBindingWrappingFailure with smart pointers
+// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
+
+template <class T, bool isSmartPtr=IsSmartPtr<T>::value>
+struct HandleNewBindingWrappingFailureHelper
+{
+  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
+                          const T& value, JS::MutableHandle<JS::Value> rval)
+  {
+    return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
+  }
+};
+
+template <class T>
+struct HandleNewBindingWrappingFailureHelper<T, false>
+{
+  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
+                          JS::MutableHandle<JS::Value> rval)
+  {
+    return HandleNewBindingWrappingFailure(cx, scope, &value, rval);
+  }
+};
+
+template<class T>
+inline bool
+HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
+                                T& value, JS::MutableHandle<JS::Value> rval)
+{
+  return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval);
+}
+
 template<bool Fatal>
 inline bool
 EnumValueNotFound(JSContext* cx, JS::HandleString str, const char* type,
                   const char* sourceDescription);
 
 template<>
 inline bool
 EnumValueNotFound<false>(JSContext* cx, JS::HandleString str, const char* type,
@@ -1449,17 +1530,17 @@ UpdateWrapper(T* p, void*, JSObject* obj
 bool
 TryPreserveWrapper(JSObject* obj);
 
 // Can only be called with a DOM JSClass.
 bool
 InstanceClassHasProtoAtDepth(const js::Class* clasp,
                              uint32_t protoID, uint32_t depth);
 
-// Only set allowNativeWrapper to false if you really know you need it; if in
+// Only set allowNativeWrapper to false if you really know you need it, if in
 // doubt use true. Setting it to false disables security wrappers.
 bool
 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
                    xpcObjectHelper& helper, const nsIID* iid,
                    bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval);
 
 // Special-cased wrapping for variants
 bool
@@ -1472,17 +1553,17 @@ VariantToJsval(JSContext* aCx, nsIVarian
 // the nsWrapperCache for "p".
 template<class T>
 inline bool
 WrapObject(JSContext* cx, T* p, nsWrapperCache* cache, const nsIID* iid,
            JS::MutableHandle<JS::Value> rval)
 {
   if (xpc_FastGetCachedWrapper(cx, cache, rval))
     return true;
-  xpcObjectHelper helper(ToSupports(p), cache);
+  qsObjectHelper helper(p, cache);
   JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
 }
 
 // A specialization of the above for nsIVariant, because that needs to
 // do something different.
 template<>
 inline bool
@@ -1572,43 +1653,72 @@ WrapObject(JSContext* cx, JSObject& p, J
 // Given an object "p" that inherits from nsISupports, wrap it and return the
 // result.  Null is returned on wrapping failure.  This is somewhat similar to
 // WrapObject() above, but does NOT allow Xrays around the result, since we
 // don't want those for our parent object.
 template<typename T>
 static inline JSObject*
 WrapNativeISupports(JSContext* cx, T* p, nsWrapperCache* cache)
 {
-  xpcObjectHelper helper(ToSupports(p), cache);
+  qsObjectHelper helper(ToSupports(p), cache);
   JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   JS::Rooted<JS::Value> v(cx);
   return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
          v.toObjectOrNull() :
          nullptr;
 }
 
 
-// Wrapping of our native parent, for cases when it's a WebIDL object.
+// Fallback for when our parent is not a WebIDL binding object.
+template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct WrapNativeFallback
+{
+  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+  {
+    return nullptr;
+  }
+};
+
+// Fallback for when our parent is not a WebIDL binding object but _is_ an
+// nsISupports object.
+template<typename T >
+struct WrapNativeFallback<T, true >
+{
+  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
+  {
+    return WrapNativeISupports(cx, parent, cache);
+  }
+};
+
+// Wrapping of our native parent, for cases when it's a WebIDL object (though
+// possibly preffed off).
 template<typename T, bool hasWrapObject=NativeHasMember<T>::WrapObject>
 struct WrapNativeHelper
 {
   static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
   {
     MOZ_ASSERT(cache);
 
     JSObject* obj;
     if ((obj = cache->GetWrapper())) {
       // GetWrapper always unmarks gray.
       MOZ_ASSERT(JS::ObjectIsNotGray(obj));
       return obj;
     }
 
-    // WrapObject never returns a gray thing.
-    obj = parent->WrapObject(cx, nullptr);
-    MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+    // Inline this here while we have non-dom objects in wrapper caches.
+    if (!CouldBeDOMBinding(parent)) {
+      // WrapNativeFallback never returns a gray thing.
+      obj = WrapNativeFallback<T>::Wrap(cx, parent, cache);
+      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+    } else {
+      // WrapObject never returns a gray thing.
+      obj = parent->WrapObject(cx, nullptr);
+      MOZ_ASSERT(JS::ObjectIsNotGray(obj));
+    }
 
     return obj;
   }
 };
 
 // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
 // this case it must be nsISupports.
 template<typename T>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -15106,16 +15106,18 @@ class CGJSImplClass(CGBindingImplClass):
                 descriptor.interface.parent.identifier.name).jsImplParent
             baseClasses = [ClassBase(parentClass)]
             isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
             ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
                       (descriptor.name, parentClass))
             constructorBody = dedent("""
                 // Make sure we're an nsWrapperCache already
                 MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
+                // And that our ancestor has not called SetIsNotDOMBinding()
+                MOZ_ASSERT(IsDOMBinding());
                 """)
             extradefinitions = fill(
                 """
                 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
                 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
                 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
                 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
                 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -143,16 +143,18 @@ template <class T>
 MOZ_MUST_USE
 typename EnableIf<IsBaseOf<nsWrapperCache, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+  // Make sure non-webidl objects don't sneak in here
+  MOZ_ASSERT(aArgument.IsDOMBinding());
 
   return GetOrCreateDOMReflector(aCx, aArgument, aValue);
 }
 
 // Accept typed arrays built from appropriate nsTArray values
 template<typename T>
 MOZ_MUST_USE
 typename EnableIf<IsBaseOf<AllTypedArraysBase, T>::value, bool>::Type
@@ -180,17 +182,17 @@ typename EnableIf<!IsBaseOf<nsWrapperCac
                   IsBaseOf<nsISupports, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   // Make sure we're called in a compartment
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
 
-  xpcObjectHelper helper(ToSupports(&aArgument));
+  qsObjectHelper helper(ToSupports(&aArgument), nullptr);
   JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
   return XPCOMObjectToJsval(aCx, scope, helper, nullptr, true, aValue);
 }
 
 // Accept nsRefPtr/nsCOMPtr
 template <typename T>
 MOZ_MUST_USE bool
 ToJSValue(JSContext* aCx,
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -417,14 +417,26 @@ private:
   bool mOriginalWantsPopupControlCheck;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(Event, NS_EVENT_IID)
 
 } // namespace dom
 } // namespace mozilla
 
+inline nsISupports*
+ToSupports(mozilla::dom::Event* e)
+{
+  return static_cast<nsIDOMEvent*>(e);
+}
+
+inline nsISupports*
+ToCanonicalSupports(mozilla::dom::Event* e)
+{
+  return static_cast<nsIDOMEvent*>(e);
+}
+
 already_AddRefed<mozilla::dom::Event>
 NS_NewDOMEvent(mozilla::dom::EventTarget* aOwner,
                nsPresContext* aPresContext,
                mozilla::WidgetEvent* aEvent);
 
 #endif // mozilla_dom_Event_h_
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1341,9 +1341,21 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(TextArea)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Tfoot)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Thead)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Time)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Title)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Track)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Unknown)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Video)
 
+inline nsISupports*
+ToSupports(nsGenericHTMLElement* aHTMLElement)
+{
+  return static_cast<nsIContent*>(aHTMLElement);
+}
+
+inline nsISupports*
+ToCanonicalSupports(nsGenericHTMLElement* aHTMLElement)
+{
+  return static_cast<nsIContent*>(aHTMLElement);
+}
+
 #endif /* nsGenericHTMLElement_h___ */
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1886,59 +1886,74 @@ nsHTMLDocument::CaptureEvents()
 }
 
 void
 nsHTMLDocument::ReleaseEvents()
 {
   WarnOnceAbout(nsIDocument::eUseOfReleaseEvents);
 }
 
-bool
-nsHTMLDocument::ResolveName(JSContext* aCx, const nsAString& aName,
-                            JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
+nsISupports*
+nsHTMLDocument::ResolveName(const nsAString& aName, nsWrapperCache **aCache)
 {
   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
   if (!entry) {
-    return false;
+    *aCache = nullptr;
+    return nullptr;
   }
 
   nsBaseContentList *list = entry->GetNameContentList();
   uint32_t length = list ? list->Length() : 0;
 
-  nsIContent *node;
   if (length > 0) {
-    if (length > 1) {
-      // The list contains more than one element, return the whole list.
-      if (!ToJSValue(aCx, list, aRetval)) {
-        aError.NoteJSContextException(aCx);
-        return false;
-      }
-      return true;
+    if (length == 1) {
+      // Only one element in the list, return the element instead of returning
+      // the list.
+      nsIContent *node = list->Item(0);
+      *aCache = node;
+      return node;
     }
 
-    // Only one element in the list, return the element instead of returning
-    // the list.
-    node = list->Item(0);
-  } else {
-    // No named items were found, see if there's one registerd by id for aName.
-    Element *e = entry->GetIdElement();
-  
-    if (!e || !nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) {
-      return false;
-    }
-
-    node = e;
+    // The list contains more than one element, return the whole list.
+    *aCache = list;
+    return list;
+  }
+
+  // No named items were found, see if there's one registerd by id for aName.
+  Element *e = entry->GetIdElement();
+
+  if (e && nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) {
+    *aCache = e;
+    return e;
   }
 
-  if (!ToJSValue(aCx, node, aRetval)) {
-    aError.NoteJSContextException(aCx);
-    return false;
+  *aCache = nullptr;
+  return nullptr;
+}
+
+void
+nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
+                            JS::MutableHandle<JSObject*> aRetval,
+                            ErrorResult& rv)
+{
+  nsWrapperCache* cache;
+  nsISupports* supp = ResolveName(aName, &cache);
+  if (!supp) {
+    aFound = false;
+    aRetval.set(nullptr);
+    return;
   }
 
-  return true;
+  JS::Rooted<JS::Value> val(cx);
+  if (!dom::WrapObject(cx, supp, cache, nullptr, &val)) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+  aFound = true;
+  aRetval.set(&val.toObject());
 }
 
 void
 nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
     nsIdentifierMapEntry* entry = iter.Get();
     if (entry->HasNameElement() ||
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -87,19 +87,17 @@ public:
   using nsDocument::GetImplementation;
   using nsDocument::GetTitle;
   using nsDocument::SetTitle;
   using nsDocument::GetLastStyleSheetSet;
   using nsDocument::MozSetImageElement;
 
   mozilla::dom::HTMLAllCollection* All();
 
-  // Returns whether an object was found for aName.
-  bool ResolveName(JSContext* aCx, const nsAString& aName,
-                   JS::MutableHandle<JS::Value> aRetval, mozilla::ErrorResult& aError);
+  nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
 
   virtual void AddedForm() override;
   virtual void RemovedForm() override;
   virtual int32_t GetNumFormsSynchronous() override;
   virtual void TearingDownEditor() override;
   virtual void SetIsXHTML(bool aXHTML) override
   {
     mType = (aXHTML ? eXHTML : eHTML);
@@ -161,23 +159,17 @@ public:
   void GetDomain(nsAString& aDomain);
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
                                             const nsACString& aOrigHost);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
                    JS::MutableHandle<JSObject*> aRetval,
-                   mozilla::ErrorResult& rv)
-  {
-    JS::Rooted<JS::Value> v(cx);
-    if ((aFound = ResolveName(cx, aName, &v, rv))) {
-      aRetval.set(v.toObjectOrNull());
-    }
-  }
+                   mozilla::ErrorResult& rv);
   void GetSupportedNames(nsTArray<nsString>& aNames);
   already_AddRefed<nsIDocument> Open(JSContext* cx,
                                      const mozilla::dom::Optional<nsAString>& /* unused */,
                                      const nsAString& aReplace,
                                      mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter>
   Open(JSContext* cx,
        const nsAString& aURL,
--- a/dom/network/Connection.cpp
+++ b/dom/network/Connection.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Connection.h"
 #include "ConnectionMainThread.h"
 #include "ConnectionWorker.h"
+#include "nsIDOMClassInfo.h"
 #include "Constants.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/WorkerPrivate.h"
 
 /**
  * We have to use macros here because our leak analysis tool things we are
  * leaking strings when we have |static const nsString|. Sad :(
  */
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -44,20 +44,20 @@ interface nsIXPConnectWrappedNative;
     #define XPC_SCRIPTABLE_WANT_FINALIZE                    (1 <<  6)
     #define XPC_SCRIPTABLE_WANT_CALL                        (1 <<  7)
     #define XPC_SCRIPTABLE_WANT_CONSTRUCT                   (1 <<  8)
     #define XPC_SCRIPTABLE_WANT_HASINSTANCE                 (1 <<  9)
     #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY       (1 << 10)
     #define XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY       (1 << 11)
     // (1 << 12) is unused
     #define XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE        (1 << 13)
-    // (1 << 14) is unused
-    // (1 << 15) is unused
+    #define XPC_SCRIPTABLE_DONT_ASK_INSTANCE_FOR_SCRIPTABLE (1 << 14)
+    #define XPC_SCRIPTABLE_CLASSINFO_INTERFACES_ONLY        (1 << 15)
     #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE   (1 << 16)
-    // (1 << 17) is unused
+    #define XPC_SCRIPTABLE_ALLOW_PROP_MODS_TO_PROTOTYPE     (1 << 17)
     #define XPC_SCRIPTABLE_IS_GLOBAL_OBJECT                 (1 << 18)
     #define XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES     (1 << 19)
 %}
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
@@ -95,16 +95,18 @@ interface nsIXPCScriptable : nsISupports
     boolean construct(in nsIXPConnectWrappedNative wrapper,
                      in JSContextPtr cx, in JSObjectPtr obj,
                      in JSCallArgsRef args);
 
     boolean hasInstance(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj,
                        in jsval val, out boolean bp);
 
+    void postCreatePrototype(in JSContextPtr cx, in JSObjectPtr proto);
+
 %{ C++
     #define GET_IT(f_, c_) \
     bool f_() { \
         return 0 != (GetScriptableFlags() & XPC_SCRIPTABLE_##c_); \
     }
 
     GET_IT(WantPreCreate,                WANT_PRECREATE)
     GET_IT(WantEnumerate,                WANT_ENUMERATE)
@@ -112,15 +114,51 @@ interface nsIXPCScriptable : nsISupports
     GET_IT(WantResolve,                  WANT_RESOLVE)
     GET_IT(WantFinalize,                 WANT_FINALIZE)
     GET_IT(WantCall,                     WANT_CALL)
     GET_IT(WantConstruct,                WANT_CONSTRUCT)
     GET_IT(WantHasInstance,              WANT_HASINSTANCE)
     GET_IT(UseJSStubForAddProperty,      USE_JSSTUB_FOR_ADDPROPERTY)
     GET_IT(UseJSStubForDelProperty,      USE_JSSTUB_FOR_DELPROPERTY)
     GET_IT(DontEnumQueryInterface,       DONT_ENUM_QUERY_INTERFACE)
+    GET_IT(DontAskInstanceForScriptable, DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
+    GET_IT(ClassInfoInterfacesOnly,      CLASSINFO_INTERFACES_ONLY)
     GET_IT(AllowPropModsDuringResolve,   ALLOW_PROP_MODS_DURING_RESOLVE)
+    GET_IT(AllowPropModsToPrototype,     ALLOW_PROP_MODS_TO_PROTOTYPE)
     GET_IT(IsGlobalObject,               IS_GLOBAL_OBJECT)
     GET_IT(DontReflectInterfaceNames,    DONT_REFLECT_INTERFACE_NAMES)
 
     #undef GET_IT
 %}
 };
+
+%{ C++
+
+#include "nsAutoPtr.h"
+
+#define NS_XPCCLASSINFO_IID \
+{ 0x43b67f01, 0xd4ce, 0x4b82, \
+ { 0xb3, 0xf8, 0xeb, 0xf2, 0x13, 0x60, 0xfb, 0x7e } }
+
+class NS_NO_VTABLE nsXPCClassInfo : public nsIClassInfo,
+                                    public nsIXPCScriptable
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_XPCCLASSINFO_IID)
+
+  NS_IMETHOD_(MozExternalRefCountType) AddRef() override = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release() override = 0;
+
+  virtual void PreserveWrapper(nsISupports *aNative) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsXPCClassInfo, NS_XPCCLASSINFO_IID)
+
+inline
+nsresult
+CallQueryInterface(nsISupports* aSourcePtr,
+                   RefPtrGetterAddRefs<nsXPCClassInfo> aDestPtr)
+{
+  return CallQueryInterface(aSourcePtr,
+                            static_cast<nsXPCClassInfo**>(aDestPtr));
+}
+
+%}
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -94,13 +94,16 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::Constru
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_HASINSTANCE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::HasInstance(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::HandleValue val, bool* bp, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
+NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreatePrototype(JSContext* cx, JSObject* proto)
+    {return NS_OK;}
+
 /**************************************************************/
 
 #undef XPC_MAP_CLASSNAME
 #undef XPC_MAP_QUOTED_CLASSNAME
 #undef XPC_MAP_FLAGS
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1053,18 +1053,21 @@ xpc::CreateSandboxObject(JSContext* cx, 
     // flag, but such a change would break code in subtle ways for minimal
     // benefit. So we just switch it off here.
     priv->wantXrays =
       AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
 
     {
         JSAutoCompartment ac(cx, sandbox);
 
-        // This creates a SandboxPrivate and passes ownership of it to |sandbox|.
-        SandboxPrivate::Create(principal, sandbox);
+        nsCOMPtr<nsIScriptObjectPrincipal> sbp =
+            new SandboxPrivate(principal, sandbox);
+
+        // Pass on ownership of sbp to |sandbox|.
+        JS_SetPrivate(sandbox, sbp.forget().take());
 
         // Ensure |Object.prototype| is instantiated before prototype-
         // splicing below.
         if (!JS_GetObjectPrototype(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
--- a/js/xpconnect/src/SandboxPrivate.h
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -17,38 +17,27 @@
 
 
 class SandboxPrivate : public nsIGlobalObject,
                        public nsIScriptObjectPrincipal,
                        public nsSupportsWeakReference,
                        public nsWrapperCache
 {
 public:
+    SandboxPrivate(nsIPrincipal* principal, JSObject* global)
+        : mPrincipal(principal)
+    {
+        SetIsNotDOMBinding();
+        SetWrapper(global);
+    }
+
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SandboxPrivate,
                                                            nsIGlobalObject)
 
-    static void Create(nsIPrincipal* principal, JS::Handle<JSObject*> global)
-    {
-        RefPtr<SandboxPrivate> sbp = new SandboxPrivate(principal);
-        sbp->SetWrapper(global);
-        sbp->PreserveWrapper(ToSupports(sbp.get()));
-
-        // Pass on ownership of sbp to |global|.
-        // The type used to cast to void needs to match the one in GetPrivate.
-        JS_SetPrivate(global, static_cast<nsIScriptObjectPrincipal*>(sbp.forget().take()));
-    }
-
-    static SandboxPrivate* GetPrivate(JSObject* obj)
-    {
-        // The type used to cast to void needs to match the one in Create.
-        return static_cast<SandboxPrivate*>(
-            static_cast<nsIScriptObjectPrincipal*>(JS_GetPrivate(obj)));
-    }
-
     nsIPrincipal* GetPrincipal() override
     {
         return mPrincipal;
     }
 
     JSObject* GetGlobalJSObject() override
     {
         return GetWrapper();
@@ -66,19 +55,14 @@ public:
 
     size_t ObjectMoved(JSObject* obj, JSObject* old)
     {
         UpdateWrapper(obj, old);
         return 0;
     }
 
 private:
-    explicit SandboxPrivate(nsIPrincipal* principal)
-        : mPrincipal(principal)
-    { }
-
-    virtual ~SandboxPrivate()
-    { }
+    virtual ~SandboxPrivate() { }
 
     nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 #endif // __SANDBOXPRIVATE_H__
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -768,23 +768,26 @@ XPCConvert::NativeInterface2JSObject(Mut
     // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
     // optimal -- we could detect this and roll the functionality into a
     // single wrapper, but the current solution is good enough for now.
     AutoJSContext cx;
     XPCWrappedNativeScope* xpcscope = ObjectScope(JS::CurrentGlobalOrNull(cx));
     if (!xpcscope)
         return false;
 
-    // First, see if this object supports the wrapper cache. In that case, the object
-    // to use is found as cache->GetWrapper(). If that is null, then the object will
-    // create (and fill the cache) from its WrapObject call.
+    // First, see if this object supports the wrapper cache.
+    // Note: If |cache->IsDOMBinding()| is true, then it means that the object
+    // implementing it doesn't want a wrapped native as its JS Object, but
+    // instead it provides its own proxy object. In that case, the object
+    // to use is found as cache->GetWrapper(). If that is null, then the
+    // object will create (and fill the cache) from its WrapObject call.
     nsWrapperCache* cache = aHelper.GetWrapperCache();
 
     RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
-    if (!flat && cache) {
+    if (!flat && cache && cache->IsDOMBinding()) {
         RootedObject global(cx, xpcscope->GetGlobalJSObject());
         js::AssertSameCompartment(cx, global);
         flat = cache->WrapObject(cx, nullptr);
         if (!flat)
             return false;
     }
     if (flat) {
         if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2863,17 +2863,17 @@ XPCJSRuntime::InitializeStrings(JSContex
     return true;
 }
 
 bool
 XPCJSRuntime::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
                                     char (&name)[72]) const
 {
 
-    if (clasp != &XPC_WN_Proto_JSClass) {
+    if (!IS_PROTO_CLASS(clasp)) {
         return false;
     }
 
     XPCWrappedNativeProto* p =
         static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
     nsCOMPtr<nsIXPCScriptable> scr = p->GetScriptable();
     if (!scr) {
         return false;
@@ -3042,20 +3042,12 @@ XPCJSRuntime::InitSingletonScopes()
     rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     mCompilationScope = js::UncheckedUnwrap(&v.toObject());
 }
 
 void
 XPCJSRuntime::DeleteSingletonScopes()
 {
-    // We're pretty late in shutdown, so we call ReleaseWrapper on the scopes. This way
-    // the GC can collect them immediately, and we don't rely on the CC to clean up.
-    RefPtr<SandboxPrivate> sandbox = SandboxPrivate::GetPrivate(mUnprivilegedJunkScope);
-    sandbox->ReleaseWrapper(sandbox);
     mUnprivilegedJunkScope = nullptr;
-    sandbox = SandboxPrivate::GetPrivate(mPrivilegedJunkScope);
-    sandbox->ReleaseWrapper(sandbox);
     mPrivilegedJunkScope = nullptr;
-    sandbox = SandboxPrivate::GetPrivate(mCompilationScope);
-    sandbox->ReleaseWrapper(sandbox);
     mCompilationScope = nullptr;
 }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -150,17 +150,17 @@ FinishCreate(XPCWrappedNativeScope* Scop
 nsresult
 XPCWrappedNative::WrapNewGlobal(xpcObjectHelper& nativeHelper,
                                 nsIPrincipal* principal,
                                 bool initStandardClasses,
                                 JS::CompartmentOptions& aOptions,
                                 XPCWrappedNative** wrappedGlobal)
 {
     AutoJSContext cx;
-    nsCOMPtr<nsISupports> identity = do_QueryInterface(nativeHelper.Object());
+    nsISupports* identity = nativeHelper.GetCanonical();
 
     // The object should specify that it's meant to be global.
     MOZ_ASSERT(nativeHelper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
 
     // We shouldn't be reusing globals.
     MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
                !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
 
@@ -192,30 +192,32 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
     // If requested, initialize the standard classes on the global.
     if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
         return NS_ERROR_FAILURE;
 
     // Make a proto.
     XPCWrappedNativeProto* proto =
         XPCWrappedNativeProto::GetNewOrUsed(scope,
                                             nativeHelper.GetClassInfo(),
-                                            scrProto);
+                                            scrProto,
+                                            /* callPostCreatePrototype = */ false);
     if (!proto)
         return NS_ERROR_FAILURE;
 
     // Set up the prototype on the global.
     MOZ_ASSERT(proto->GetJSProtoObject());
     RootedObject protoObj(cx, proto->GetJSProtoObject());
     bool success = JS_SplicePrototype(cx, global, protoObj);
     if (!success)
         return NS_ERROR_FAILURE;
 
     // Construct the wrapper, which takes over the strong reference to the
     // native object.
-    RefPtr<XPCWrappedNative> wrapper = new XPCWrappedNative(identity.forget(), proto);
+    RefPtr<XPCWrappedNative> wrapper =
+        new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
 
     //
     // We don't call ::Init() on this wrapper, because our setup requirements
     // are different for globals. We do our setup inline here, instead.
     //
 
     wrapper->mScriptable = scrWrapper;
 
@@ -276,17 +278,17 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
                "We assume the caller already checked if it could get the "
                "wrapper from the cache.");
 
     nsresult rv;
 
     MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
                "XPCWrappedNative::GetNewOrUsed called during GC");
 
-    nsCOMPtr<nsISupports> identity = do_QueryInterface(helper.Object());
+    nsISupports* identity = helper.GetCanonical();
 
     if (!identity) {
         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
         return NS_ERROR_FAILURE;
     }
 
     RefPtr<XPCWrappedNative> wrapper;
 
@@ -398,30 +400,31 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
     // Note that the security check happens inside FindTearOff - after the
     // wrapper is actually created, but before JS code can see it.
 
     if (info && !isClassInfoSingleton) {
         proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, scrProto);
         if (!proto)
             return NS_ERROR_FAILURE;
 
-        wrapper = new XPCWrappedNative(identity.forget(), proto);
+        wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
     } else {
         RefPtr<XPCNativeInterface> iface = Interface;
         if (!iface)
             iface = XPCNativeInterface::GetISupports();
 
         XPCNativeSetKey key(iface);
         RefPtr<XPCNativeSet> set =
             XPCNativeSet::GetNewOrUsed(&key);
 
         if (!set)
             return NS_ERROR_FAILURE;
 
-        wrapper = new XPCWrappedNative(identity.forget(), Scope, set.forget());
+        wrapper = new XPCWrappedNative(helper.forgetCanonical(), Scope,
+                                       set.forget());
     }
 
     MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
                "Xray wrapper being used to parent XPCWrappedNative?");
 
     // We use an AutoMarkingPtr here because it is possible for JS gc to happen
     // after we have Init'd the wrapper but *before* we add it to the hashtable.
     // This would cause the mSet to get collected and we'd later crash. I've
@@ -541,16 +544,24 @@ XPCWrappedNative::Destroy()
 
 // This is factored out so that it can be called publicly.
 // static
 nsIXPCScriptable*
 XPCWrappedNative::GatherProtoScriptable(nsIClassInfo* classInfo)
 {
     MOZ_ASSERT(classInfo, "bad param");
 
+    nsXPCClassInfo* classInfoHelper = nullptr;
+    CallQueryInterface(classInfo, &classInfoHelper);
+    if (classInfoHelper) {
+        nsCOMPtr<nsIXPCScriptable> helper =
+          dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
+        return helper;
+    }
+
     nsCOMPtr<nsIXPCScriptable> helper;
     nsresult rv = classInfo->GetScriptableHelper(getter_AddRefs(helper));
     if (NS_SUCCEEDED(rv) && helper) {
         return helper;
     }
 
     return nullptr;
 }
@@ -566,16 +577,23 @@ XPCWrappedNative::GatherScriptable(nsISu
     MOZ_ASSERT(!*aScrWrapper, "bad param");
 
     nsCOMPtr<nsIXPCScriptable> scrProto;
     nsCOMPtr<nsIXPCScriptable> scrWrapper;
 
     // Get the class scriptable helper (if present)
     if (aClassInfo) {
         scrProto = GatherProtoScriptable(aClassInfo);
+
+        if (scrProto && scrProto->DontAskInstanceForScriptable()) {
+            scrWrapper = scrProto;
+            scrProto.forget(aScrProto);
+            scrWrapper.forget(aScrWrapper);
+            return;
+        }
     }
 
     // Do the same for the wrapper specific scriptable
     scrWrapper = do_QueryInterface(aObj);
     if (scrWrapper) {
         // A whole series of assertions to catch bad uses of scriptable flags on
         // the scrWrapper...
 
@@ -584,20 +602,35 @@ XPCWrappedNative::GatherScriptable(nsISu
         MOZ_ASSERT_IF(scrWrapper->WantPreCreate(),
                       scrProto && scrProto->WantPreCreate());
 
         // Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable
         // without also setting it on the class scriptable (if present).
         MOZ_ASSERT_IF(scrWrapper->DontEnumQueryInterface() && scrProto,
                       scrProto->DontEnumQueryInterface());
 
+        // Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable
+        // without also setting it on the class scriptable.
+        MOZ_ASSERT_IF(scrWrapper->DontAskInstanceForScriptable(),
+                      scrProto && scrProto->DontAskInstanceForScriptable());
+
+        // Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable
+        // without also setting it on the class scriptable (if present).
+        MOZ_ASSERT_IF(scrWrapper->ClassInfoInterfacesOnly() && scrProto,
+                      scrProto->ClassInfoInterfacesOnly());
+
         // Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable
         // without also setting it on the class scriptable (if present).
         MOZ_ASSERT_IF(scrWrapper->AllowPropModsDuringResolve() && scrProto,
                       scrProto->AllowPropModsDuringResolve());
+
+        // Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable
+        // without also setting it on the class scriptable (if present).
+        MOZ_ASSERT_IF(scrWrapper->AllowPropModsToPrototype() && scrProto,
+                      scrProto->AllowPropModsToPrototype());
     } else {
         scrWrapper = scrProto;
     }
 
     scrProto.forget(aScrProto);
     scrWrapper.forget(aScrWrapper);
 }
 
@@ -954,16 +987,25 @@ XPCWrappedNative::InitTearOff(XPCWrapped
 
     const nsIID* iid = aInterface->GetIID();
     nsISupports* identity = GetIdentityObject();
 
     // This is an nsRefPtr instead of an nsCOMPtr because it may not be the
     // canonical nsISupports for this object.
     RefPtr<nsISupports> qiResult;
 
+    // If the scriptable helper forbids us from reflecting additional
+    // interfaces, then don't even try the QI, just fail.
+    if (mScriptable &&
+        mScriptable->ClassInfoInterfacesOnly() &&
+        !mSet->HasInterface(aInterface) &&
+        !mSet->HasInterfaceWithAncestor(aInterface)) {
+        return NS_ERROR_NO_INTERFACE;
+    }
+
     // We are about to call out to other code.
     // So protect our intended tearoff.
 
     aTearOff->SetReserved();
 
     if (NS_FAILED(identity->QueryInterface(*iid, getter_AddRefs(qiResult))) || !qiResult) {
         aTearOff->SetInterface(nullptr);
         return NS_ERROR_NO_INTERFACE;
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -247,16 +247,28 @@ DefinePropertyIfFound(XPCCallContext& cc
         else
             found = set->FindMember(id, &member, &iface);
     } else
         found = (nullptr != (member = iface->FindMember(id)));
 
     if (!found) {
         if (reflectToStringAndToSource) {
             JSNative call;
+            uint32_t flags = 0;
+
+            if (scr) {
+                nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(scr);
+
+                if (classInfo) {
+                    nsresult rv = classInfo->GetFlags(&flags);
+                    if (NS_FAILED(rv))
+                        return Throw(rv, ccx);
+                }
+            }
+
             if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING)) {
                 call = XPC_WN_Shared_ToString;
                 name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING);
             } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) {
                 call = XPC_WN_Shared_ToSource;
                 name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE);
             } else if (id == SYMBOL_TO_JSID(
                                JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
@@ -935,19 +947,20 @@ XPC_WN_GetterSetter(JSContext* cx, unsig
 
     ccx.SetCallInfo(iface, member, false);
     return XPCWrappedNative::GetAttribute(ccx);
 }
 
 /***************************************************************************/
 
 static bool
-XPC_WN_Proto_Enumerate(JSContext* cx, HandleObject obj)
+XPC_WN_Shared_Proto_Enumerate(JSContext* cx, HandleObject obj)
 {
-    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_Proto_JSClass,
+    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_Proto_JSClass ||
+               js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
                "bad proto");
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!self)
         return false;
 
     XPCNativeSet* set = self->GetSet();
     if (!set)
@@ -968,53 +981,106 @@ XPC_WN_Proto_Enumerate(JSContext* cx, Ha
                 return false;
         }
     }
 
     return true;
 }
 
 static void
-XPC_WN_Proto_Finalize(js::FreeOp* fop, JSObject* obj)
+XPC_WN_Shared_Proto_Finalize(js::FreeOp* fop, JSObject* obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (p)
         p->JSProtoObjectFinalized(fop, obj);
 }
 
 static size_t
-XPC_WN_Proto_ObjectMoved(JSObject* obj, JSObject* old)
+XPC_WN_Shared_Proto_ObjectMoved(JSObject* obj, JSObject* old)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!p)
         return 0;
 
     p->JSProtoObjectMoved(obj, old);
     return 0;
 }
 
 static void
-XPC_WN_Proto_Trace(JSTracer* trc, JSObject* obj)
+XPC_WN_Shared_Proto_Trace(JSTracer* trc, JSObject* obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (p)
         p->TraceInside(trc);
 }
 
 /*****************************************************/
 
 static bool
+XPC_WN_ModsAllowed_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvep)
+{
+    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_Proto_JSClass,
+               "bad proto");
+
+    XPCWrappedNativeProto* self =
+        (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+    if (!self)
+        return false;
+
+    XPCCallContext ccx(cx);
+    if (!ccx.IsValid())
+        return false;
+
+    nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable();
+    return DefinePropertyIfFound(ccx, obj, id,
+                                 self->GetSet(), nullptr, nullptr,
+                                 self->GetScope(),
+                                 true, nullptr, nullptr, scr,
+                                 JSPROP_ENUMERATE, resolvep);
+}
+
+static const js::ClassOps XPC_WN_ModsAllowed_Proto_JSClassOps = {
+    nullptr,                            // addProperty
+    nullptr,                            // delProperty
+    XPC_WN_Shared_Proto_Enumerate,      // enumerate
+    nullptr,                            // newEnumerate
+    XPC_WN_ModsAllowed_Proto_Resolve,   // resolve
+    nullptr,                            // mayResolve
+    XPC_WN_Shared_Proto_Finalize,       // finalize
+    nullptr,                            // call
+    nullptr,                            // construct
+    nullptr,                            // hasInstance
+    XPC_WN_Shared_Proto_Trace,          // trace
+};
+
+static const js::ClassExtension XPC_WN_Shared_Proto_ClassExtension = {
+    nullptr,    /* weakmapKeyDelegateOp */
+    XPC_WN_Shared_Proto_ObjectMoved
+};
+
+const js::Class XPC_WN_ModsAllowed_Proto_JSClass = {
+    "XPC_WN_ModsAllowed_Proto_JSClass",
+    XPC_WRAPPER_FLAGS,
+    &XPC_WN_ModsAllowed_Proto_JSClassOps,
+    JS_NULL_CLASS_SPEC,
+    &XPC_WN_Shared_Proto_ClassExtension,
+    JS_NULL_OBJECT_OPS
+};
+
+/***************************************************************************/
+
+static bool
 XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
                                         HandleValue v)
 {
-    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_Proto_JSClass,
+    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
                "bad proto");
 
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!self)
         return false;
 
     XPCCallContext ccx(cx);
@@ -1024,19 +1090,19 @@ XPC_WN_OnlyIWrite_Proto_AddPropertyStub(
     // Allow XPConnect to add the property only
     if (ccx.GetResolveName() == id)
         return true;
 
     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
 }
 
 static bool
-XPC_WN_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+XPC_WN_NoMods_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
-    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_Proto_JSClass,
+    MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
                "bad proto");
 
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (!self)
         return false;
 
     XPCCallContext ccx(cx);
@@ -1049,41 +1115,36 @@ XPC_WN_Proto_Resolve(JSContext* cx, Hand
                                  self->GetSet(), nullptr, nullptr,
                                  self->GetScope(),
                                  true, nullptr, nullptr, scr,
                                  JSPROP_READONLY |
                                  JSPROP_PERMANENT |
                                  JSPROP_ENUMERATE, resolvedp);
 }
 
-static const js::ClassOps XPC_WN_Proto_JSClassOps = {
+static const js::ClassOps XPC_WN_NoMods_Proto_JSClassOps = {
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,   // addProperty
     XPC_WN_CannotDeletePropertyStub,           // delProperty
-    XPC_WN_Proto_Enumerate,                    // enumerate
+    XPC_WN_Shared_Proto_Enumerate,             // enumerate
     nullptr,                                   // newEnumerate
-    XPC_WN_Proto_Resolve,                      // resolve
+    XPC_WN_NoMods_Proto_Resolve,               // resolve
     nullptr,                                   // mayResolve
-    XPC_WN_Proto_Finalize,                     // finalize
+    XPC_WN_Shared_Proto_Finalize,              // finalize
     nullptr,                                   // call
     nullptr,                                   // construct
     nullptr,                                   // hasInstance
-    XPC_WN_Proto_Trace,                        // trace
+    XPC_WN_Shared_Proto_Trace,                 // trace
 };
 
-static const js::ClassExtension XPC_WN_Proto_ClassExtension = {
-    nullptr,    /* weakmapKeyDelegateOp */
-    XPC_WN_Proto_ObjectMoved
-};
-
-const js::Class XPC_WN_Proto_JSClass = {
-    "XPC_WN_Proto_JSClass",
+const js::Class XPC_WN_NoMods_Proto_JSClass = {
+    "XPC_WN_NoMods_Proto_JSClass",
     XPC_WRAPPER_FLAGS,
-    &XPC_WN_Proto_JSClassOps,
+    &XPC_WN_NoMods_Proto_JSClassOps,
     JS_NULL_CLASS_SPEC,
-    &XPC_WN_Proto_ClassExtension,
+    &XPC_WN_Shared_Proto_ClassExtension,
     JS_NULL_OBJECT_OPS
 };
 
 /***************************************************************************/
 
 static bool
 XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj)
 {
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -45,34 +45,64 @@ XPCWrappedNativeProto::~XPCWrappedNative
 #endif
 
     // Note that our weak ref to mScope is not to be trusted at this point.
 
     XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
 }
 
 bool
-XPCWrappedNativeProto::Init(nsIXPCScriptable* scriptable)
+XPCWrappedNativeProto::Init(nsIXPCScriptable* scriptable,
+                            bool callPostCreatePrototype)
 {
     AutoJSContext cx;
     mScriptable = scriptable;
 
+    const js::Class* jsclazz =
+        (mScriptable && mScriptable->AllowPropModsToPrototype())
+        ? &XPC_WN_ModsAllowed_Proto_JSClass
+        : &XPC_WN_NoMods_Proto_JSClass;
+
     JS::RootedObject global(cx, mScope->GetGlobalJSObject());
     JS::RootedObject proto(cx, JS_GetObjectPrototype(cx, global));
-    mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(&XPC_WN_Proto_JSClass),
+    mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(jsclazz),
                                                 proto);
 
     bool success = !!mJSProtoObject;
     if (success) {
         JS_SetPrivate(mJSProtoObject, this);
+        if (callPostCreatePrototype)
+            success = CallPostCreatePrototype();
     }
 
     return success;
 }
 
+bool
+XPCWrappedNativeProto::CallPostCreatePrototype()
+{
+    AutoJSContext cx;
+
+    // Nothing to do if we don't have a scriptable callback.
+    if (!mScriptable)
+        return true;
+
+    // Call the helper. This can handle being called if it's not implemented,
+    // so we don't have to check any sort of "want" here. See xpc_map_end.h.
+    nsresult rv = mScriptable->PostCreatePrototype(cx, mJSProtoObject);
+    if (NS_FAILED(rv)) {
+        JS_SetPrivate(mJSProtoObject, nullptr);
+        mJSProtoObject = nullptr;
+        XPCThrower::Throw(rv, cx);
+        return false;
+    }
+
+    return true;
+}
+
 void
 XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj)
 {
     MOZ_ASSERT(obj == mJSProtoObject, "huh?");
 
 #ifdef DEBUG
     // Check that this object has already been swept from the map.
     ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap();
@@ -103,17 +133,18 @@ XPCWrappedNativeProto::SystemIsBeingShut
         mJSProtoObject = nullptr;
     }
 }
 
 // static
 XPCWrappedNativeProto*
 XPCWrappedNativeProto::GetNewOrUsed(XPCWrappedNativeScope* scope,
                                     nsIClassInfo* classInfo,
-                                    nsIXPCScriptable* scriptable)
+                                    nsIXPCScriptable* scriptable,
+                                    bool callPostCreatePrototype)
 {
     AutoJSContext cx;
     MOZ_ASSERT(scope, "bad param");
     MOZ_ASSERT(classInfo, "bad param");
 
     AutoMarkingWrappedNativeProtoPtr proto(cx);
     ClassInfo2WrappedNativeProtoMap* map = nullptr;
 
@@ -123,17 +154,17 @@ XPCWrappedNativeProto::GetNewOrUsed(XPCW
         return proto;
 
     RefPtr<XPCNativeSet> set = XPCNativeSet::GetNewOrUsed(classInfo);
     if (!set)
         return nullptr;
 
     proto = new XPCWrappedNativeProto(scope, classInfo, set.forget());
 
-    if (!proto || !proto->Init(scriptable)) {
+    if (!proto || !proto->Init(scriptable, callPostCreatePrototype)) {
         delete proto.get();
         return nullptr;
     }
 
     map->Add(classInfo, proto);
 
     return proto;
 }
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += [
     'BackstagePass.h',
+    'qsObjectHelper.h',
     'XPCJSMemoryReporter.h',
     'xpcObjectHelper.h',
     'xpcpublic.h',
 ]
 
 UNIFIED_SOURCES += [
     'ExportHelpers.cpp',
     'nsXPConnect.cpp',
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/src/qsObjectHelper.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=8 sts=4 et sw=4 tw=99: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef qsObjectHelper_h
+#define qsObjectHelper_h
+
+#include "xpcObjectHelper.h"
+
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "mozilla/TypeTraits.h"
+
+class qsObjectHelper : public xpcObjectHelper
+{
+public:
+    template <class T>
+    inline
+    qsObjectHelper(T* aObject, nsWrapperCache* aCache)
+        : xpcObjectHelper(ToSupports(aObject), ToCanonicalSupports(aObject),
+                          aCache)
+    {}
+
+    template <class T>
+    inline
+    qsObjectHelper(nsCOMPtr<T>& aObject, nsWrapperCache* aCache)
+        : xpcObjectHelper(ToSupports(aObject.get()),
+                          ToCanonicalSupports(aObject.get()), aCache)
+    {
+        if (mCanonical) {
+            // Transfer the strong reference.
+            mCanonicalStrong = dont_AddRef(mCanonical);
+            aObject.forget();
+        }
+    }
+
+    template <class T>
+    inline
+    qsObjectHelper(RefPtr<T>& aObject, nsWrapperCache* aCache)
+        : xpcObjectHelper(ToSupports(aObject.get()),
+                          ToCanonicalSupports(aObject.get()), aCache)
+    {
+        if (mCanonical) {
+            // Transfer the strong reference.
+            mCanonicalStrong = dont_AddRef(mCanonical);
+            aObject.forget();
+        }
+    }
+};
+
+#endif
--- a/js/xpconnect/src/xpcObjectHelper.h
+++ b/js/xpconnect/src/xpcObjectHelper.h
@@ -21,56 +21,117 @@
 #include "nsISupports.h"
 #include "nsIXPCScriptable.h"
 #include "nsWrapperCache.h"
 
 class xpcObjectHelper
 {
 public:
     explicit xpcObjectHelper(nsISupports* aObject, nsWrapperCache* aCache = nullptr)
-      : mObject(aObject)
+      : mCanonical(nullptr)
+      , mObject(aObject)
       , mCache(aCache)
     {
-        if (!mCache && aObject) {
-            CallQueryInterface(aObject, &mCache);
+        if (!mCache) {
+            if (aObject)
+                CallQueryInterface(aObject, &mCache);
+            else
+                mCache = nullptr;
         }
     }
 
     nsISupports* Object()
     {
         return mObject;
     }
 
+    nsISupports* GetCanonical()
+    {
+        if (!mCanonical) {
+            mCanonicalStrong = do_QueryInterface(mObject);
+            mCanonical = mCanonicalStrong;
+        }
+        return mCanonical;
+    }
+
+    already_AddRefed<nsISupports> forgetCanonical()
+    {
+        MOZ_ASSERT(mCanonical, "Huh, no canonical to forget?");
+
+        if (!mCanonicalStrong)
+            mCanonicalStrong = mCanonical;
+        mCanonical = nullptr;
+        return mCanonicalStrong.forget();
+    }
+
     nsIClassInfo* GetClassInfo()
     {
+        if (mXPCClassInfo)
+          return mXPCClassInfo;
         if (!mClassInfo)
             mClassInfo = do_QueryInterface(mObject);
         return mClassInfo;
     }
+    nsXPCClassInfo* GetXPCClassInfo()
+    {
+        if (!mXPCClassInfo) {
+            CallQueryInterface(mObject, getter_AddRefs(mXPCClassInfo));
+        }
+        return mXPCClassInfo;
+    }
+
+    already_AddRefed<nsXPCClassInfo> forgetXPCClassInfo()
+    {
+        GetXPCClassInfo();
+
+        return mXPCClassInfo.forget();
+    }
 
     // We assert that we can reach an nsIXPCScriptable somehow.
     uint32_t GetScriptableFlags()
     {
-        nsCOMPtr<nsIXPCScriptable> sinfo = do_QueryInterface(mObject);
+        // Try getting an nsXPCClassInfo - this handles DOM scriptable helpers.
+        nsCOMPtr<nsIXPCScriptable> sinfo = GetXPCClassInfo();
+
+        // If that didn't work, try just QI-ing. This handles BackstagePass.
+        if (!sinfo)
+            sinfo = do_QueryInterface(GetCanonical());
 
         // We should have something by now.
         MOZ_ASSERT(sinfo);
 
         // Grab the flags.
         return sinfo->GetScriptableFlags();
     }
 
     nsWrapperCache* GetWrapperCache()
     {
         return mCache;
     }
 
+protected:
+    xpcObjectHelper(nsISupports* aObject, nsISupports* aCanonical,
+                    nsWrapperCache* aCache)
+      : mCanonical(aCanonical)
+      , mObject(aObject)
+      , mCache(aCache)
+    {
+        if (!mCache && aObject)
+            CallQueryInterface(aObject, &mCache);
+    }
+
+    nsCOMPtr<nsISupports>    mCanonicalStrong;
+    nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
+                                "to avoid unnecessary AddRefs and Releases. "
+                                "(see bug 565742)") mCanonical;
+
 private:
     xpcObjectHelper(xpcObjectHelper& aOther) = delete;
 
     nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
                                 "to avoid unnecessary AddRefs and Releases. "
                                 "(see bug 565742)") mObject;
     nsWrapperCache*          mCache;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
+    RefPtr<nsXPCClassInfo> mXPCClassInfo;
 };
 
 #endif
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -826,28 +826,38 @@ private:
 *
 ****************************************************************************
 ***************************************************************************/
 
 // These are the various JSClasses and callbacks whose use that required
 // visibility from more than one .cpp file.
 
 extern const js::Class XPC_WN_NoHelper_JSClass;
-extern const js::Class XPC_WN_Proto_JSClass;
+extern const js::Class XPC_WN_NoMods_Proto_JSClass;
+extern const js::Class XPC_WN_ModsAllowed_Proto_JSClass;
 extern const js::Class XPC_WN_Tearoff_JSClass;
 #define XPC_WN_TEAROFF_RESERVED_SLOTS 1
 #define XPC_WN_TEAROFF_FLAT_OBJECT_SLOT 0
 extern const js::Class XPC_WN_NoHelper_Proto_JSClass;
 
 extern bool
 XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);
 
 extern bool
 XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);
 
+// Maybe this macro should check for class->enumerate ==
+// XPC_WN_Shared_Proto_Enumerate or something rather than checking for
+// 4 classes?
+static inline bool IS_PROTO_CLASS(const js::Class* clazz)
+{
+    return clazz == &XPC_WN_NoMods_Proto_JSClass ||
+           clazz == &XPC_WN_ModsAllowed_Proto_JSClass;
+}
+
 /***************************************************************************/
 // XPCWrappedNativeScope is one-to-one with a JS global object.
 
 class nsXPCComponentsBase;
 class XPCWrappedNativeScope final
 {
 public:
 
@@ -1309,17 +1319,18 @@ class XPCNativeSet final
 // for XPCWrappedNative whose native objects expose nsIClassInfo.
 
 class XPCWrappedNativeProto final
 {
 public:
     static XPCWrappedNativeProto*
     GetNewOrUsed(XPCWrappedNativeScope* scope,
                  nsIClassInfo* classInfo,
-                 nsIXPCScriptable* scriptable);
+                 nsIXPCScriptable* scriptable,
+                 bool callPostCreatePrototype = true);
 
     XPCWrappedNativeScope*
     GetScope()   const {return mScope;}
 
     XPCJSRuntime*
     GetRuntime() const {return mScope->GetRuntime();}
 
     JSObject*
@@ -1332,16 +1343,17 @@ public:
     GetClassInfo()     const {return mClassInfo;}
 
     XPCNativeSet*
     GetSet()           const {return mSet;}
 
     nsIXPCScriptable*
     GetScriptable() const { return mScriptable; }
 
+    bool CallPostCreatePrototype();
     void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
     void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
 
     void SystemIsBeingShutDown();
 
     void DebugDump(int16_t depth);
 
     void TraceSelf(JSTracer* trc) {
@@ -1375,17 +1387,17 @@ protected:
     XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
     XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r) = delete;
 
     // hide ctor
     XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                           nsIClassInfo* ClassInfo,
                           already_AddRefed<XPCNativeSet>&& Set);
 
-    bool Init(nsIXPCScriptable* scriptable);
+    bool Init(nsIXPCScriptable* scriptable, bool callPostCreatePrototype);
 
 private:
 #ifdef DEBUG
     static int32_t gDEBUG_LiveProtoCount;
 #endif
 
 private:
     XPCWrappedNativeScope*   mScope;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1446,16 +1446,21 @@ static bool
 IsWindow(JSContext* cx, JSObject* wrapper)
 {
     return !!AsWindow(cx, wrapper);
 }
 
 void
 XPCWrappedNativeXrayTraits::preserveWrapper(JSObject* target)
 {
+    XPCWrappedNative* wn = XPCWrappedNative::Get(target);
+    RefPtr<nsXPCClassInfo> ci;
+    CallQueryInterface(wn->Native(), getter_AddRefs(ci));
+    if (ci)
+        ci->PreserveWrapper(wn->Native());
 }
 
 static bool
 XrayToString(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wrapper,
                                                   HandleObject holder, HandleId id,
--- a/xpcom/base/nsISupportsImpl.h
+++ b/xpcom/base/nsISupportsImpl.h
@@ -34,16 +34,22 @@
                 "Make this class's destructor non-public");
 
 inline nsISupports*
 ToSupports(nsISupports* aSupports)
 {
   return aSupports;
 }
 
+inline nsISupports*
+ToCanonicalSupports(nsISupports* aSupports)
+{
+  return nullptr;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Macros to help detect thread-safety:
 
 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
 
 #include "prthread.h" /* needed for thread-safety checks */
 
 class nsAutoOwningThread
--- a/xpcom/components/nsIClassInfo.idl
+++ b/xpcom/components/nsIClassInfo.idl
@@ -56,16 +56,22 @@ interface nsIClassInfo : nsISupports
      */
     const uint32_t SINGLETON            = 1 << 0;
     const uint32_t THREADSAFE           = 1 << 1;
     const uint32_t MAIN_THREAD_ONLY     = 1 << 2;
     const uint32_t DOM_OBJECT           = 1 << 3;
     const uint32_t PLUGIN_OBJECT        = 1 << 4;
     const uint32_t SINGLETON_CLASSINFO  = 1 << 5;
 
+    /**
+     * 'flags' attribute bitflag: whether objects of this type implement
+     * nsIContent.
+     */
+    const uint32_t CONTENT_NODE         = 1 << 6;
+
     // The high order bit is RESERVED for consumers of these flags. 
     // No implementor of this interface should ever return flags 
     // with this bit set.
     const uint32_t RESERVED             = 1 << 31;
 
 
     readonly attribute uint32_t flags;