Add infrastructure to create main-thread WebIDL globals with XPConnect.
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 08 Apr 2014 20:48:37 +0200
changeset 181845 75c1becb3d9b020d7b9ef583126ca53c302f4eee
parent 181844 3ab5bc52b9c556735d99b7fdb349b055feb8bad7
child 181846 2a388ff19a0a9390651c95642c54d4cd540cd314
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
milestone32.0a1
Add infrastructure to create main-thread WebIDL globals with XPConnect.
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/workers/WorkerScope.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/nsXPConnect.cpp
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2208,17 +2208,17 @@ CreateNativeGlobalForInner(JSContext* aC
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
   if (aNewInner->IsDOMBinding()) {
     aGlobal.set(WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
-                                    nsJSPrincipals::get(aPrincipal)));
+                                    nsJSPrincipals::get(aPrincipal), false));
     if (!aGlobal || !xpc::InitGlobalObject(aCx, aGlobal, flags)) {
       return NS_ERROR_FAILURE;
     }
   } else {
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
       aCx, ToSupports(aNewInner),
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2209,22 +2209,25 @@ IsInCertifiedApp(JSContext* aCx, JSObjec
   if (!NS_IsMainThread()) {
     return GetWorkerPrivateFromContext(aCx)->IsInCertifiedApp();
   }
 
   nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj);
   return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
 }
 
+#ifdef DEBUG
 void
-TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+VerifyTraceProtoAndIfaceCacheCalled(JSTracer *trc, void **thingp,
+                                    JSGCTraceKind kind)
 {
-  MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
-  mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
+    // We don't do anything here, we only want to verify that TraceProtoAndIfaceCache
+    // was called.
 }
+#endif
 
 void
 FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
 {
   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
   mozilla::dom::DestroyProtoAndIfaceCache(aObj);
 }
 
@@ -2422,10 +2425,26 @@ ConvertExceptionToPromise(JSContext* cx,
     ThrowMethodFailedWithDetails(cx, rv, "", "");
     JS_SetPendingException(cx, exn);
     return false;
   }
 
   return WrapNewBindingObject(cx, promise, rval);
 }
 
+/* static */
+void
+CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+{
+  mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
+  xpc::GetCompartmentPrivate(aObj)->scope->TraceSelf(aTrc);
+}
+
+/* static */
+bool
+CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
+                                                  JS::Handle<JSObject*> aGlobal)
+{
+  return XPCWrappedNativeScope::GetNewOrUsed(aCx, aGlobal);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -480,21 +480,46 @@ AllocateProtoAndIfaceCache(JSObject* obj
   MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
 
   ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
 
   js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
                       JS::PrivateValue(protoAndIfaceCache));
 }
 
+#ifdef DEBUG
+void
+VerifyTraceProtoAndIfaceCacheCalled(JSTracer *trc, void **thingp,
+                                    JSGCTraceKind kind);
+
+struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JSTracer
+{
+    bool ok;
+
+    VerifyTraceProtoAndIfaceCacheCalledTracer(JSRuntime *rt)
+      : JSTracer(rt, VerifyTraceProtoAndIfaceCacheCalled), ok(false)
+    {}
+};
+#endif
+
 inline void
 TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj)
 {
   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 
+#ifdef DEBUG
+  if (trc->callback == VerifyTraceProtoAndIfaceCacheCalled) {
+    // We don't do anything here, we only want to verify that
+    // TraceProtoAndIfaceCache was called.
+    reinterpret_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok =
+      true;
+    return;
+  }
+#endif
+
   if (!HasProtoAndIfaceCache(obj))
     return;
   ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
   protoAndIfaceCache->Trace(trc);
 }
 
 inline void
 DestroyProtoAndIfaceCache(JSObject* obj)
