Bug 1125423 part 1 - Attach WindowProxies to globals instead of using innerObject/outerObject hooks. r=bz,luke

This commit is contained in:
Jan de Mooij 2015-11-06 19:03:51 +01:00
parent ce3739bf35
commit 68d1fa5c75
27 changed files with 225 additions and 177 deletions

View File

@ -988,7 +988,7 @@ bool
nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsWindowProxy(aGlobal));
// Check the bits on the compartment private.
return xpc::Scriptability::Get(aGlobal).Allowed();

View File

@ -1102,6 +1102,7 @@ NewOuterWindowProxy(JSContext *cx, JS::Handle<JSObject*> global, bool isChrome)
isChrome ? &nsChromeOuterWindowProxy::singleton
: &nsOuterWindowProxy::singleton,
options);
MOZ_ASSERT_IF(obj, js::IsWindowProxy(obj));
NS_ASSERTION(js::GetObjectClass(obj)->ext.innerObject, "bad class");
return obj;
@ -2659,6 +2660,11 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
// Enter the new global's compartment.
JSAutoCompartment ac(cx, GetWrapperPreserveColor());
{
JS::Rooted<JSObject*> outer(cx, GetWrapperPreserveColor());
js::SetWindowProxy(cx, newInnerGlobal, outer);
}
// Set scriptability based on the state of the docshell.
bool allow = GetDocShell()->GetCanExecuteScripts();
xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);

View File

@ -3124,7 +3124,7 @@ DeprecationWarning(JSContext* aCx, JSObject* aObject,
bool
ObjectToOuterObjectValue(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp)
{
JSObject* outer = JS_ObjectToOuterObject(cx, obj);
JSObject* outer = js::ToWindowProxyIfWindow(obj);
vp.setObject(*outer);
return true;
}

View File

@ -776,13 +776,9 @@ CouldBeDOMBinding(nsWrapperCache* aCache)
inline bool
TryToOuterize(JSContext* cx, JS::MutableHandle<JS::Value> rval)
{
if (js::IsInnerObject(&rval.toObject())) {
JS::Rooted<JSObject*> obj(cx, &rval.toObject());
obj = JS_ObjectToOuterObject(cx, obj);
if (!obj) {
return false;
}
if (js::IsWindow(&rval.toObject())) {
JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
MOZ_ASSERT(obj);
rval.set(JS::ObjectValue(*obj));
}

View File

@ -1350,10 +1350,8 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
return false;
}
obj = JS_ObjectToInnerObject(cx, obj);
MOZ_ASSERT(obj,
"JS_ObjectToInnerObject should never return null with non-null "
"input.");
obj = js::ToWindowIfWindowProxy(obj);
MOZ_ASSERT(obj, "ToWindowIfWindowProxy should never return null");
if (result) {
// Initialize the out param to void

View File

@ -537,9 +537,8 @@ JavaScriptShared::findObjectById(JSContext* cx, const ObjectId& objId)
if (objId.hasXrayWaiver()) {
{
JSAutoCompartment ac2(cx, obj);
obj = JS_ObjectToOuterObject(cx, obj);
if (!obj)
return nullptr;
obj = js::ToWindowProxyIfWindow(obj);
MOZ_ASSERT(obj);
}
if (!xpc::WrapperFactory::WaiveXrayAndWrap(cx, &obj))
return nullptr;

View File

@ -737,7 +737,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 36)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 37)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \

View File

@ -33,7 +33,7 @@ AssertInnerizedScopeChain(JSContext* cx, JSObject& scopeobj)
#ifdef DEBUG
RootedObject obj(cx);
for (obj = &scopeobj; obj; obj = obj->enclosingScope())
MOZ_ASSERT(GetInnerObject(obj) == obj);
MOZ_ASSERT(!IsWindowProxy(obj));
#endif
}

View File

@ -5551,13 +5551,15 @@ TryAttachNativeGetAccessorPropStub(JSContext* cx, HandleScript script, jsbytecod
const Class* outerClass = nullptr;
if (!isDOMProxy && !obj->isNative()) {
outerClass = obj->getClass();
DebugOnly<JSObject*> outer = obj.get();
obj = GetInnerObject(obj);
MOZ_ASSERT(script->global().isNative());
if (obj != &script->global())
if (!IsWindowProxy(obj))
return true;
// ICGetProp_CallNative*::Compiler::generateStubCode depends on this.
MOZ_ASSERT(&((GetProxyDataLayout(outer)->values->privateSlot).toObject()) == obj);
// This must be a WindowProxy for the current Window/global. Else it'd
// be a cross-compartment wrapper and IsWindowProxy returns false for
// those.
MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx->global());
obj = cx->global();
if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy,
&domProxyShadowsResult, &domProxyHasGeneration))
{
@ -6471,13 +6473,9 @@ ICGetPropCallNativeCompiler::generateStubCode(MacroAssembler& masm)
masm.branchTestObject(Assembler::NotEqual, R0, &failure);
objReg = masm.extractObject(R0, ExtractTemp0);
if (outerClass_) {
ValueOperand val = regs.takeAnyValue();
Register tmp = regs.takeAny();
masm.branchTestObjClass(Assembler::NotEqual, objReg, tmp, outerClass_, &failure);
masm.loadPtr(Address(objReg, ProxyDataOffset + offsetof(ProxyDataLayout, values)), tmp);
masm.loadValue(Address(tmp, offsetof(ProxyValueArray, privateSlot)), val);
masm.movePtr(masm.extractObject(val, ExtractTemp0), objReg);
regs.add(val);
masm.movePtr(ImmGCPtr(cx->global()), objReg);
regs.add(tmp);
}
}

