Bug 1267186 - Split lookup of WebIDL DOM class names from lookup of DOMCI DOM class names. r=bz.
☠☠ backed out by b6c20b0bbaab ☠ ☠
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 09 Mar 2016 15:48:12 +0100
changeset 321098 d385df62c0e7f25dc846d4992638f1c63491a788
parent 321097 e8256ae1b2672250240077ccdd6a97557b34526b
child 321099 87c6479a51c6c40ef291ae3a4b89f447d67b2ca8
push id9671
push userraliiev@mozilla.com
push dateMon, 06 Jun 2016 20:27:52 +0000
treeherdermozilla-aurora@cea65ca3d0bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1267186
milestone49.0a1
Bug 1267186 - Split lookup of WebIDL DOM class names from lookup of DOMCI DOM class names. r=bz.
dom/base/nsDOMClassInfo.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsScriptNameSpaceManager.cpp
dom/base/nsScriptNameSpaceManager.h
dom/bindings/BindingUtils.cpp
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/WebIDLGlobalNameHash.cpp
dom/bindings/WebIDLGlobalNameHash.h
dom/bindings/moz.build
js/xpconnect/src/xpcpublic.h
layout/build/nsLayoutStatics.cpp
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1761,108 +1761,16 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
 
   // The class_name had better match our name
   MOZ_ASSERT(name.Equals(class_name));
 
   NS_ENSURE_TRUE(class_name, NS_ERROR_UNEXPECTED);
 
   nsresult rv = NS_OK;
 
-  if (name_struct->mType == nsGlobalNameStruct::eTypeNewDOMBinding ||
-      name_struct->mType == nsGlobalNameStruct::eTypeClassProto ||
-      name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
-    // Lookup new DOM bindings.
-    DefineInterface getOrCreateInterfaceObject =
-      name_struct->mDefineDOMInterface;
-    if (getOrCreateInterfaceObject) {
-      if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
-          !OldBindingConstructorEnabled(name_struct, aWin, cx)) {
-        return NS_OK;
-      }
-
-      ConstructorEnabled* checkEnabledForScope = name_struct->mConstructorEnabled;
-      // We do the enabled check on the current compartment of cx, but for the
-      // actual object we pass in the underlying object in the Xray case.  That
-      // way the callee can decide whether to allow access based on the caller
-      // or the window being touched.
-      JS::Rooted<JSObject*> global(cx,
-        js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
-      if (!global) {
-        return NS_ERROR_DOM_SECURITY_ERR;
-      }
-      if (checkEnabledForScope && !checkEnabledForScope(cx, global)) {
-        return NS_OK;
-      }
-
-      // The DOM constructor resolve machinery interacts with Xrays in tricky
-      // ways, and there are some asymmetries that are important to understand.
-      //
-      // In the regular (non-Xray) case, we only want to resolve constructors
-      // once (so that if they're deleted, they don't reappear). We do this by
-      // stashing the constructor in a slot on the global, such that we can see
-      // during resolve whether we've created it already. This is rather
-      // memory-intensive, so we don't try to maintain these semantics when
-      // manipulating a global over Xray (so the properties just re-resolve if
-      // they've been deleted).
-      //
-      // Unfortunately, there's a bit of an impedance-mismatch between the Xray
-      // and non-Xray machinery. The Xray machinery wants an API that returns a
-      // JS::PropertyDescriptor, so that the resolve hook doesn't have to get
-      // snared up with trying to define a property on the Xray holder. At the
-      // same time, the DefineInterface callbacks are set up to define things
-      // directly on the global.  And re-jiggering them to return property
-      // descriptors is tricky, because some DefineInterface callbacks define
-      // multiple things (like the Image() alias for HTMLImageElement).
-      //
-      // So the setup is as-follows:
-      //
-      // * The resolve function takes a JS::PropertyDescriptor, but in the
-      //   non-Xray case, callees may define things directly on the global, and
-      //   set the value on the property descriptor to |undefined| to indicate
-      //   that there's nothing more for the caller to do. We assert against
-      //   this behavior in the Xray case.
-      //
-      // * We make sure that we do a non-Xray resolve first, so that all the
-      //   slots are set up. In the Xray case, this means unwrapping and doing
-      //   a non-Xray resolve before doing the Xray resolve.
-      //
-      // This all could use some grand refactoring, but for now we just limp
-      // along.
-      if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
-        JS::Rooted<JSObject*> interfaceObject(cx);
-        {
-          JSAutoCompartment ac(cx, global);
-          interfaceObject = getOrCreateInterfaceObject(cx, global, id, false);
-        }
-        if (NS_WARN_IF(!interfaceObject)) {
-          return NS_ERROR_FAILURE;
-        }
-        if (!JS_WrapObject(cx, &interfaceObject)) {
-          return NS_ERROR_FAILURE;
-        }
-
-        FillPropertyDescriptor(desc, obj, 0, JS::ObjectValue(*interfaceObject));
-      } else {
-        JS::Rooted<JSObject*> interfaceObject(cx,
-          getOrCreateInterfaceObject(cx, obj, id, true));
-        if (NS_WARN_IF(!interfaceObject)) {
-          return NS_ERROR_FAILURE;
-        }
-        // We've already defined the property.  We indicate this to the caller
-        // by filling a property descriptor with JS::UndefinedValue() as the
-        // value.  We still have to fill in a property descriptor, though, so
-        // that the caller knows the property is in fact on this object.  It
-        // doesn't matter what we pass for the "readonly" argument here.
-        FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
-      }
-
-      return NS_OK;
-    }
-  }
-
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     if (!OldBindingConstructorEnabled(name_struct, aWin, cx)) {
       return NS_OK;
     }
 
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
     // set up the prototype chain.  This will go ahead and define things on the
     // actual window's global.
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -224,16 +224,17 @@
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/U2F.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -4494,16 +4495,25 @@ nsGlobalWindow::DoResolve(JSContext* aCx
 
   // Note: Keep this in sync with MayResolve.
 
   // Note: The infallibleInit call in GlobalResolve depends on this check.
   if (!JSID_IS_STRING(aId)) {
     return true;
   }
 
+  bool found;
+  if (!WebIDLGlobalNameHash::DefineIfEnabled(aCx, aObj, aId, aDesc, &found)) {
+    return false;
+  }
+
+  if (found) {
+    return true;
+  }
+
   nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
   if (NS_FAILED(rv)) {
     return Throw(aCx, rv);
   }
 
   return true;
 }
 
