bool WindowNamedPropertiesHandler::getOwnPropDescriptor(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId, bool /* unused */, JS::MutableHandle<JS::PropertyDescriptor> aDesc) const { if (!JSID_IS_STRING(aId)) { // Nothing to do if we're resolving a non-string property. return true; } bool hasOnPrototype; if (!HasPropertyOnPrototype(aCx, aProxy, aId, &hasOnPrototype)) { return false; } if (hasOnPrototype) { return true; } nsAutoJSString str; if (!str.init(aCx, JSID_TO_STRING(aId))) { return false; } if(str.IsEmpty()) { return true; } // Grab the DOM window. JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, aProxy)); nsGlobalWindow* win = xpc::WindowOrNull(global); if (win->Length() > 0) { nsCOMPtr<nsPIDOMWindowOuter> childWin = win->GetChildWindow(str); if (childWin && ShouldExposeChildWindow(str, childWin)) { // We found a subframe of the right name. Shadowing via |var foo| in // global scope is still allowed, since |var| only looks up |own| // properties. But unqualified shadowing will fail, per-spec. JS::Rooted<JS::Value> v(aCx); if (!WrapObject(aCx, childWin, &v)) { return false; } FillPropertyDescriptor(aDesc, aProxy, 0, v); return true; } } // The rest of this function is for HTML documents only. nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc()); if (!htmlDoc) { return true; } nsHTMLDocument* document = static_cast<nsHTMLDocument*>(htmlDoc.get()); Element* element = document->GetElementById(str); if (element) { JS::Rooted<JS::Value> v(aCx); if (!WrapObject(aCx, element, &v)) { return false; } FillPropertyDescriptor(aDesc, aProxy, 0, v); return true; } nsWrapperCache* cache; nsISupports* result = document->ResolveName(str, &cache); if (!result) { return true; } JS::Rooted<JS::Value> v(aCx); if (!WrapObject(aCx, result, cache, nullptr, &v)) { return false; } FillPropertyDescriptor(aDesc, aProxy, 0, v); return true; }
/* static */ bool WebIDLGlobalNameHash::DefineIfEnabled( JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, JS::MutableHandle<JS::PropertyDescriptor> aDesc, bool* aFound) { MOZ_ASSERT(JSID_IS_STRING(aId), "Check for string id before calling this!"); const WebIDLNameTableEntry* entry; { entry = GetEntry(JSID_TO_FLAT_STRING(aId)); } if (!entry) { *aFound = false; return true; } *aFound = true; ConstructorEnabled checkEnabledForScope = entry->mEnabled; // We do the enabled check on the current Realm of aCx, but for the // actual object we pass in the underlying object in the Xray case. That // way the callee can decide whether to allow access based on the caller // or the window being touched. // // Using aCx to represent the current Realm for CheckedUnwrapDynamic // purposes is OK here, because that's the Realm where we plan to do // our property-defining. JS::Rooted<JSObject*> global( aCx, js::CheckedUnwrapDynamic(aObj, aCx, /* stopAtWindowProxy = */ false)); if (!global) { return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR); } { // It's safe to pass "&global" here, because we've already unwrapped it, but // for general sanity better to not have debug code even having the // appearance of mutating things that opt code uses. #ifdef DEBUG JS::Rooted<JSObject*> temp(aCx, global); DebugOnly<nsGlobalWindowInner*> win; MOZ_ASSERT(NS_SUCCEEDED( UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &temp, win, aCx))); #endif } if (checkEnabledForScope && !checkEnabledForScope(aCx, global)) { return true; } // The DOM constructor resolve machinery interacts with Xrays in tricky // ways, and there are some asymmetries that are important to understand. // // In the regular (non-Xray) case, we only want to resolve constructors // once (so that if they're deleted, they don't reappear). We do this by // stashing the constructor in a slot on the global, such that we can see // during resolve whether we've created it already. This is rather // memory-intensive, so we don't try to maintain these semantics when // manipulating a global over Xray (so the properties just re-resolve if // they've been deleted). // // Unfortunately, there's a bit of an impedance-mismatch between the Xray // and non-Xray machinery. The Xray machinery wants an API that returns a // JS::PropertyDescriptor, so that the resolve hook doesn't have to get // snared up with trying to define a property on the Xray holder. At the // same time, the DefineInterface callbacks are set up to define things // directly on the global. And re-jiggering them to return property // descriptors is tricky, because some DefineInterface callbacks define // multiple things (like the Image() alias for HTMLImageElement). // // So the setup is as-follows: // // * The resolve function takes a JS::PropertyDescriptor, but in the // non-Xray case, callees may define things directly on the global, and // set the value on the property descriptor to |undefined| to indicate // that there's nothing more for the caller to do. We assert against // this behavior in the Xray case. // // * We make sure that we do a non-Xray resolve first, so that all the // slots are set up. In the Xray case, this means unwrapping and doing // a non-Xray resolve before doing the Xray resolve. // // This all could use some grand refactoring, but for now we just limp // along. if (xpc::WrapperFactory::IsXrayWrapper(aObj)) { JS::Rooted<JSObject*> constructor(aCx); { JSAutoRealm ar(aCx, global); constructor = FindNamedConstructorForXray(aCx, aId, entry); } if (NS_WARN_IF(!constructor)) { return Throw(aCx, NS_ERROR_FAILURE); } if (!JS_WrapObject(aCx, &constructor)) { return Throw(aCx, NS_ERROR_FAILURE); } FillPropertyDescriptor(aDesc, aObj, 0, JS::ObjectValue(*constructor)); return true; } JS::Rooted<JSObject*> interfaceObject( aCx, GetPerInterfaceObjectHandle(aCx, entry->mConstructorId, entry->mCreate, /* aDefineOnGlobal = */ true)); if (NS_WARN_IF(!interfaceObject)) { return Throw(aCx, NS_ERROR_FAILURE); } // We've already defined the property. We indicate this to the caller // by filling a property descriptor with JS::UndefinedValue() as the // value. We still have to fill in a property descriptor, though, so // that the caller knows the property is in fact on this object. It // doesn't matter what we pass for the "readonly" argument here. FillPropertyDescriptor(aDesc, aObj, JS::UndefinedValue(), false); return true; }