diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index a0e8acf60c2a..badd59d687a9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1940,6 +1940,39 @@ 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 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) { @@ -4360,10 +4393,10 @@ 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 glob = do_QueryInterface(aOpener); diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 357b3bb80ef6..1581d4369329 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -372,6 +372,8 @@ public: virtual bool IsBlackForCC(bool aTracingNeeded = true); + static JSObject* OuterObject(JSContext* aCx, JS::HandleObject aObj); + // nsIScriptObjectPrincipal virtual nsIPrincipal* GetPrincipal(); diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 96efccc58f6d..b010f8a07538 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -53,7 +53,8 @@ def isTypeCopyConstructible(type): def wantsAddProperty(desc): return (desc.concrete and desc.wrapperCache and - not desc.interface.getExtendedAttribute("Global")) + not (desc.workers and + desc.interface.getExtendedAttribute("Global"))) # We'll want to insert the indent at the beginnings of lines, but we @@ -330,9 +331,46 @@ class CGDOMJSClass(CGThing): callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots classFlags = "JSCLASS_IS_DOMJSCLASS | " + classExtensionAndObjectOps = """\ +JS_NULL_CLASS_EXT, +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 = "JS_GlobalObjectTraceHook" + if not self.descriptor.workers: + classExtensionAndObjectOps = """\ +{ + nsGlobalWindow::OuterObject, /* outerObject */ + nullptr, /* innerObject */ + nullptr, /* iteratorObject */ + false, /* isWrappedNative */ + nullptr /* weakmapKeyDelegateOp */ +}, +{ + nullptr, /* lookupGeneric */ + nullptr, /* lookupProperty */ + nullptr, /* lookupElement */ + nullptr, /* defineGeneric */ + nullptr, /* defineProperty */ + nullptr, /* defineElement */ + nullptr, /* getGeneric */ + nullptr, /* getProperty */ + nullptr, /* getElement */ + nullptr, /* setGeneric */ + nullptr, /* setProperty */ + nullptr, /* setElement */ + nullptr, /* getGenericAttributes */ + nullptr, /* setGenericAttributes */ + nullptr, /* deleteProperty */ + nullptr, /* deleteElement */ + nullptr, /* watch */ + nullptr, /* unwatch */ + nullptr, /* slice */ + nullptr, /* enumerate */ + JS_ObjectToOuterObject /* thisObject */ +} +""" else: classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"): @@ -366,8 +404,7 @@ class CGDOMJSClass(CGThing): nullptr, /* construct */ ${trace}, /* trace */ JS_NULL_CLASS_SPEC, - JS_NULL_CLASS_EXT, - JS_NULL_OBJECT_OPS + $*{classExtensionAndObjectOps} }, $*{descriptor} }; @@ -380,6 +417,7 @@ class CGDOMJSClass(CGThing): finalize=FINALIZE_HOOK_NAME, call=callHook, trace=traceHook, + classExtensionAndObjectOps=classExtensionAndObjectOps, descriptor=DOMClass(self.descriptor))