@@ -4522,16 +4532,20 @@ nsGlobalWindow::MayResolve(jsid aId)
   }
 
   if (aId == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_CONTROLLERS)) {
     // We only resolve .controllers in release builds and on non-chrome windows,
     // but let's not worry about any of that stuff.
     return true;
   }
 
+  if (WebIDLGlobalNameHash::MayResolve(aId)) {
+    return true;
+  }
+
   nsScriptNameSpaceManager *nameSpaceManager = PeekNameSpaceManager();
   if (!nameSpaceManager) {
     // Really shouldn't happen.  Fail safe.
     return true;
   }
 
   nsAutoString name;
   AssignJSFlatString(name, JSID_TO_FLAT_STRING(aId));
@@ -4545,22 +4559,23 @@ nsGlobalWindow::GetOwnPropertyNames(JSCo
 {
   MOZ_ASSERT(IsInnerWindow());
   // "Components" is marked as enumerable but only resolved on demand :-/.
   //aNames.AppendElement(NS_LITERAL_STRING("Components"));
 
   nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
   if (nameSpaceManager) {
     JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
+
+    WebIDLGlobalNameHash::GetNames(aCx, wrapper, aNames);
+
     for (auto i = nameSpaceManager->GlobalNameIter(); !i.Done(); i.Next()) {
       const GlobalNameMapEntry* entry = i.Get();
       if (nsWindowSH::NameStructEnabled(aCx, this, entry->mKey,
-                                        entry->mGlobalName) &&
-          (!entry->mGlobalName.mConstructorEnabled ||
-           entry->mGlobalName.mConstructorEnabled(aCx, wrapper))) {
+                                        entry->mGlobalName)) {
         aNames.AppendElement(entry->mKey);
       }
     }
   }
 }
 
 /* static */ bool
 nsGlobalWindow::IsPrivilegedChromeWindow(JSContext* aCx, JSObject* aObj)
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -20,25 +20,27 @@
 #include "nsXPIDLString.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsHashKeys.h"
 #include "nsDOMClassInfo.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
 #include "nsISimpleEnumerator.h"
-
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #define NS_INTERFACE_PREFIX "nsI"
 #define NS_DOM_INTERFACE_PREFIX "nsIDOM"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 static PLDHashNumber
 GlobalNameHashHashKey(const void *key)
 {
   const nsAString *str = static_cast<const nsAString *>(key);
   return HashString(*str);
 }
 
@@ -90,40 +92,43 @@ static const PLDHashTableOps hash_table_
 {
   GlobalNameHashHashKey,
   GlobalNameHashMatchEntry,
   PLDHashTable::MoveEntryStub,
   GlobalNameHashClearEntry,
   GlobalNameHashInitEntry
 };
 
-#define GLOBALNAME_HASHTABLE_INITIAL_LENGTH   512
+#define GLOBALNAME_HASHTABLE_INITIAL_LENGTH          32
 
 nsScriptNameSpaceManager::nsScriptNameSpaceManager()
   : mGlobalNames(&hash_table_ops, sizeof(GlobalNameMapEntry),
                  GLOBALNAME_HASHTABLE_INITIAL_LENGTH)
 {
   MOZ_COUNT_CTOR(nsScriptNameSpaceManager);
 }
 
 nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
 {
   UnregisterWeakMemoryReporter(this);
   MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
 }
 
 nsGlobalNameStruct *
-nsScriptNameSpaceManager::AddToHash(const nsAString *aKey,
+nsScriptNameSpaceManager::AddToHash(const char *aKey,
                                     const char16_t **aClassName)
 {
-  auto entry = static_cast<GlobalNameMapEntry*>(mGlobalNames.Add(aKey, fallible));
+  NS_ConvertASCIItoUTF16 key(aKey);
+  auto entry = static_cast<GlobalNameMapEntry*>(mGlobalNames.Add(&key, fallible));
   if (!entry) {
     return nullptr;
   }
 
+  WebIDLGlobalNameHash::Remove(aKey, key.Length());
+
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
 
   return &entry->mGlobalName;
 }
 
 void
@@ -225,18 +230,17 @@ nsScriptNameSpaceManager::RegisterClassN
 
   // If a external constructor is already defined with aClassName we
   // won't overwrite it.
 
   if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
     return NS_OK;
   }
 