View File

@ -11847,11 +11847,14 @@ IonBuilder::tryInnerizeWindow(MDefinition* obj)
if (!singleton)
return obj;
JSObject* inner = GetInnerObject(singleton);
if (inner == singleton || inner != &script()->global())
if (!IsWindowProxy(singleton))
return obj;
// When we navigate, the outer object is brain transplanted and we'll mark
// This must be a WindowProxy for the current Window/global. Else it'd be
// a cross-compartment wrapper and IsWindowProxy returns false for those.
MOZ_ASSERT(ToWindowIfWindowProxy(singleton) == &script()->global());
// When we navigate, the WindowProxy is brain transplanted and we'll mark
// its ObjectGroup as having unknown properties. The type constraint we add
// here will invalidate JIT code when this happens.
TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(singleton);

View File

@ -56,6 +56,8 @@ static const JSWrapObjectCallbacks WrapObjectCallbacks = {
BEGIN_TEST(testBug604087)
{
js::SetWindowProxyClass(cx->runtime(), &OuterWrapperClass);
js::WrapperOptions options;
options.setClass(&OuterWrapperClass);
options.setSingleton(true);

View File

@ -403,7 +403,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existin
const JSWrapObjectCallbacks* cb = cx->runtime()->wrapObjectCallbacks;
if (obj->compartment() == this) {
obj.set(GetOuterObject(cx, obj));
obj.set(ToWindowProxyIfWindow(obj));
return true;
}
@ -419,7 +419,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existin
obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true));
if (obj->compartment() == this) {
MOZ_ASSERT(obj == GetOuterObject(cx, obj));
MOZ_ASSERT(!IsWindow(obj));
return true;
}
@ -442,7 +442,7 @@ JSCompartment::wrap(JSContext* cx, MutableHandleObject obj, HandleObject existin
if (!obj)
return false;
}
MOZ_ASSERT(obj == GetOuterObject(cx, obj));
MOZ_ASSERT(!IsWindow(obj));
if (obj->compartment() == this)
return true;

View File

