Bug 1117167. Make the generated implementation classes for JS-implemented bindings store an nsIGlobalObject, not an nsPIDOMWindow, so we can use them in sandboxes. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 08 Jan 2015 11:57:19 -0500
changeset 222798 23ce8a6e826fbd6fd284b1db9bb37633e623b9bf
parent 222797 37bd1af93c544eeb6134378e6b574a560b5a9bfe
child 222799 9bb49c25941819c41e7c40633c67a7baef9aa4a2
push id28073
push userkwierso@gmail.com
push dateFri, 09 Jan 2015 01:08:23 +0000
treeherdermozilla-central@b3f84cf78dc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1117167
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1117167. Make the generated implementation classes for JS-implemented bindings store an nsIGlobalObject, not an nsPIDOMWindow, so we can use them in sandboxes. r=peterv
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/Codegen.py
dom/bindings/test/TestCImplementedInterface.h
dom/datastore/DataStoreService.cpp
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2063,18 +2063,18 @@ ReportLenientThisUnwrappingFailure(JSCon
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
   if (window && window->GetDoc()) {
     window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
   }
   return true;
 }
 
 bool
-GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
-                                nsPIDOMWindow** window)
+GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
+                                       nsIGlobalObject** globalObj)
 {
   // Be very careful to not get tricked here.
   MOZ_ASSERT(NS_IsMainThread());
   if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
     NS_RUNTIMEABORT("Should have a chrome object here");
   }
 
   // Look up the content-side object.
@@ -2090,47 +2090,46 @@ GetWindowForJSImplementedObject(JSContex
 
   // Go ahead and get the global from it.  GlobalObject will handle
   // doing unwrapping as needed.
   GlobalObject global(cx, &domImplVal.toObject());
   if (global.Failed()) {
     return false;
   }
 
-  // It's OK if we have null here: that just means the content-side
-  // object really wasn't associated with any window.
-  nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global.GetAsSupports()));
-  win.forget(window);
+  DebugOnly<nsresult> rv = CallQueryInterface(global.GetAsSupports(), globalObj);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  MOZ_ASSERT(*globalObj);
   return true;
 }
 
-already_AddRefed<nsPIDOMWindow>
+already_AddRefed<nsIGlobalObject>
 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
                           const GlobalObject& aGlobal,
                           JS::MutableHandle<JSObject*> aObject,
                           ErrorResult& aRv)
 {
-  // Get the window to use as a parent and for initialization.
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window) {
+  // Get the global object to use as a parent and for initialization.
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  ConstructJSImplementation(aCx, aContractId, window, aObject, aRv);
+  ConstructJSImplementation(aCx, aContractId, global, aObject, aRv);
 
   if (aRv.Failed()) {
     return nullptr;
   }
-  return window.forget();
+  return global.forget();
 }
 
 void
 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
-                          nsPIDOMWindow* aWindow,
+                          nsIGlobalObject* aGlobal,
                           JS::MutableHandle<JSObject*> aObject,
                           ErrorResult& aRv)
 {
   // Make sure to divorce ourselves from the calling JS while creating and
   // initializing the object, so exceptions from that will get reported
   // properly, since those are never exceptions that a spec wants to be thrown.
   {
     AutoNoJSAPI nojsapi;
@@ -2140,22 +2139,24 @@ ConstructJSImplementation(JSContext* aCx
     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
     if (!implISupports) {
       nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"",
                           aContractId);
       NS_WARNING(msg.get());
       aRv.Throw(rv);
       return;
     }
-    // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
+    // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer
+    // and our global is a window.
     nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
       do_QueryInterface(implISupports);
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal);
     if (gpi) {
       JS::Rooted<JS::Value> initReturn(aCx);
-      rv = gpi->Init(aWindow, &initReturn);
+      rv = gpi->Init(window, &initReturn);
       if (NS_FAILED(rv)) {
         aRv.Throw(rv);
         return;
       }
       // With JS-implemented WebIDL, the return value of init() is not used to determine
       // if init() failed, so init() should only return undefined. Any kind of permission
       // or pref checking must happen by adding an attribute to the WebIDL interface.
       if (!initReturn.isUndefined()) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2646,29 +2646,29 @@ GetUnforgeableHolder(JSObject* aGlobal, 
 {
   ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
   JSObject* interfaceProto = protoAndIfaceCache.EntrySlotMustExist(aId);
   return &js::GetReservedSlot(interfaceProto,
                               DOM_INTERFACE_PROTO_SLOTS_BASE).toObject();
 }
 
 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
-// interface, get the nsPIDOMWindow corresponding to the content side, if any.
+// interface, get the nsIGlobalObject corresponding to the content side, if any.
 // A false return means an exception was thrown.
 bool
-GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
-                                nsPIDOMWindow** window);
+GetContentGlobalForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
+                                       nsIGlobalObject** global);
 
 void
 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
-                          nsPIDOMWindow* aWindow,
+                          nsIGlobalObject* aGlobal,
                           JS::MutableHandle<JSObject*> aObject,
                           ErrorResult& aRv);
 