-  NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
-               s->mType == nsGlobalNameStruct::eTypeNewDOMBinding,
+  NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized,
                "Whaaa, JS environment name clash!");
 
   s->mType = nsGlobalNameStruct::eTypeClassConstructor;
   s->mDOMClassInfoID = aDOMClassInfoID;
   s->mChromeOnly = aPrivileged;
   s->mAllowXBL = aXBLAllowed;
 
   return NS_OK;
@@ -249,18 +253,17 @@ nsScriptNameSpaceManager::RegisterClassP
 {
   NS_ENSURE_ARG_POINTER(aConstructorProtoIID);
 
   *aFoundOld = false;
 
   nsGlobalNameStruct *s = AddToHash(aClassName);
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
-  if (s->mType != nsGlobalNameStruct::eTypeNotInitialized &&
-      s->mType != nsGlobalNameStruct::eTypeNewDOMBinding) {
+  if (s->mType != nsGlobalNameStruct::eTypeNotInitialized) {
     *aFoundOld = true;
 
     return NS_OK;
   }
 
   s->mType = nsGlobalNameStruct::eTypeClassProto;
   s->mIID = *aConstructorProtoIID;
 
@@ -344,18 +347,17 @@ nsScriptNameSpaceManager::OperateCategor
   // Copy CID onto the stack, so we can free it right away and avoid having
   // to add cleanup code at every exit point from this function.
   nsCID cid = *cidPtr;
   free(cidPtr);
 
   nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
   NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
 
-  if (s->mType == nsGlobalNameStruct::eTypeNotInitialized ||
-      s->mType == nsGlobalNameStruct::eTypeNewDOMBinding) {
+  if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
     s->mType = type;
     s->mCID = cid;
     s->mChromeOnly =
       strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0;
   } else {
     NS_WARNING("Global script name not overwritten!");
   }
 
@@ -409,31 +411,16 @@ nsScriptNameSpaceManager::Observe(nsISup
   }
 
   // TODO: we could observe NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID
   // but we are safe without it. See bug 600460.
 
   return NS_OK;
 }
 
-void
-nsScriptNameSpaceManager::RegisterDefineDOMInterface(const nsAFlatString& aName,
-    mozilla::dom::DefineInterface aDefineDOMInterface,
-    mozilla::dom::ConstructorEnabled* aConstructorEnabled)
-{
-  nsGlobalNameStruct *s = AddToHash(&aName);
-  if (s) {
-    if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
-      s->mType = nsGlobalNameStruct::eTypeNewDOMBinding;
-    }
-    s->mDefineDOMInterface = aDefineDOMInterface;
-    s->mConstructorEnabled = aConstructorEnabled;
-  }
-}
-
 MOZ_DEFINE_MALLOC_SIZE_OF(ScriptNameSpaceManagerMallocSizeOf)
 
 NS_IMETHODIMP
 nsScriptNameSpaceManager::CollectReports(
   nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES,
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -28,45 +28,34 @@
 #include "nsString.h"
 #include "nsID.h"
 #include "PLDHashTable.h"
 #include "nsDOMClassInfo.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "xpcpublic.h"
 
-
 struct nsGlobalNameStruct
 {
   enum nametype {
     eTypeNotInitialized,
-    eTypeNewDOMBinding,
     eTypeProperty,
     eTypeExternalConstructor,
     eTypeClassConstructor,
     eTypeClassProto,
   } mType;
 
-  // mChromeOnly is only used for structs that define non-WebIDL things
-  // (possibly in addition to WebIDL ones).  In particular, it's not even
-  // initialized for eTypeNewDOMBinding structs.
   bool mChromeOnly : 1;
   bool mAllowXBL : 1;
 
   union {
     int32_t mDOMClassInfoID; // eTypeClassConstructor
     nsIID mIID; // eTypeClassProto
-    nsCID mCID; // All other types except eTypeNewDOMBinding
+    nsCID mCID; // All other types
   };
-
-  // For new style DOM bindings.
-  mozilla::dom::DefineInterface mDefineDOMInterface;
-
-  // May be null if enabled unconditionally
-  mozilla::dom::ConstructorEnabled* mConstructorEnabled;
 };
 
 class GlobalNameMapEntry : public PLDHashEntryHdr
 {
 public:
   // Our hash table ops don't care about the order of these members.
   nsString mKey;
   nsGlobalNameStruct mGlobalName;
@@ -107,29 +96,16 @@ public:
                              bool aPrivileged,
                              bool aXBLAllowed,
                              const char16_t **aResult);
 
   nsresult RegisterClassProto(const char *aClassName,
                               const nsIID *aConstructorProtoIID,
                               bool *aFoundOld);
 
-  void RegisterDefineDOMInterface(const nsAFlatString& aName,
-    mozilla::dom::DefineInterface aDefineDOMInterface,
-    mozilla::dom::ConstructorEnabled* aConstructorEnabled);
-  template<size_t N>
-  void RegisterDefineDOMInterface(const char16_t (&aKey)[N],
-    mozilla::dom::DefineInterface aDefineDOMInterface,
-    mozilla::dom::ConstructorEnabled* aConstructorEnabled)
-  {
-    nsLiteralString key(aKey);
-    return RegisterDefineDOMInterface(key, aDefineDOMInterface,
-                                      aConstructorEnabled);
-  }
-
   class NameIterator : public PLDHashTable::Iterator
   {
   public:
     typedef PLDHashTable::Iterator Base;
     explicit NameIterator(PLDHashTable* aTable) : Base(aTable) {}
     NameIterator(NameIterator&& aOther) : Base(mozilla::Move(aOther.mTable)) {}
 
     const GlobalNameMapEntry* Get() const
@@ -150,32 +126,24 @@ public:
 
 private:
   virtual ~nsScriptNameSpaceManager();
 
   // Adds a new entry to the hash and returns the nsGlobalNameStruct
   // that aKey will be mapped to. If mType in the returned
   // nsGlobalNameStruct is != eTypeNotInitialized, an entry for aKey
   // already existed.
-  nsGlobalNameStruct *AddToHash(const nsAString *aKey,
+  nsGlobalNameStruct *AddToHash(const char *aKey,
                                 const char16_t **aClassName = nullptr);
-  nsGlobalNameStruct *AddToHash(const char *aKey,
-                                const char16_t **aClassName = nullptr)
-  {
-    NS_ConvertASCIItoUTF16 key(aKey);
-    return AddToHash(&key, aClassName);
-  }
+
   // Removes an existing entry from the hash.
   void RemoveFromHash(const nsAString *aKey);
 
   nsresult FillHash(nsICategoryManager *aCategoryManager,
                     const char *aCategory);
-  nsresult RegisterInterface(const char* aIfName,
-                             const nsIID *aIfIID,
-                             bool* aFoundOld);
 
   /**
    * Add a new category entry into the hash table.
    * Only some categories can be added (see the beginning of the definition).
    * The other ones will be ignored.
    *
    * @aCategoryManager Instance of the category manager service.
    * @aCategory        Category where the entry comes from.
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -40,16 +40,17 @@
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLObjectElement.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/HTMLAppletElementBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ResolveSystemBinding.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsDOMClassInfo.h"
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
 
 namespace mozilla {
@@ -2931,30 +2932,25 @@ static bool sRegisteredDOMNames = false;
 
 nsresult
 RegisterDOMNames()
 {
   if (sRegisteredDOMNames) {
     return NS_OK;
   }
 
+  // Register new DOM bindings
+  WebIDLGlobalNameHash::Init();
+
   nsresult rv = nsDOMClassInfo::Init();
   if (NS_FAILED(rv)) {
     NS_ERROR("Could not initialize nsDOMClassInfo");
     return rv;
   }
 
-  // Register new DOM bindings
-  nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
-  if (!nameSpaceManager) {
-    NS_ERROR("Could not initialize nsScriptNameSpaceManager");
-    return NS_ERROR_FAILURE;
-  }
-  mozilla::dom::Register(nameSpaceManager);
-
   sRegisteredDOMNames = true;
 
   return NS_OK;
 }
 
 /* static */
 bool
 CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -12052,24 +12052,24 @@ class CGDescriptor(CGThing):
             cgThings.append(CGEnumerateHook(descriptor))
 
         if descriptor.hasNamedPropertiesObject:
             cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
 
         if descriptor.interface.hasInterfacePrototypeObject():
             cgThings.append(CGPrototypeJSClass(descriptor, properties))
 
-        if descriptor.interface.hasInterfaceObject():
-            cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
-
         if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.isNavigatorProperty()) and
             not descriptor.interface.isExternal() and
             descriptor.isExposedConditionally()):
             cgThings.append(CGConstructorEnabled(descriptor))
 
+        if descriptor.registersGlobalNamesOnWindow:
+            cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+
         if (descriptor.interface.hasMembersInSlots() and
             descriptor.interface.hasChildInterfaces()):
             raise TypeError("We don't support members in slots on "
                             "non-leaf interfaces like %s" %
                             descriptor.interface.identifier.name)
 
         if descriptor.concrete:
             if descriptor.proxy:
@@ -13052,58 +13052,63 @@ class CGResolveSystemBinding(CGAbstractM
         return CGList([CGGeneric("MOZ_ASSERT(NS_IsMainThread());\n"),
                        jsidDecls,
                        jsidInits,
                        definitions,
                        CGGeneric("return true;\n")],
                       "\n").define()
 
 
-class CGRegisterProtos(CGAbstractMethod):
+def getGlobalNames(config):
+    names = []
+    for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
+        names.append((desc.name, desc))
+        names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
+    return names
+
+class CGGlobalNamesString(CGGeneric):
     def __init__(self, config):
-        CGAbstractMethod.__init__(self, None, 'Register', 'void',
-                                  [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
+        globalNames = getGlobalNames(config)
+        currentOffset = 0
+        strings = []
+        for (name, _) in globalNames:
+            strings.append('/* %i */ "%s\\0"' % (currentOffset, name))
+            currentOffset += len(name) + 1 # Add trailing null.
+        define = fill("""
+            const uint32_t WebIDLGlobalNameHash::sCount = ${count};
+
+            const char WebIDLGlobalNameHash::sNames[] =
+              $*{strings}
+
+            """,
+            count=len(globalNames),
+            strings="\n".join(strings) + ";\n")
+
+        CGGeneric.__init__(self, define=define)
+
+
+class CGRegisterGlobalNames(CGAbstractMethod):
+    def __init__(self, config):
+        CGAbstractMethod.__init__(self, None, 'RegisterWebIDLGlobalNames',
+                                  'void', [])
         self.config = config
 
-    def _defineMacro(self):
-        return dedent("""
-            #define REGISTER_PROTO(_dom_class, _ctor_check) \\
-              aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_class), _dom_class##Binding::DefineDOMInterface, _ctor_check);
-            #define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _ctor_check) \\
-              aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _ctor_check);
-            """)
-
-    def _undefineMacro(self):
-        return dedent("""
-            #undef REGISTER_CONSTRUCTOR
-            #undef REGISTER_PROTO
-            """)
-
-    def _registerProtos(self):
+    def definition_body(self):
         def getCheck(desc):
             if not desc.isExposedConditionally():
                 return "nullptr"
             return "%sBinding::ConstructorEnabled" % desc.name
-        lines = []
-        for desc in self.config.getDescriptors(hasInterfaceObject=True,
-                                               isExternal=False,
-                                               workers=False,
-                                               isExposedInWindow=True,
-                                               register=True):
-            lines.append("REGISTER_PROTO(%s, %s);\n" % (desc.name, getCheck(desc)))
-            lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);\n" % (n.identifier.name, desc.name, getCheck(desc))
-                         for n in desc.interface.namedConstructors)
-        return ''.join(lines)
-
-    def indent_body(self, body):
-        # Don't indent the body of this method, as it's all preprocessor gunk.
-        return body
-
-    def definition_body(self):
-        return "\n" + self._defineMacro() + "\n" + self._registerProtos() + "\n" + self._undefineMacro()
+
+        define = ""
+        currentOffset = 0
+        for (name, desc) in getGlobalNames(self.config):
+            length = len(name)
+            define += "WebIDLGlobalNameHash::Register(%i, %i, %sBinding::DefineDOMInterface, %s);\n" % (currentOffset, length, desc.name, getCheck(desc))
+            currentOffset += length + 1 # Add trailing null.
+        return define
 
 
 def dependencySortObjects(objects, dependencyGetter, nameGetter):
     """
     Sort IDL objects with dependencies on each other such that if A
     depends on B then B will come before A.  This is needed for
     declaring C++ classes in the right order, for example.  Objects
     that have no dependencies are just sorted by name.
@@ -16203,30 +16208,30 @@ class GlobalGenRoots():
         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 
         # Done.
         return curr
 
     @staticmethod
     def RegisterBindings(config):
 
-        curr = CGRegisterProtos(config)
+        curr = CGList([CGGlobalNamesString(config), CGRegisterGlobalNames(config)])
 
         # Wrap all of that in our namespaces.
         curr = CGNamespace.build(['mozilla', 'dom'],
                                  CGWrapper(curr, post='\n'))
         curr = CGWrapper(curr, post='\n')
 
         # Add the includes
         defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
                           for desc in config.getDescriptors(hasInterfaceObject=True,
                                                             workers=False,
                                                             isExposedInWindow=True,
                                                             register=True)]
-        defineIncludes.append('nsScriptNameSpaceManager.h')
+        defineIncludes.append('mozilla/dom/WebIDLGlobalNameHash.h')
         defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
                                for desc in config.getDescriptors(isNavigatorProperty=True,
                                                                  workers=False,
                                                                  register=True)])
         curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
                          curr)
 
         # Add include guards.
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -805,16 +805,24 @@ class Descriptor(DescriptorProvider):
     def isGlobal(self):
         """
         Returns true if this is the primary interface for a global object
         of some sort.
         """
         return (self.interface.getExtendedAttribute("Global") or
                 self.interface.getExtendedAttribute("PrimaryGlobal"))
 
+    @property
+    def registersGlobalNamesOnWindow(self):
+        return (not self.interface.isExternal() and
+                self.interface.hasInterfaceObject() and
+                not self.workers and
+                self.interface.isExposedInWindow() and
+                self.register)
+
 
 # Some utility methods
 def getTypesFromDescriptor(descriptor):
     """
     Get all argument and return types for all members of the descriptor
     """
     members = [m for m in descriptor.interface.members]
     if descriptor.interface.ctor():
new file mode 100644
--- /dev/null
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebIDLGlobalNameHash.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/dom/DOMJSProxyHandler.h"
+#include "mozilla/dom/RegisterBindings.h"
+#include "nsIMemoryReporter.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+
+struct MOZ_STACK_CLASS WebIDLNameTableKey
+{
+  explicit WebIDLNameTableKey(JSFlatString* aJSString)
+    : mLength(js::GetFlatStringLength(aJSString))
+  {
+    JSLinearString* jsString = js::FlatStringToLinearString(aJSString);
+    if (js::LinearStringHasLatin1Chars(jsString)) {
+      mLatin1String = reinterpret_cast<const char*>(
+        js::GetLatin1LinearStringChars(mNogc, jsString));
+      mTwoBytesString = nullptr;
+      mHash = mLatin1String ? HashString(mLatin1String, mLength) : 0;
+    } else {
+      mLatin1String = nullptr;
+      mTwoBytesString = js::GetTwoByteLinearStringChars(mNogc, jsString);
+      mHash = mTwoBytesString ? HashString(mTwoBytesString, mLength) : 0;
+    }
+  }
+  explicit WebIDLNameTableKey(const char* aString, size_t aLength)
+    : mLatin1String(aString),
+      mTwoBytesString(nullptr),
+      mLength(aLength),
+      mHash(HashString(aString, aLength))
+  {
+    MOZ_ASSERT(aString[aLength] == '\0');
+  }
+
+  JS::AutoCheckCannotGC mNogc;
+  const char* mLatin1String;
+  const char16_t* mTwoBytesString;
+  size_t mLength;
+  uint32_t mHash;
+};
+
+struct WebIDLNameTableEntry : public PLDHashEntryHdr
+{
+  typedef const WebIDLNameTableKey& KeyType;
+  typedef const WebIDLNameTableKey* KeyTypePointer;
+
+  explicit WebIDLNameTableEntry(KeyTypePointer aKey)
+  {}
+  WebIDLNameTableEntry(WebIDLNameTableEntry&& aEntry)
+    : mNameOffset(aEntry.mNameOffset),
+      mNameLength(aEntry.mNameLength),
+      mDefine(aEntry.mDefine),
+      mEnabled(aEntry.mEnabled)
+  {}
+  ~WebIDLNameTableEntry()
+  {}
+
+  bool KeyEquals(KeyTypePointer aKey) const
+  {
+    if (mNameLength != aKey->mLength) {
+      return false;
+    }
+
+    const char* name = WebIDLGlobalNameHash::sNames + mNameOffset;
+
+    if (aKey->mLatin1String) {
+      return PodEqual(aKey->mLatin1String, name, aKey->mLength);
+    }
+
+    return nsCharTraits<char16_t>::compareASCII(aKey->mTwoBytesString, name,
+                                                aKey->mLength) == 0;
+  }
+
+  static KeyTypePointer KeyToPointer(KeyType aKey)
+  {
+    return &aKey;
+  }
+
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    return aKey->mHash;
+  }
+
+  enum { ALLOW_MEMMOVE = true };
+
+  uint16_t mNameOffset;
+  uint16_t mNameLength;
+  WebIDLGlobalNameHash::DefineGlobalName mDefine;
+  // May be null if enabled unconditionally
+  WebIDLGlobalNameHash::ConstructorEnabled* mEnabled;
+};
+
+static nsTHashtable<WebIDLNameTableEntry>* sWebIDLGlobalNames;
+
+class WebIDLGlobalNamesHashReporter final : public nsIMemoryReporter
+{
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+  ~WebIDLGlobalNamesHashReporter() {}
+
+public:
+  NS_DECL_ISUPPORTS
+
+  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+                            nsISupports* aData, bool aAnonymize) override
+  {
+    int64_t amount =
+      sWebIDLGlobalNames ?
+      sWebIDLGlobalNames->ShallowSizeOfIncludingThis(MallocSizeOf) : 0;
+
+    return MOZ_COLLECT_REPORT("explicit/dom/webidl-globalnames", KIND_HEAP,
+                              UNITS_BYTES, amount,
+                              "Memory used by the hash table for WebIDL's "
+                              "global names.");
+  }
+};
+
+NS_IMPL_ISUPPORTS(WebIDLGlobalNamesHashReporter, nsIMemoryReporter)
+
+/* static */
+void
+WebIDLGlobalNameHash::Init()
+{
+  sWebIDLGlobalNames = new nsTHashtable<WebIDLNameTableEntry>(sCount);
+  RegisterWebIDLGlobalNames();
+
+  RegisterStrongMemoryReporter(new WebIDLGlobalNamesHashReporter());
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Shutdown()
+{
+  delete sWebIDLGlobalNames;
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Register(uint16_t aNameOffset, uint16_t aNameLength,
+                               DefineGlobalName aDefine,
+                               ConstructorEnabled* aEnabled)
+{
+  const char* name = sNames + aNameOffset;
+  WebIDLNameTableKey key(name, aNameLength);
+  WebIDLNameTableEntry* entry = sWebIDLGlobalNames->PutEntry(key);
+  entry->mNameOffset = aNameOffset;
+  entry->mNameLength = aNameLength;
+  entry->mDefine = aDefine;
+  entry->mEnabled = aEnabled;
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::Remove(const char* aName, uint32_t aLength)
+{
+  WebIDLNameTableKey key(aName, aLength);
+  sWebIDLGlobalNames->RemoveEntry(key);
+}
+
+/* static */
+bool
+WebIDLGlobalNameHash::DefineIfEnabled(JSContext* aCx,
+                                      JS::Handle<JSObject*> aObj,
+                                      JS::Handle<jsid> aId,
+                                      JS::MutableHandle<JS::PropertyDescriptor> aDesc,
+                                      bool* aFound)
+{
+  MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!");
+
+  const WebIDLNameTableEntry* entry;
+  {
+    WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
+    entry = sWebIDLGlobalNames->GetEntry(key);
+  }
+
+  if (!entry) {
+    *aFound = false;
+    return true;
+  }
+
+  *aFound = true;
+
+  ConstructorEnabled* checkEnabledForScope = entry->mEnabled;
+  // We do the enabled check on the current compartment of aCx, but for the
+  // actual object we pass in the underlying object in the Xray case.  That
+  // way the callee can decide whether to allow access based on the caller
+  // or the window being touched.
+  JS::Rooted<JSObject*> global(aCx,
+    js::CheckedUnwrap(aObj, /* stopAtWindowProxy = */ false));
+  if (!global) {
+    return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
+  }
+
+  {
+    DebugOnly<nsGlobalWindow*> win;
+    MOZ_ASSERT(NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, win)));
+  }
+
+  if (checkEnabledForScope && !checkEnabledForScope(aCx, global)) {
+    return true;
+  }
+
+  // The DOM constructor resolve machinery interacts with Xrays in tricky
+  // ways, and there are some asymmetries that are important to understand.
+  //
+  // In the regular (non-Xray) case, we only want to resolve constructors
+  // once (so that if they're deleted, they don't reappear). We do this by
+  // stashing the constructor in a slot on the global, such that we can see
+  // during resolve whether we've created it already. This is rather
+  // memory-intensive, so we don't try to maintain these semantics when
+  // manipulating a global over Xray (so the properties just re-resolve if
+  // they've been deleted).
+  //
+  // Unfortunately, there's a bit of an impedance-mismatch between the Xray
+  // and non-Xray machinery. The Xray machinery wants an API that returns a
+  // JS::PropertyDescriptor, so that the resolve hook doesn't have to get
+  // snared up with trying to define a property on the Xray holder. At the
+  // same time, the DefineInterface callbacks are set up to define things
+  // directly on the global.  And re-jiggering them to return property
+  // descriptors is tricky, because some DefineInterface callbacks define
+  // multiple things (like the Image() alias for HTMLImageElement).
+  //
+  // So the setup is as-follows:
+  //
+  // * The resolve function takes a JS::PropertyDescriptor, but in the
+  //   non-Xray case, callees may define things directly on the global, and
+  //   set the value on the property descriptor to |undefined| to indicate
+  //   that there's nothing more for the caller to do. We assert against
+  //   this behavior in the Xray case.
+  //
+  // * We make sure that we do a non-Xray resolve first, so that all the
+  //   slots are set up. In the Xray case, this means unwrapping and doing
+  //   a non-Xray resolve before doing the Xray resolve.
+  //
+  // This all could use some grand refactoring, but for now we just limp
+  // along.
+  if (xpc::WrapperFactory::IsXrayWrapper(aObj)) {
+    JS::Rooted<JSObject*> interfaceObject(aCx);
+    {
+      JSAutoCompartment ac(aCx, global);
+      interfaceObject = entry->mDefine(aCx, global, aId, false);
+    }
+    if (NS_WARN_IF(!interfaceObject)) {
+      return Throw(aCx, NS_ERROR_FAILURE);
+    }
+    if (!JS_WrapObject(aCx, &interfaceObject)) {
+      return Throw(aCx, NS_ERROR_FAILURE);
+    }
+
+    FillPropertyDescriptor(aDesc, aObj, 0, JS::ObjectValue(*interfaceObject));
+    return true;
+  }
+
+  JS::Rooted<JSObject*> interfaceObject(aCx,
+                                        entry->mDefine(aCx, aObj, aId, true));
+  if (NS_WARN_IF(!interfaceObject)) {
+    return Throw(aCx, NS_ERROR_FAILURE);
+  }
+
+  // We've already defined the property.  We indicate this to the caller
+  // by filling a property descriptor with JS::UndefinedValue() as the
+  // value.  We still have to fill in a property descriptor, though, so
+  // that the caller knows the property is in fact on this object.  It
+  // doesn't matter what we pass for the "readonly" argument here.
+  FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false);
+
+  return true;
+}
+
+/* static */
+bool
+WebIDLGlobalNameHash::MayResolve(jsid aId)
+{
+  WebIDLNameTableKey key(JSID_TO_FLAT_STRING(aId));
+  return sWebIDLGlobalNames->Contains(key);
+}
+
+/* static */
+void
+WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                               nsTArray<nsString>& aNames)
+{
+  for (auto iter = sWebIDLGlobalNames->Iter(); !iter.Done(); iter.Next()) {
+    const WebIDLNameTableEntry* entry = iter.Get();
+    if (!entry->mEnabled || entry->mEnabled(aCx, aObj)) {
+      AppendASCIItoUTF16(nsDependentCString(sNames + entry->mNameOffset,
+                                            entry->mNameLength),
+                         *aNames.AppendElement());
+    }
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_WebIDLGlobalNameHash_h__
+#define mozilla_dom_WebIDLGlobalNameHash_h__
+
+#include "js/RootingAPI.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+struct WebIDLNameTableEntry;
+
+class WebIDLGlobalNameHash
+{
+public:
+  static void Init();
+  static void Shutdown();
+
+  typedef JSObject*
+  (*DefineGlobalName)(JSContext* cx, JS::Handle<JSObject*> global,
+                      JS::Handle<jsid> id, bool defineOnGlobal);
+
+  // Check whether a constructor should be enabled for the given object.
+  // Note that the object should NOT be an Xray, since Xrays will end up
+  // defining constructors on the underlying object.
+  // This is a typedef for the function type itself, not the function
+  // pointer, so it's more obvious that pointers to a ConstructorEnabled
+  // can be null.
+  typedef bool
+  (ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
+
+  static void Register(uint16_t aNameOffset, uint16_t aNameLength,
+                       DefineGlobalName aDefine, ConstructorEnabled* aEnabled);
+
+  static void Remove(const char* aName, uint32_t aLength);
+
+  // Returns false if something failed. aFound is set to true if the name is in
+  // the hash, whether it's enabled or not.
+  static bool DefineIfEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                              JS::Handle<jsid> aId,
+                              JS::MutableHandle<JS::PropertyDescriptor> aDesc,
+                              bool* aFound);
+
+  static bool MayResolve(jsid aId);
+
+  static void GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                       nsTArray<nsString>& aNames);
+
+private:
+  friend struct WebIDLNameTableEntry;
+
+  // The total number of names that we will add to the hash.
+  // The value of sCount is generated by Codegen.py in RegisterBindings.cpp.
+  static const uint32_t sCount;
+
+  // The names that will be registered in the hash, concatenated as one big
+  // string with \0 as a separator between names.
+  // The value of sNames is generated by Codegen.py in RegisterBindings.cpp.
+  static const char sNames[];
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebIDLGlobalNameHash_h__
--- a/dom/bindings/moz.build
+++ b/dom/bindings/moz.build
@@ -34,16 +34,17 @@ EXPORTS.mozilla.dom += [
     'Nullable.h',
     'PrimitiveConversions.h',
     'RootedDictionary.h',
     'SimpleGlobalObject.h',
     'StructuredClone.h',
     'ToJSValue.h',
     'TypedArray.h',
     'UnionMember.h',
+    'WebIDLGlobalNameHash.h',
 ]
 
 # Generated bindings reference *Binding.h, not mozilla/dom/*Binding.h. And,
 # since we generate exported bindings directly to $(DIST)/include, we need
 # to add that path to the search list.
 #
 # Ideally, binding generation uses the prefixed header file names.
 # Bug 932082 tracks.
@@ -84,16 +85,17 @@ UNIFIED_SOURCES += [
     'CallbackInterface.cpp',
     'CallbackObject.cpp',
     'Date.cpp',
     'DOMJSProxyHandler.cpp',
     'Exceptions.cpp',
     'IterableIterator.cpp',
     'SimpleGlobalObject.cpp',
     'ToJSValue.cpp',
+    'WebIDLGlobalNameHash.cpp',
 ]
 
 SOURCES += [
     'StructuredClone.cpp',
 ]
 
 # Tests for maplike and setlike require bindings to be built, which means they
 # must be included in libxul. This breaks the "no test classes are exported"
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -573,35 +573,16 @@ GetJSRuntime();
 void AddGCCallback(xpcGCCallback cb);
 void RemoveGCCallback(xpcGCCallback cb);
 
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 
-typedef JSObject*
-(*DefineInterface)(JSContext* cx, JS::Handle<JSObject*> global,
-                   JS::Handle<jsid> id, bool defineOnGlobal);
-
-typedef JSObject*
-(*ConstructNavigatorProperty)(JSContext* cx, JS::Handle<JSObject*> naviObj);
-
-// Check whether a constructor should be enabled for the given object.
-// Note that the object should NOT be an Xray, since Xrays will end up
-// defining constructors on the underlying object.
-// This is a typedef for the function type itself, not the function
-// pointer, so it's more obvious that pointers to a ConstructorEnabled
-// can be null.
-typedef bool
-(ConstructorEnabled)(JSContext* cx, JS::Handle<JSObject*> obj);
-
-void
-Register(nsScriptNameSpaceManager* aNameSpaceManager);
-
 /**
  * A test for whether WebIDL methods that should only be visible to
  * chrome or XBL scopes should be exposed.
  */
 bool IsChromeOrXBL(JSContext* cx, JSObject* /* unused */);
 
 } // namespace dom
 } // namespace mozilla
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -126,16 +126,17 @@ using namespace mozilla::system;
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CameraPreferences.h"
 #include "TouchManager.h"
 #include "MediaDecoder.h"
 #include "mozilla/layers/CompositorLRU.h"
 #include "mozilla/dom/devicestorage/DeviceStorageStatics.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPresData.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
 
 #ifdef MOZ_B2G_BT
 #include "mozilla/dom/BluetoothUUID.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::net;
 using namespace mozilla::dom;
@@ -379,16 +380,17 @@ nsLayoutStatics::Shutdown()
   nsAttrValue::Shutdown();
   nsContentUtils::Shutdown();
   nsLayoutStylesheetCache::Shutdown();
   RuleProcessorCache::Shutdown();
 
   ShutdownJSEnvironment();
   nsGlobalWindow::ShutDown();
   nsDOMClassInfo::ShutDown();
+  WebIDLGlobalNameHash::Shutdown();
   nsListControlFrame::Shutdown();
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_ANDROID_OMX
   AndroidMediaPluginHost::Shutdown();
 #endif