@@ -2557,77 +2582,102 @@ IsInPrivilegedApp(JSContext* aCx, JSObje
 /*
  * Helper function for testing whether the given object comes from a
  * certified app.
  */
 bool
 IsInCertifiedApp(JSContext* aCx, JSObject* aObj);
 
 void
-TraceGlobal(JSTracer* aTrc, JSObject* aObj);
-
-void
 FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
 
 bool
 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
               JS::Handle<jsid> aId, JS::MutableHandle<JSObject*> aObjp);
 
 bool
 EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj);
 
-template <class T, JS::Handle<JSObject*> (*ProtoGetter)(JSContext*,
-                                                        JS::Handle<JSObject*>)>
-JSObject*
-CreateGlobal(JSContext* aCx, T* aObject, nsWrapperCache* aCache,
-             const JSClass* aClass, JS::CompartmentOptions& aOptions,
-             JSPrincipals* aPrincipal)
+template <class T>
+struct CreateGlobalOptions
 {
-  MOZ_ASSERT(!NS_IsMainThread());
+  static MOZ_CONSTEXPR_VAR ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
+    ProtoAndIfaceCache::NonWindowLike;
+  // Intl API is broken and makes JS_InitStandardClasses fail intermittently,
+  // see bug 934889.
+  static MOZ_CONSTEXPR_VAR bool ForceInitStandardClassesToFalse = true;
+  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj)
+  {
+    mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
+  }
+  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
+  {
+    MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
 
-  aOptions.setTrace(TraceGlobal);
+    return true;
+  }
+};
 
-  JS::Rooted<JSObject*> global(aCx,
-    JS_NewGlobalObject(aCx, aClass, aPrincipal, JS::DontFireOnNewGlobalHook,
-                       aOptions));
-  if (!global) {
+template <>
+struct CreateGlobalOptions<nsGlobalWindow>
+{
+  static MOZ_CONSTEXPR_VAR ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
+    ProtoAndIfaceCache::WindowLike;
+  static MOZ_CONSTEXPR_VAR bool ForceInitStandardClassesToFalse = false;
+  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
+  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
+};
+
+template <class T, ProtoGetter GetProto>
+bool
+CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
+             const JSClass* aClass, JS::CompartmentOptions& aOptions,
+             JSPrincipals* aPrincipal, bool aInitStandardClasses,
+             JS::MutableHandle<JSObject*> aGlobal)
+{
+  aOptions.setTrace(CreateGlobalOptions<T>::TraceGlobal);
+
+  aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
+                                 JS::DontFireOnNewGlobalHook, aOptions));
+  if (!aGlobal) {
     NS_WARNING("Failed to create global");
     return nullptr;
   }
 
-  JSAutoCompartment ac(aCx, global);
+  JSAutoCompartment ac(aCx, aGlobal);
 
-  dom::AllocateProtoAndIfaceCache(global, ProtoAndIfaceCache::WindowLike);
-
-  js::SetReservedSlot(global, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
-  NS_ADDREF(aObject);
+  js::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aNative));
+  NS_ADDREF(aNative);
 
   aCache->SetIsDOMBinding();
-  aCache->SetWrapper(global);
+  aCache->SetWrapper(aGlobal);
+
+  dom::AllocateProtoAndIfaceCache(aGlobal,
+                                  CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
 
-  /* Intl API is broken and makes this fail intermittently, see bug 934889.
-  if (!JS_InitStandardClasses(aCx, global)) {
-    NS_WARNING("Failed to init standard classes");
-    return nullptr;
-  }
-  */
-
-  JS::Handle<JSObject*> proto = ProtoGetter(aCx, global);
-  NS_ENSURE_TRUE(proto, nullptr);
-
-  if (!JS_SetPrototype(aCx, global, proto)) {
-    NS_WARNING("Failed to set proto");
-    return nullptr;
+  // Call this before doing anything that can GC, the setup of our global needs
+  // to be done before a GC happens.
+  if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
+    return false;
   }
 
-  MOZ_ALWAYS_TRUE(TryPreserveWrapper(global));
+  if (aInitStandardClasses &&
+      !CreateGlobalOptions<T>::ForceInitStandardClassesToFalse &&
+      !JS_InitStandardClasses(aCx, aGlobal)) {
+    NS_WARNING("Failed to init standard classes");
+    return false;
+  }
 
-  MOZ_ASSERT(UnwrapDOMObjectToISupports(global));
+  JS::Handle<JSObject*> proto = GetProto(aCx, aGlobal);
+  if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) {
+    NS_WARNING("Failed to set proto");
+    return false;
+  }
 