-already_AddRefed<nsPIDOMWindow>
+already_AddRefed<nsIGlobalObject>
 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
                           const GlobalObject& aGlobal,
                           JS::MutableHandle<JSObject*> aObject,
                           ErrorResult& aRv);
 
 /**
  * Convert an nsCString to jsval, returning true on success.
  * These functions are intended for ByteString implementations.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3705,22 +3705,22 @@ class CastableObjectUnwrapper():
             exceptionCode = exceptionCode or codeOnFailure
             self.substitution["codeOnFailure"] = fill(
                 """
                 // Be careful to not wrap random DOM objects here, even if
                 // they're wrapped in opaque security wrappers for some reason.
                 // XXXbz Wish we could check for a JS-implemented object
                 // that already has a content reflection...
                 if (!IsDOMObject(js::UncheckedUnwrap(${source}))) {
-                  nsCOMPtr<nsPIDOMWindow> ourWindow;
-                  if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) {
+                  nsCOMPtr<nsIGlobalObject> contentGlobal;
+                  if (!GetContentGlobalForJSImplementedObject(cx, Callback(), getter_AddRefs(contentGlobal))) {
                     $*{exceptionCode}
                   }
                   JS::Rooted<JSObject*> jsImplSourceObj(cx, ${source});
-                  ${target} = new ${type}(jsImplSourceObj, ourWindow);
+                  ${target} = new ${type}(jsImplSourceObj, contentGlobal);
                 } else {
                   $*{codeOnFailure}
                 }
                 """,
                 exceptionCode=exceptionCode,
                 **self.substitution)
         else:
             self.substitution["codeOnFailure"] = codeOnFailure
@@ -12286,17 +12286,17 @@ class CGBindingRoot(CGThing):
                                             workers=False)
         workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                               workers=True)
         callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                                     isCallback=True)
         jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
                                               isJSImplemented=True)
         bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
-        bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
+        bindingHeaders["nsIGlobalObject.h"] = jsImplemented
         bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
 
         def addHeaderBasedOnTypes(header, typeChecker):
             bindingHeaders[header] = (
                 bindingHeaders.get(header, False) or
                 any(map(typeChecker,
                         getAllTypes(descriptors + callbackDescriptors,
                                     dictionaries,
@@ -13295,17 +13295,16 @@ class CGJSImplMethod(CGJSImplMember):
             # arguments to the WebIDL constructor, so don't pass them to __Init()
             assert args[0].argType == 'const GlobalObject&'
             assert args[1].argType == 'JSContext*'
             constructorArgs = [arg.name for arg in args[2:]]
             constructorArgs.append("js::GetObjectCompartment(scopeObj)")
             initCall = fill(
                 """
                 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
-                nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
                 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
                 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
                 JS::Rooted<JS::Value> wrappedVal(cx);
                 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal)) {
                   //XXX Assertion disabled for now, see bug 991271.
                   MOZ_ASSERT(true || JS_IsExceptionPending(cx));
                   aRv.Throw(NS_ERROR_UNEXPECTED);
                   return nullptr;
@@ -13321,23 +13320,23 @@ class CGJSImplMethod(CGJSImplMember):
             initCall = ""
         return genConstructorBody(self.descriptor, initCall)
 
 
 def genConstructorBody(descriptor, initCall=""):
     return fill(
         """
         JS::Rooted<JSObject*> jsImplObj(cx);
-        nsCOMPtr<nsPIDOMWindow> window =
+        nsCOMPtr<nsIGlobalObject> globalHolder =
           ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv);
         if (aRv.Failed()) {
           return nullptr;
         }
         // Build the C++ implementation.
-        nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);
+        nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, globalHolder);
         $*{initCall}
         return impl.forget();
         """,
         contractId=descriptor.interface.getJSImplementation(),
         implClass=descriptor.name,
         initCall=initCall)
 
 
@@ -13511,17 +13510,17 @@ class CGJSImplClass(CGBindingImplClass):
             parentInterface = parentInterface.parent
         if not parentInterface and descriptor.interface.parent:
             # We only have C++ ancestors, so only pass along the window
             baseConstructors.insert(0,
                                     "%s(aParent)" % parentClass)
 
         constructor = ClassConstructor(
             [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
-             Argument("nsPIDOMWindow*", "aParent")],
+             Argument("nsIGlobalObject*", "aParent")],
             visibility="public",
             baseConstructors=baseConstructors)
 
         self.methodDecls.append(
             ClassMethod("_Create",
                         "bool",
                         JSNativeArguments(),
                         static=True,
@@ -13582,22 +13581,20 @@ class CGJSImplClass(CGBindingImplClass):
             }
 
             // GlobalObject will go through wrappers as needed for us, and
             // is simpler than the right UnwrapArg incantation.
             GlobalObject global(cx, &args[0].toObject());
             if (global.Failed()) {
               return false;
             }
-            nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
-            if (!window) {
-              return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window");
-            }
+            nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
+            MOZ_ASSERT(globalHolder);
             JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
-            nsRefPtr<${implName}> impl = new ${implName}(arg, window);
+            nsRefPtr<${implName}> impl = new ${implName}(arg, globalHolder);
             MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
             return GetOrCreateDOMReflector(cx, impl, args.rval());
             """,
             ifaceName=self.descriptor.interface.identifier.name,
             implName=self.descriptor.name)
 
 
 def isJSImplementedDescriptor(descriptorProvider):
--- a/dom/bindings/test/TestCImplementedInterface.h
+++ b/dom/bindings/test/TestCImplementedInterface.h
@@ -13,26 +13,26 @@ class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class TestCImplementedInterface : public TestJSImplInterface
 {
 public:
   TestCImplementedInterface(JS::Handle<JSObject*> aJSImpl,
-                            nsPIDOMWindow* aParent)
+                            nsIGlobalObject* aParent)
     : TestJSImplInterface(aJSImpl, aParent)
   {}
 };
 
 class TestCImplementedInterface2 : public nsISupports,
                                    public nsWrapperCache
 {
 public:
-  explicit TestCImplementedInterface2(nsPIDOMWindow* aParent)
+  explicit TestCImplementedInterface2(nsIGlobalObject* aParent)
   {}
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestCImplementedInterface2)
 
   // We need a GetParentObject to make binding codegen happy
   nsISupports* GetParentObject();
 };
 
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -1000,19 +1000,22 @@ DataStoreService::GetDataStoresResolve(n
       return;
     }
 
     JS::Rooted<JSObject*> dataStoreJS(cx, xpcwrappedjs->GetJSObject());
     if (NS_WARN_IF(!dataStoreJS)) {
       return;
     }
 
+    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aWindow);
+    MOZ_ASSERT(global);
+
     JSAutoCompartment ac(cx, dataStoreJS);
     nsRefPtr<DataStoreImpl> dataStoreObj = new DataStoreImpl(dataStoreJS,
-                                                             aWindow);
+                                                             global);
 
     nsRefPtr<DataStore> exposedStore = new DataStore(aWindow);
 
     ErrorResult error;
     exposedStore->SetDataStoreImpl(*dataStoreObj, error);
     if (error.Failed()) {
       return;
     }