@ -82,10 +82,10 @@ JS_FindCompilationScope(JSContext* cx, HandleObject objArg)
obj = UncheckedUnwrap(obj);
/*
* Innerize the target_obj so that we compile in the correct (inner)
* scope.
* Get the Window if `obj` is a WindowProxy so that we compile in the
* correct (global) scope.
*/
return GetInnerObject(obj);
return ToWindowIfWindowProxy(obj);
}
JS_FRIEND_API(JSFunction*)
@ -1282,3 +1282,53 @@ js::GetPropertyNameFromPC(JSScript* script, jsbytecode* pc)
return nullptr;
return script->getName(pc);
}
JS_FRIEND_API(void)
js::SetWindowProxyClass(JSRuntime* rt, const js::Class* clasp)
{
MOZ_ASSERT(!rt->maybeWindowProxyClass());
rt->setWindowProxyClass(clasp);
}
JS_FRIEND_API(void)
js::SetWindowProxy(JSContext* cx, HandleObject global, HandleObject windowProxy)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, global, windowProxy);
MOZ_ASSERT(IsWindowProxy(windowProxy));
global->as<GlobalObject>().setWindowProxy(windowProxy);
}
JS_FRIEND_API(JSObject*)
js::ToWindowIfWindowProxy(JSObject* obj)
{
if (IsWindowProxy(obj))
return &obj->global();
return obj;
}
JS_FRIEND_API(JSObject*)
js::ToWindowProxyIfWindow(JSObject* obj)
{
if (IsWindow(obj))
return obj->as<GlobalObject>().windowProxy();
return obj;
}
JS_FRIEND_API(bool)
js::IsWindowProxy(JSObject* obj)
{
// Note: simply checking `obj == obj->global().windowProxy()` is not
// sufficient: we may have transplanted the window proxy with a CCW.
// Check the Class to ensure we really have a window proxy.
return obj->getClass() == obj->runtimeFromAnyThread()->maybeWindowProxyClass();
}
JS_FRIEND_API(bool)
js::detail::IsWindowSlow(JSObject* obj)
{
return obj->as<GlobalObject>().maybeWindowProxy();
}

View File

@ -148,14 +148,6 @@ JS_GetScriptPrincipals(JSScript* script);
extern JS_FRIEND_API(bool)
JS_ScriptHasMutedErrors(JSScript* script);
/* Safe to call with input obj == nullptr. Returns non-nullptr iff obj != nullptr. */
extern JS_FRIEND_API(JSObject*)
JS_ObjectToInnerObject(JSContext* cx, JS::HandleObject obj);
/* Requires obj != nullptr. */
extern JS_FRIEND_API(JSObject*)
JS_ObjectToOuterObject(JSContext* cx, JS::HandleObject obj);
extern JS_FRIEND_API(JSObject*)
JS_CloneObject(JSContext* cx, JS::HandleObject obj, JS::HandleObject proto);
@ -676,16 +668,6 @@ ParentKeyForStandardClass(JSProtoKey key)
return JSProto_Object;
}
inline bool
IsInnerObject(JSObject* obj) {
return !!GetObjectClass(obj)->ext.outerObject;
}
inline bool
IsOuterObject(JSObject* obj) {
return !!GetObjectClass(obj)->ext.innerObject;
}
JS_FRIEND_API(bool)
IsFunctionObject(JSObject* obj);
@ -2859,6 +2841,77 @@ ReportIsNotFunction(JSContext* cx, JS::HandleValue v);
extern JS_FRIEND_API(JSObject*)
ConvertArgsToArray(JSContext* cx, const JS::CallArgs& args);
/**
* Window and WindowProxy
*
* The functions below have to do with Windows and WindowProxies. There's an
* invariant that actual Window objects (the global objects of web pages) are
* never directly exposed to script. Instead we often substitute a WindowProxy.
*
* The scope chain, on the other hand, contains the Window and never its
* WindowProxy.
*
* As a result, we have calls to these "substitute-this-object-for-that-object"
* functions sprinkled at apparently arbitrary (but actually *very* carefully
* and nervously selected) places throughout the engine and indeed the
* universe.
*/
/**
* Tell the JS engine which Class is used for WindowProxy objects. Used by the
* functions below.
*/
extern JS_FRIEND_API(void)
SetWindowProxyClass(JSRuntime* rt, const Class* clasp);
/**
* Associates a WindowProxy with a Window (global object). `windowProxy` must
* have the Class set by SetWindowProxyClass.
*/
extern JS_FRIEND_API(void)
SetWindowProxy(JSContext* cx, JS::HandleObject global, JS::HandleObject windowProxy);
namespace detail {
JS_FRIEND_API(bool)
IsWindowSlow(JSObject* obj);
} // namespace detail
/**
* Returns true iff `obj` is a global object with an associated WindowProxy,
* see SetWindowProxy.
*/
inline bool
IsWindow(JSObject* obj)
{
if (GetObjectClass(obj)->flags & JSCLASS_IS_GLOBAL)
return detail::IsWindowSlow(obj);
return false;
}
/**
* Returns true iff `obj` has the WindowProxy Class (see SetWindowProxyClass).
*/
JS_FRIEND_API(bool)
IsWindowProxy(JSObject* obj);
/**
* If `obj` is a Window, get its associated WindowProxy (or a CCW if the
* page was navigated away from), else return `obj`. This function is
* infallible and never returns nullptr.
*/
extern JS_FRIEND_API(JSObject*)
ToWindowProxyIfWindow(JSObject* obj);
/**
* If `obj` is a WindowProxy, get its associated Window (the compartment's
* global), else return `obj`. This function is infallible and never returns
* nullptr.
*/
extern JS_FRIEND_API(JSObject*)
ToWindowIfWindowProxy(JSObject* obj);
} /* namespace js */
extern JS_FRIEND_API(void)

