Support outerizing and addProperty hooks on globals with WebIDL bindings.
authorPeter Van der Beken <peterv@propagandism.org>
Sat, 15 Feb 2014 22:12:33 +0100
changeset 171898 b08a45c07c6b13a95f271b1acb63802aa2347d2c
parent 171897 dbc420908043b8d0cc6a12f273c9d6f00109296a
child 171899 edd49bf7aee7e0ac5b313f017a6ec3344850a7ac
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone30.0a1
Support outerizing and addProperty hooks on globals with WebIDL bindings.
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/bindings/Codegen.py
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1923,16 +1923,49 @@ nsGlobalWindow::GetGlobalJSObject()
 }
 
 void
 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
 {
   TraceWrapper(aTrc, "active window global");
 }
 
+/* static */
+JSObject*
+nsGlobalWindow::OuterObject(JSContext* aCx, JS::HandleObject aObj)
+{
+  nsGlobalWindow *origWin;
+  UNWRAP_OBJECT(Window, aObj, origWin);
+  nsGlobalWindow *win = origWin->GetOuterWindowInternal();
+
+  if (!win) {
+    // If we no longer have an outer window. No code should ever be
+    // running on a window w/o an outer, which means this hook should
+    // never be called when we have no outer. But just in case, return
+    // null to prevent leaking an inner window to code in a different
+    // window.
+    NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject());
+  MOZ_ASSERT(winObj);
+
+  // Note that while |wrapper| is same-compartment with cx, the outer window
+  // might not be. If we're running script in an inactive scope and evalute
+  // |this|, the outer window is actually a cross-compartment wrapper. So we
+  // need to wrap here.
+  if (!JS_WrapObject(aCx, &winObj)) {
+    NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
+    return nullptr;
+  }
+
+  return winObj;
+}
+
 bool
 nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
 {
   // We reuse the inner window when:
   // a. We are currently at our original document.
   // b. At least one of the following conditions are true:
   // -- The new document is the same as the old document. This means that we're
   //    getting called from document.open().
@@ -4287,20 +4320,20 @@ nsGlobalWindow::GetOpener(nsIDOMWindow**
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
 {
   // Check if we were called from a privileged chrome script.  If not, and if
   // aOpener is not null, just define aOpener on our inner window's JS object,
-  // wapped into the current compartment so that for Xrays we define on the Xray
-  // expando object, but don't set it on the outer window, so that it'll get
-  // reset on navigation.  This is just like replaceable properties, but we're
-  // not quite readonly.
+  // wrapped into the current compartment so that for Xrays we define on the
+  // Xray expando object, but don't set it on the outer window, so that it'll
+  // get reset on navigation.  This is just like replaceable properties, but
+  // we're not quite readonly.
   if (aOpener && !nsContentUtils::IsCallerChrome()) {
     // JS_WrapObject will outerize, so we don't care if aOpener is an inner.
     nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOpener);
     if (!glob) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -365,16 +365,18 @@ public:
   virtual nsresult EnsureScriptEnvironment();
 
   virtual nsIScriptContext *GetScriptContext();
 
   void PoisonOuterWindowProxy(JSObject *aObject);
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true);
 
+  static JSObject* OuterObject(JSContext* aCx, JS::HandleObject aObj);
+
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
 
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
 #ifdef MOZ_B2G
   // nsIDOMWindowB2G
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -41,17 +41,18 @@ def isTypeCopyConstructible(type):
     return (type.isPrimitive() or type.isString() or type.isEnum() or
             (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type)) or
             (type.isDictionary() and
              CGDictionary.isDictionaryCopyConstructible(type.inner)))
 
 def wantsAddProperty(desc):
     return desc.concrete and \
            desc.wrapperCache and \
-           not desc.interface.getExtendedAttribute("Global")
+           (not desc.workers or
+            not desc.interface.getExtendedAttribute("Global"))
 
 class CGThing():
     """
     Abstract base class for things that spit out code.
     """
     def __init__(self):
         pass # Nothing for now
     def declare(self):
@@ -211,19 +212,27 @@ class CGDOMJSClass(CGThing):
         self.descriptor = descriptor
     def declare(self):
         return ""
     def define(self):
         traceHook = 'nullptr'
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
         classFlags = "JSCLASS_IS_DOMJSCLASS | "
+        classExtension = "JS_NULL_CLASS_EXT"
+        objectOps = "JS_NULL_OBJECT_OPS"
         if self.descriptor.interface.getExtendedAttribute("Global"):
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
             traceHook = "mozilla::dom::TraceGlobal"
+            if not self.descriptor.workers:
+                classExtension = "{ nsGlobalWindow::OuterObject, nullptr, nullptr, false, nullptr }"
+                objectOps = ("{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "
+                             "nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "
+                             "nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "
+                             "JS_ObjectToOuterObject }")
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
             newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.interface.getExtendedAttribute("Global"):
             newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
@@ -244,25 +253,26 @@ static const DOMJSClass Class = {
     %s, /* resolve */
     JS_ConvertStub,
     %s, /* finalize */
     %s, /* call */
     nullptr,               /* hasInstance */
     nullptr,               /* construct */
     %s, /* trace */
     JS_NULL_CLASS_SPEC,
-    JS_NULL_CLASS_EXT,
-    JS_NULL_OBJECT_OPS
+    %s,
+    %s
   },
 %s
 };
 """ % (self.descriptor.interface.identifier.name,
        classFlags,
        ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
        enumerateHook, newResolveHook, FINALIZE_HOOK_NAME, callHook, traceHook,
+       classExtension, objectOps,
        CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
 
 class CGDOMProxyJSClass(CGThing):
     """
     Generate a DOMJSClass for a given proxy descriptor
     """
     def __init__(self, descriptor):
         CGThing.__init__(self)