-  return global;
+  return true;
 }
 
 /*
  * Holds a jsid that is initialized to an interned string, with conversion to
  * Handle<jsid>.
  */
 class InternedStringId
 {
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2945,67 +2945,79 @@ class CGWrapGlobalMethod(CGAbstractMetho
     properties should be a PropertyArrays instance.
     """
     def __init__(self, descriptor, properties):
         assert descriptor.interface.hasInterfacePrototypeObject()
         args = [Argument('JSContext*', 'aCx'),
                 Argument(descriptor.nativeType + '*', 'aObject'),
                 Argument('nsWrapperCache*', 'aCache'),
                 Argument('JS::CompartmentOptions&', 'aOptions'),
-                Argument('JSPrincipals*', 'aPrincipal')]
+                Argument('JSPrincipals*', 'aPrincipal'),
+                Argument('bool', 'aInitStandardClasses')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
         self.descriptor = descriptor
         self.properties = properties
 
     def definition_body(self):
         if self.properties.hasNonChromeOnly():
             properties = "GlobalPropertiesAreOwn() ? &sNativeProperties : nullptr"
         else:
             properties = "nullptr"
         if self.properties.hasChromeOnly():
             chromeProperties = "GlobalPropertiesAreOwn() && nsContentUtils::ThreadsafeIsCallerChrome() ? &sChromeOnlyNativeProperties : nullptr"
         else:
             chromeProperties = "nullptr"
 
+        if self.descriptor.workers:
+            fireOnNewGlobal = """// XXXkhuey can't do this yet until workers can lazy resolve.
+// JS_FireOnNewGlobalObject(aCx, obj);
+"""
+        else:
+            fireOnNewGlobal = ""
+
         return fill(
             """
             ${assertions}
               MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                          "nsISupports must be on our primary inheritance chain");
 
               JS::Rooted<JSObject*> obj(aCx);
-              obj = CreateGlobal<${nativeType}, GetProtoObject>(aCx,
-                                                     aObject,
-                                                     aCache,
-                                                     Class.ToJSClass(),
-                                                     aOptions,
-                                                     aPrincipal);
+              CreateGlobal<${nativeType}, GetProtoObject>(aCx,
+                                               aObject,
+                                               aCache,
+                                               Class.ToJSClass(),
+                                               aOptions,
+                                               aPrincipal,
+                                               aInitStandardClasses,
+                                               &obj);
+              if (!obj) {
+                return nullptr;
+              }
 
               // obj is a new global, so has a new compartment.  Enter it
               // before doing anything with it.
               JSAutoCompartment ac(aCx, obj);
 
               if (!DefineProperties(aCx, obj, ${properties}, ${chromeProperties})) {
                 return nullptr;
               }
               $*{unforgeable}
 
               $*{slots}
-
-              // XXXkhuey can't do this yet until workers can lazy resolve.
-              // JS_FireOnNewGlobalObject(aCx, obj);
+              $*{fireOnNewGlobal}
 
               return obj;
             """,
             assertions=AssertInheritanceChain(self.descriptor),
             nativeType=self.descriptor.nativeType,
             properties=properties,
             chromeProperties=chromeProperties,
             unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
-            slots=InitMemberSlots(self.descriptor, True))
+            slots=InitMemberSlots(self.descriptor, True),
+            fireOnNewGlobal=fireOnNewGlobal)
 
 
 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'aCx'),
                 Argument('JS::Handle<JSObject*>', 'aWrapper'),
                 Argument(descriptor.nativeType + '*', 'aObject')]
         CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -294,17 +294,18 @@ DedicatedWorkerGlobalScope::WrapGlobalOb
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker());
 
   JS::CompartmentOptions options;
   mWorkerPrivate->CopyJSCompartmentOptions(options);
 
   return DedicatedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this,
                                                          options,
-                                                         GetWorkerPrincipal());
+                                                         GetWorkerPrincipal(),
+                                                         true);
 }
 
 void
 DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
                                         JS::Handle<JS::Value> aMessage,
                                         const Optional<Sequence<JS::Value>>& aTransferable,
                                         ErrorResult& aRv)
 {
@@ -331,17 +332,18 @@ SharedWorkerGlobalScope::WrapGlobalObjec
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mWorkerPrivate->IsSharedWorker());
 
   JS::CompartmentOptions options;
   mWorkerPrivate->CopyJSCompartmentOptions(options);
 
   return SharedWorkerGlobalScopeBinding_workers::Wrap(aCx, this, this, options,
-                                                      GetWorkerPrincipal());
+                                                      GetWorkerPrincipal(),
+                                                      true);
 }
 
 bool
 GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
   return false;
 }
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -73,18 +73,16 @@ XPCWrappedNativeScope::XPCWrappedNativeS
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
         mIsXBLScope(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
-        MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
-                                                         JSCLASS_HAS_PRIVATE)); 
 #ifdef DEBUG
         for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
             MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object");
 #endif
 
         mNext = gScopes;
         gScopes = this;
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -316,46 +316,19 @@ nsXPConnect::InitClasses(JSContext * aJS
     scope->RemoveWrappedNativeProtos();
 
     if (!XPCNativeWrapper::AttachNewConstructorObject(aJSContext, globalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     return NS_OK;
 }
 
-#ifdef DEBUG
-static void
-VerifyTraceXPCGlobalCalled(JSTracer *trc, void **thingp, JSGCTraceKind kind)
-{
-    // We don't do anything here, we only want to verify that TraceXPCGlobal
-    // was called.
-}
-
-struct VerifyTraceXPCGlobalCalledTracer : public JSTracer
-{
-    bool ok;
-
-    VerifyTraceXPCGlobalCalledTracer(JSRuntime *rt)
-      : JSTracer(rt, VerifyTraceXPCGlobalCalled), ok(false)
-    {}
-};
-#endif
-
 void
 TraceXPCGlobal(JSTracer *trc, JSObject *obj)
 {
-#ifdef DEBUG
-    if (trc->callback == VerifyTraceXPCGlobalCalled) {
-        // We don't do anything here, we only want to verify that TraceXPCGlobal
-        // was called.
-        reinterpret_cast<VerifyTraceXPCGlobalCalledTracer*>(trc)->ok = true;
-        return;
-    }
-#endif
-
     if (js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL)
         mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
 }
 
 namespace xpc {
 
 JSObject*
 CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
@@ -376,17 +349,17 @@ CreateGlobalObject(JSContext *cx, const 
     (void) new XPCWrappedNativeScope(cx, global);
 
 #ifdef DEBUG
     // Verify that the right trace hook is called. Note that this doesn't
     // work right for wrapped globals, since the tracing situation there is
     // more complicated. Manual inspection shows that they do the right thing.
     if (!((const js::Class*)clasp)->ext.isWrappedNative)
     {
-        VerifyTraceXPCGlobalCalledTracer trc(JS_GetRuntime(cx));
+        VerifyTraceProtoAndIfaceCacheCalledTracer trc(JS_GetRuntime(cx));
         JS_TraceChildren(&trc, global, JSTRACE_OBJECT);
         MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments.");
     }
 #endif
 
     if (clasp->flags & JSCLASS_DOM_GLOBAL) {
         const char* className = clasp->name;
         AllocateProtoAndIfaceCache(global,