View File

@ -76,23 +76,6 @@ using mozilla::DebugOnly;
using mozilla::Maybe;
using mozilla::UniquePtr;
JS_FRIEND_API(JSObject*)
JS_ObjectToInnerObject(JSContext* cx, HandleObject obj)
{
if (!obj) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INACTIVE);
return nullptr;
}
return GetInnerObject(obj);
}
JS_FRIEND_API(JSObject*)
JS_ObjectToOuterObject(JSContext* cx, HandleObject obj)
{
assertSameCompartment(cx, obj);
return GetOuterObject(cx, obj);
}
void
js::ReportNotObject(JSContext* cx, const Value& v)
{
@ -2484,14 +2467,14 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto, JS::Object
/*
* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
* have to do this comparison on the observable outer objects, not on the
* possibly-inner object we're setting the proto on.
* have to do this comparison on the observable WindowProxy, not on the
* possibly-Window object we're setting the proto on.
*/
RootedObject outerObj(cx, GetOuterObject(cx, obj));
RootedObject objMaybeWindowProxy(cx, ToWindowProxyIfWindow(obj));
RootedObject obj2(cx);
for (obj2 = proto; obj2; ) {
MOZ_ASSERT(GetOuterObject(cx, obj2) == obj2);
if (obj2 == outerObj)
MOZ_ASSERT(!IsWindow(obj2));
if (obj2 == objMaybeWindowProxy)
return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
if (!GetPrototype(cx, obj2, &obj2))
@ -2767,7 +2750,7 @@ js::GetPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
bool
js::WatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
{
RootedObject obj(cx, GetInnerObject(origObj));
RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
if (obj->isNative()) {
// Use sparse indexes for watched objects, as dense elements can be
// written to without checking the watchpoint map.
@ -2796,7 +2779,7 @@ js::UnwatchGuts(JSContext* cx, JS::HandleObject origObj, JS::HandleId id)
{
// Looking in the map for an unsupported object will never hit, so we don't
// need to check for nativeness or watchable-ness here.
RootedObject obj(cx, GetInnerObject(origObj));
RootedObject obj(cx, ToWindowIfWindowProxy(origObj));
if (WatchpointMap* wpmap = cx->compartment()->watchpointMap)
wpmap->unwatch(obj, id, nullptr, nullptr);
return true;

View File

@ -1035,59 +1035,6 @@ ToPrimitive(JSContext* cx, JSType preferredType, MutableHandleValue vp)
extern const char*
GetObjectClassName(JSContext* cx, HandleObject obj);
/*
* Inner and outer objects
*
* GetInnerObject and GetOuterObject (and also GetThisValue, somewhat) have to
* do with Windows and WindowProxies. There's a screwy invariant that actual
* Window objects (the global objects of web pages) are never directly exposed
* to script. Instead we often substitute a WindowProxy.
*
* As a result, we have calls to these three "substitute-this-object-for-that-
* object" functions sprinkled at apparently arbitrary (but actually *very*
* carefully and nervously selected) places throughout the engine and indeed
* the universe.
*/
/*
* If obj is a WindowProxy, return its current inner Window. Otherwise return
* obj. This function can't fail and never returns nullptr.
*
* GetInnerObject is called when we need a scope chain; you never want a
* WindowProxy on a scope chain.
*
* It's also called in a few places where an object comes in from script, and
* the user probably intends to operate on the Window, not the
* WindowProxy. Object.prototype.watch and various Debugger features do
* this. (Users can't simply pass the Window, because the Window isn't exposed
* to scripts.)
*/
inline JSObject*
GetInnerObject(JSObject* obj)
{
if (InnerObjectOp op = obj->getClass()->ext.innerObject) {
JS::AutoSuppressGCAnalysis nogc;
return op(obj);
}
return obj;
}
/*
* If obj is a Window object, return the WindowProxy. Otherwise return obj.
* This function can't fail; it never sets an exception or returns nullptr.
*
* This must be called before passing an object to script, if the object might
* be a Window. (But usually those cases involve scope objects, and for those,
* it is better to call GetThisValue instead.)
*/
inline JSObject*
GetOuterObject(JSContext* cx, HandleObject obj)
{
if (ObjectOp op = obj->getClass()->ext.outerObject)
return op(cx, obj);
return obj;
}
/*
* Return an object that may be used as `this` in place of obj. For most
* objects this just returns obj.

View File

@ -261,12 +261,10 @@ Proxy::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
}
static Value
OuterizeValue(JSContext* cx, HandleValue v)
ValueToWindowProxyIfWindow(Value v)
{
if (v.isObject()) {
RootedObject obj(cx, &v.toObject());
return ObjectValue(*GetOuterObject(cx, obj));
}
if (v.isObject())
return ObjectValue(*ToWindowProxyIfWindow(&v.toObject()));
return v;
}
@ -281,9 +279,9 @@ Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id
if (!policy.allowed())
return policy.returnValue();
// Outerize the receiver. Proxy handlers shouldn't have to know about
// the Window/WindowProxy distinction.
RootedValue receiver(cx, OuterizeValue(cx, receiver_));
// Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
// shouldn't have to know about the Window/WindowProxy distinction.
RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
if (handler->hasPrototype()) {
bool own;
@ -315,9 +313,9 @@ Proxy::set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v, Handle
return result.succeed();
}
// Outerize the receiver. Proxy handlers shouldn't have to know about
// the Window/WindowProxy distinction.
RootedValue receiver(cx, OuterizeValue(cx, receiver_));
// Use the WindowProxy as receiver if receiver_ is a Window. Proxy handlers
// shouldn't have to know about the Window/WindowProxy distinction.
RootedValue receiver(cx, ValueToWindowProxyIfWindow(receiver_));
// Special case. See the comment on BaseProxyHandler::mHasPrototype.
if (handler->hasPrototype())

View File

@ -2553,9 +2553,8 @@ EvalInContext(JSContext* cx, unsigned argc, Value* vp)
ac.emplace(cx, sobj);
}
sobj = GetInnerObject(sobj);
if (!sobj)
return false;
sobj = ToWindowIfWindowProxy(sobj);
if (!(sobj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
JS_ReportError(cx, "Invalid scope argument to evalcx");
return false;

View File

@ -3064,10 +3064,8 @@ Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v)
return nullptr;
}
/* If that produced an outer window, innerize it. */
obj = GetInnerObject(obj);
if (!obj)
return nullptr;
/* If that produced a WindowProxy, get the Window (global). */
obj = ToWindowIfWindowProxy(obj);
/* If that didn't produce a global object, it's an error. */
if (!obj->is<GlobalObject>()) {
@ -7696,8 +7694,8 @@ RequireGlobalObject(JSContext* cx, HandleValue dbgobj, HandleObject referent)
}
/* ... and WindowProxies around Windows. */
if (IsOuterObject(obj)) {
obj = JS_ObjectToInnerObject(cx, obj);
if (IsWindowProxy(obj)) {
obj = ToWindowIfWindowProxy(obj);
isWindowProxy = "a WindowProxy referring to ";
}
@ -7799,8 +7797,8 @@ DebuggerObject_unsafeDereference(JSContext* cx, unsigned argc, Value* vp)
if (!cx->compartment()->wrap(cx, args.rval()))
return false;
// Wrapping should outerize inner objects.
MOZ_ASSERT(!IsInnerObject(&args.rval().toObject()));
// Wrapping should return the WindowProxy.
MOZ_ASSERT(!IsWindow(&args.rval().toObject()));
return true;
}

View File

@ -117,6 +117,7 @@ class GlobalObject : public NativeObject
INT32X4_TYPE_DESCR,
FOR_OF_PIC_CHAIN,
MODULE_RESOLVE_HOOK,
WINDOW_PROXY,
/* Total reserved-slot count for global objects. */
RESERVED_SLOTS
@ -747,6 +748,18 @@ class GlobalObject : public NativeObject
}
static NativeObject* getOrCreateForOfPICObject(JSContext* cx, Handle<GlobalObject*> global);
JSObject* windowProxy() const {
return &getReservedSlot(WINDOW_PROXY).toObject();
}
JSObject* maybeWindowProxy() const {
Value v = getReservedSlot(WINDOW_PROXY);
MOZ_ASSERT(v.isObject() || v.isUndefined());
return v.isObject() ? &v.toObject() : nullptr;
}
void setWindowProxy(JSObject* windowProxy) {
setReservedSlot(WINDOW_PROXY, ObjectValue(*windowProxy));
}
void setModuleResolveHook(HandleFunction hook) {
MOZ_ASSERT(hook);
setSlot(MODULE_RESOLVE_HOOK, ObjectValue(*hook));

View File

@ -570,11 +570,7 @@ js::ExecuteKernel(JSContext* cx, HandleScript script, JSObject& scopeChainArg, c
MOZ_ASSERT_IF(type == EXECUTE_GLOBAL, IsGlobalLexicalScope(&scopeChainArg) ||
!IsSyntacticScope(&scopeChainArg));
#ifdef DEBUG
if (thisv.isObject()) {
RootedObject thisObj(cx, &thisv.toObject());
AutoSuppressGC nogc(cx);
MOZ_ASSERT(GetOuterObject(cx, thisObj) == thisObj);
}
MOZ_ASSERT_IF(thisv.isObject(), !IsWindow(&thisv.toObject()));
RootedObject terminatingScope(cx, &scopeChainArg);
while (IsSyntacticScope(terminatingScope))
terminatingScope = terminatingScope->enclosingScope();
@ -613,7 +609,7 @@ js::Execute(JSContext* cx, HandleScript script, JSObject& scopeChainArg, Value*
/* The scope chain is something we control, so we know it can't
have any outer objects on it. */
RootedObject scopeChain(cx, &scopeChainArg);
MOZ_ASSERT(scopeChain == GetInnerObject(scopeChain));
MOZ_ASSERT(!IsWindowProxy(scopeChain));
if (script->module()) {
MOZ_RELEASE_ASSERT(scopeChain == script->module()->environment(),

View File

@ -226,6 +226,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
ionPcScriptCache(nullptr),
scriptEnvironmentPreparer(nullptr),
ctypesActivityCallback(nullptr),
windowProxyClass_(nullptr),
offthreadIonCompilationEnabled_(true),
parallelParsingEnabled_(true),
autoWritableJitCodeActive_(false),

View File

@ -1399,6 +1399,7 @@ struct JSRuntime : public JS::shadow::Runtime,
private:
JS::RuntimeOptions options_;
const js::Class* windowProxyClass_;
// Settings for how helper threads can be used.
bool offthreadIonCompilationEnabled_;
@ -1436,6 +1437,13 @@ struct JSRuntime : public JS::shadow::Runtime,
return options_;
}
const js::Class* maybeWindowProxyClass() const {
return windowProxyClass_;
}
void setWindowProxyClass(const js::Class* clasp) {
windowProxyClass_ = clasp;
}
#ifdef DEBUG
public:
js::AutoEnterPolicy* enteredPolicy;

View File

@ -3461,6 +3461,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
js::SetScriptEnvironmentPreparer(runtime, &mEnvironmentPreparer);
js::SetActivityCallback(runtime, ActivityCallback, this);
JS_SetInterruptCallback(runtime, InterruptCallback);
js::SetWindowProxyClass(runtime, &OuterWindowProxyClass);
// The JS engine needs to keep the source code around in order to implement
// Function.prototype.toSource(). It'd be nice to not have to do this for

View File

@ -42,8 +42,7 @@ XPCVariant::XPCVariant(JSContext* cx, Value aJSVal)
// thing, but I'm saving the cleanup here for another day. Blake thinks
// that we should just not store the WN if we're creating a variant for
// an outer window.
JS::RootedObject obj(cx, &mJSVal.toObject());
obj = JS_ObjectToInnerObject(cx, obj);
JSObject* obj = js::ToWindowIfWindowProxy(&mJSVal.toObject());
mJSVal = JS::ObjectValue(*obj);
JSObject* unwrapped = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);

View File

@ -98,7 +98,7 @@ WrapperFactory::WaiveXray(JSContext* cx, JSObject* objArg)
{
RootedObject obj(cx, objArg);
obj = UncheckedUnwrap(obj);
MOZ_ASSERT(!js::IsInnerObject(obj));
MOZ_ASSERT(!js::IsWindow(obj));
JSObject* waiver = GetXrayWaiver(obj);
if (waiver)
@ -155,20 +155,20 @@ WrapperFactory::PrepareForWrapping(JSContext* cx, HandleObject scope,
RootedObject obj(cx, objArg);
// Outerize any raw inner objects at the entry point here, so that we don't
// have to worry about them for the rest of the wrapping code.
if (js::IsInnerObject(obj)) {
if (js::IsWindow(obj)) {
JSAutoCompartment ac(cx, obj);
obj = JS_ObjectToOuterObject(cx, obj);
NS_ENSURE_TRUE(obj, nullptr);
// The outerization hook wraps, which means that we can end up with a
// CCW here if |obj| was a navigated-away-from inner. Strip any CCWs.
obj = js::ToWindowProxyIfWindow(obj);
MOZ_ASSERT(obj);
// ToWindowProxyIfWindow can return a CCW if |obj| was a
// navigated-away-from Window. Strip any CCWs.
obj = js::UncheckedUnwrap(obj);
MOZ_ASSERT(js::IsOuterObject(obj));
MOZ_ASSERT(js::IsWindowProxy(obj));
}
// If we've got an outer window, there's nothing special that needs to be
// If we've got a WindowProxy, there's nothing special that needs to be
// done here, and we can move on to the next phase of wrapping. We handle
// this case first to allow us to assert against wrappers below.
if (js::IsOuterObject(obj))
if (js::IsWindowProxy(obj))
return waive ? WaiveXray(cx, obj) : obj;
// Here are the rules for wrapping:
@ -400,7 +400,7 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj)
js::GetObjectClass(obj)->ext.innerObject,
"wrapped object passed to rewrap");
MOZ_ASSERT(!XrayUtils::IsXPCWNHolderClass(JS_GetClass(obj)), "trying to wrap a holder");
MOZ_ASSERT(!js::IsInnerObject(obj));
MOZ_ASSERT(!js::IsWindow(obj));
// We sometimes end up here after nsContentUtils has been shut down but before
// XPConnect has been shut down, so check the context stack the roundabout way.
MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == cx);
@ -542,7 +542,7 @@ WrapperFactory::WaiveXrayAndWrap(JSContext* cx, MutableHandleObject argObj)
{
MOZ_ASSERT(argObj);
RootedObject obj(cx, js::UncheckedUnwrap(argObj));
MOZ_ASSERT(!js::IsInnerObject(obj));
MOZ_ASSERT(!js::IsWindow(obj));
if (js::IsObjectInContextCompartment(obj, cx)) {
argObj.set(obj);
return true;

View File

@ -970,9 +970,9 @@ CycleCollectedJSRuntime::UsefulToMergeZones() const
if (!obj) {
continue;
}
MOZ_ASSERT(js::IsOuterObject(obj));
// Grab the inner from the outer.
obj = JS_ObjectToInnerObject(cx, obj);
MOZ_ASSERT(js::IsWindowProxy(obj));
// Grab the global from the WindowProxy.
obj = js::ToWindowIfWindowProxy(obj);
MOZ_ASSERT(JS_IsGlobalObject(obj));
if (JS::ObjectIsMarkedGray(obj) &&
!js::IsSystemCompartment(js::GetObjectCompartment(obj))) {