/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef MOZ_WEBSPEECH # include "mozilla/dom/SpeechSynthesis.h" #endif
#ifdef ANDROID # include <android/log.h> #endif
#ifdef XP_WIN # include <process.h> # define getpid _getpid #else # include <unistd.h> // for getpid() #endif
usingnamespace mozilla; usingnamespace mozilla::dom; usingnamespace mozilla::dom::ipc; using mozilla::BasePrincipal; using mozilla::OriginAttributes; using mozilla::TimeStamp; using mozilla::layout::RemotePrintJobChild;
// Give OuterWindowProxyClass 2 reserved slots, like the other wrappers, so // JSObject::swap can swap it with CrossCompartmentWrappers without requiring // malloc. // // We store the nsGlobalWindowOuter* in our first slot. // // We store our holder weakmap in the second slot. const JSClass OuterWindowProxyClass = PROXY_CLASS_DEF( "Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); /* additional class flags */
// Standard internal methods /** * Implementation of [[GetOwnProperty]] as defined at * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx.
*/ bool getOwnPropertyDescriptor(
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const override;
/** * Implementation of [[OwnPropertyKeys]] as defined at * * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx.
*/ bool ownPropertyKeys(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandleVector<jsid> props) const override; /** * Implementation of [[Delete]] as defined at * https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx.
*/ bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::ObjectOpResult& result) const override;
/** * Implementaton of hook for superclass getPrototype() method.
*/
JSObject* getSameOriginPrototype(JSContext* cx) const override;
/** * Implementation of [[HasProperty]] internal method as defined at * https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx. * * Note that the HTML spec does not define an override for this internal * method, so we just want the "normal object" behavior. We have to override * it, because js::Wrapper also overrides, with "not normal" behavior.
*/ bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) const override;
/** * Implementation of [[Get]] internal method as defined at * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get>. * * "proxy" is the WindowProxy object involved. It may or may not be * same-compartment with "cx". * * "receiver" is the receiver ("this") for the get. It will be * same-compartment with "cx". * * "vp" is the return value. It will be same-compartment with "cx".
*/ bool get(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp) const override;
/** * Implementation of [[Set]] internal method as defined at * <https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set>. * * "proxy" is the WindowProxy object involved. It may or may not be * same-compartment with "cx". * * "v" is the value being set. It will be same-compartment with "cx". * * "receiver" is the receiver ("this") for the set. It will be * same-compartment with "cx".
*/ bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
JS::ObjectOpResult& result) const override;
// SpiderMonkey extensions /** * Implementation of SpiderMonkey extension which just checks whether this * object has the property. Basically Object.getOwnPropertyDescriptor(obj, * prop) !== undefined. but does not require reifying the descriptor. * * We have to override this because js::Wrapper overrides it, but we want * different behavior from js::Wrapper. * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx.
*/ bool hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) const override;
/** * Implementation of SpiderMonkey extension which is used as a fast path for * enumerating. * * We have to override this because js::Wrapper overrides it, but we want * different behavior from js::Wrapper. * * "proxy" is the WindowProxy object involved. It may not be same-compartment * with cx.
*/ bool getOwnEnumerablePropertyKeys(
JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandleVector<jsid> props) const override;
/** * Hook used by SpiderMonkey to implement Object.prototype.toString.
*/ constchar* className(JSContext* cx,
JS::Handle<JSObject*> wrapper) const override;
protected: // False return value means we threw an exception. True return value // but false "found" means we didn't have a subframe at that index. bool GetSubframeWindow(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp, bool& found) const;
// Returns a non-null window only if id is an index and we have a // window at that index.
Nullable<WindowProxyHolder> GetSubframeWindow(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id) const;
// Helper method for creating a special "print" method that allows printing // our PDF-viewer documents even if you're not same-origin with them. // // aProxy must be our nsOuterWindowProxy. It will not be same-compartment // with aCx, since we only use this on the different-origin codepath! // // Can return true without filling in aDesc, which corresponds to not exposing // a "print" method. staticbool MaybeGetPDFJSPrintMethod(
JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc);
// The actual "print" method we use for the PDFJS case. staticbool PDFJSPrintMethod(JSContext* cx, unsigned argc, JS::Value* vp);
// Helper method to get the pre-PDF-viewer-messing-with-it principal from an // inner window. Will return null if this is not a PDF-viewer inner or if the // principal could not be found for some reason. static already_AddRefed<nsIPrincipal> GetNoPDFJSPrincipal(
nsGlobalWindowInner* inner);
};
if (!IsPlatformObjectSameOrigin(cx, proxy)) { return"Object";
}
return"Window";
}
void nsOuterWindowProxy::finalize(JS::GCContext* gcx, JSObject* proxy) const {
nsGlobalWindowOuter* outerWindow = GetOuterWindow(proxy); if (outerWindow) {
outerWindow->ClearWrapper(proxy);
BrowsingContext* bc = outerWindow->GetBrowsingContext(); if (bc) {
bc->ClearWindowProxy();
}
// Ideally we would use OnFinalize here, but it's possible that // EnsureScriptEnvironment will later be called on the window, and we don't // want to create a new script object in that case. Therefore, we need to // write a non-null value that will reliably crash when dereferenced.
outerWindow->PoisonOuterWindowProxy(proxy);
}
}
bool nsOuterWindowProxy::getOwnPropertyDescriptor(
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) const { // First check for indexed access. This is // https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty // step 2, mostly.
JS::Rooted<JS::Value> subframe(cx); bool found; if (!GetSubframeWindow(cx, proxy, id, &subframe, found)) { returnfalse;
} if (found) { // Step 2.4.
// If we did not find a subframe, we could still have an indexed property // access. In that case we should throw a SecurityError in the cross-origin // case. if (!isSameOrigin && IsArrayIndex(GetArrayIndexFromId(id))) { // Step 2.5.2. return ReportCrossOriginDenial(cx, id, "access"_ns);
}
// Step 2.5.1 is handled via the forwarding to js::Wrapper; it saves us an // IsArrayIndex(GetArrayIndexFromId(id)) here. We'll never have a property on // the Window whose name is an index, because our defineProperty doesn't pass // those on to the Window.
// Step 3. if (isSameOrigin) { if (StaticPrefs::dom_missing_prop_counters_enabled() && id.isAtom()) {
Window_Binding::CountMaybeMissingProperty(proxy, id);
}
// Fall through to js::Wrapper.
{ // Scope for JSAutoRealm while we are dealing with js::Wrapper. // When forwarding to js::Wrapper, we should just enter the Realm of proxy // for now. That's what js::Wrapper expects, and since we're same-origin // anyway this is not changing any security behavior.
JSAutoRealm ar(cx, proxy);
JS_MarkCrossZoneId(cx, id); bool ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc); if (!ok) { returnfalse;
}
#if 0 // See https://github.com/tc39/ecma262/issues/672 for more information. if (desc.isSome() &&
!IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) {
(*desc).setConfigurable(true);
} #endif
}
// Now wrap our descriptor back into the Realm that asked for it. return JS_WrapPropertyDescriptor(cx, desc);
}
// Non-spec step for the PDF viewer's window.print(). This comes before we // check for named subframes, because in the same-origin case print() would // shadow those. if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PRINT)) { if (!MaybeGetPDFJSPrintMethod(cx, proxy, desc)) { returnfalse;
}
if (desc.isSome()) { returntrue;
}
}
// Step 6 -- check for named subframes. if (id.isString()) {
nsAutoJSString name; if (!name.init(cx, id.toString())) { returnfalse;
}
nsGlobalWindowOuter* win = GetOuterWindow(proxy); if (RefPtr<BrowsingContext> childDOMWin = win->GetChildWindow(name)) {
JS::Rooted<JS::Value> childValue(cx); if (!ToJSValue(cx, WindowProxyHolder(childDOMWin), &childValue)) { returnfalse;
}
desc.set(Some(JS::PropertyDescriptor::Data(
childValue, {JS::PropertyAttribute::Configurable}))); returntrue;
}
}
// And step 7. return CrossOriginPropertyFallback(cx, proxy, id, desc);
}
bool nsOuterWindowProxy::definePropertySameOrigin(
JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::Handle<JS::PropertyDescriptor> desc, JS::ObjectOpResult& result) const { if (IsArrayIndex(GetArrayIndexFromId(id))) { // Spec says to Reject whether this is a supported index or not, // since we have no indexed setter or indexed creator. It is up // to the caller to decide whether to throw a TypeError. return result.failCantDefineWindowElement();
}
JS::ObjectOpResult ourResult; bool ok = js::Wrapper::defineProperty(cx, proxy, id, desc, ourResult); if (!ok) { returnfalse;
}
if (!ourResult.ok()) { // It's possible that this failed because the page got the existing // descriptor (which we force to claim to be configurable) and then tried to // redefine the property with the descriptor it got but a different value. // We want to allow this case to succeed, so check for it and if we're in // that case try again but now with an attempt to define a non-configurable // property. if (!desc.hasConfigurable() || !desc.configurable()) { // The incoming descriptor was not explicitly marked "configurable: true", // so it failed for some other reason. Just propagate that reason out.
result = ourResult; returntrue;
}
JS::Rooted<Maybe<JS::PropertyDescriptor>> existingDesc(cx);
ok = js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, &existingDesc); if (!ok) { returnfalse;
} if (existingDesc.isNothing() || existingDesc->configurable()) { // We have no existing property, or its descriptor is already configurable // (on the Window itself, where things really can be non-configurable). // So we failed for some other reason, which we should propagate out.
result = ourResult; returntrue;
}
JS::ObjectOpResult ourNewResult;
ok = js::Wrapper::defineProperty(cx, proxy, id, updatedDesc, ourNewResult); if (!ok) { returnfalse;
}
if (!ourNewResult.ok()) { // Twiddling the configurable flag didn't help. Just return this failure // out to the caller.
result = ourNewResult; returntrue;
}
}
#if 0 // See https://github.com/tc39/ecma262/issues/672 for more information. if (desc.hasConfigurable() && !desc.configurable() &&
!IsNonConfigurableReadonlyPrimitiveGlobalProp(cx, id)) { // Give callers a way to detect that they failed to "really" define a // non-configurable property.
result.failCantDefineWindowNonConfigurable(); returntrue;
} #endif
result.succeed(); returntrue;
}
bool nsOuterWindowProxy::ownPropertyKeys(
JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandleVector<jsid> props) const { // Just our indexed stuff followed by our "normal" own property names. if (!AppendIndexedPropertyNames(proxy, props)) { returnfalse;
}
if (IsPlatformObjectSameOrigin(cx, proxy)) { // When forwarding to js::Wrapper, we should just enter the Realm of proxy // for now. That's what js::Wrapper expects, and since we're same-origin // anyway this is not changing any security behavior.
JS::RootedVector<jsid> innerProps(cx);
{ // Scope for JSAutoRealm so we can mark the ids once we exit it
JSAutoRealm ar(cx, proxy); if (!js::Wrapper::ownPropertyKeys(cx, proxy, &innerProps)) { returnfalse;
}
} for (auto& id : innerProps) {
JS_MarkCrossZoneId(cx, id);
} return js::AppendUnique(cx, props, innerProps);
}
// In the cross-origin case we purposefully exclude subframe names from the // list of property names we report here.
JS::Rooted<JSObject*> holder(cx); if (!EnsureHolder(cx, proxy, &holder)) { returnfalse;
}
if (!GetSubframeWindow(cx, proxy, id).IsNull()) { // Fail (which means throw if strict, else return false). return result.failCantDeleteWindowElement();
}
if (IsArrayIndex(GetArrayIndexFromId(id))) { // Indexed, but not supported. Spec says return true. return result.succeed();
}
// We're same-origin, so it should be safe to enter the Realm of "proxy". // Let's do that, just in case, to avoid cross-compartment issues in our // js::Wrapper caller..
JSAutoRealm ar(cx, proxy);
JS_MarkCrossZoneId(cx, id); return js::Wrapper::delete_(cx, proxy, id, result);
}
bool nsOuterWindowProxy::has(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* bp) const { // We could just directly forward this method to js::BaseProxyHandler, but // that involves reifying the actual property descriptor, which might be more // work than we have to do for has() on the Window.
if (!IsPlatformObjectSameOrigin(cx, proxy)) { // In the cross-origin case we only have own properties. Just call hasOwn // directly. return hasOwn(cx, proxy, id, bp);
}
if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
*bp = true; returntrue;
}
// Just to be safe in terms of compartment asserts, enter the Realm of // "proxy". We're same-origin with it, so this should be safe.
JSAutoRealm ar(cx, proxy);
JS_MarkCrossZoneId(cx, id); return js::Wrapper::has(cx, proxy, id, bp);
}
bool nsOuterWindowProxy::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* bp) const { // We could just directly forward this method to js::BaseProxyHandler, but // that involves reifying the actual property descriptor, which might be more // work than we have to do for hasOwn() on the Window.
if (!IsPlatformObjectSameOrigin(cx, proxy)) { // Avoiding reifying the property descriptor here would require duplicating // a bunch of "is this property exposed cross-origin" logic, which is // probably not worth it. Just forward this along to the base // implementation. // // It's very important to not forward this to js::Wrapper, because that will // not do the right security and cross-origin checks and will pass through // the call to the Window. // // The BaseProxyHandler code is OK with this happening without entering the // compartment of "proxy". return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
}
if (!GetSubframeWindow(cx, proxy, id).IsNull()) {
*bp = true; returntrue;
}
// Just to be safe in terms of compartment asserts, enter the Realm of // "proxy". We're same-origin with it, so this should be safe.
JSAutoRealm ar(cx, proxy);
JS_MarkCrossZoneId(cx, id); return js::Wrapper::hasOwn(cx, proxy, id, bp);
}
if (IsArrayIndex(GetArrayIndexFromId(id))) { // Reject the set. It's up to the caller to decide whether to throw a // TypeError. If the caller is strict mode JS code, it'll throw. return result.failReadOnly();
}
// Do the rest in the Realm of "proxy", since we're in the same-origin case.
JSAutoRealm ar(cx, proxy);
JS::Rooted<JS::Value> wrappedArg(cx, v); if (!MaybeWrapValue(cx, &wrappedArg)) { returnfalse;
}
JS::Rooted<JS::Value> wrappedReceiver(cx, receiver); if (!MaybeWrapValue(cx, &wrappedReceiver)) { returnfalse;
}
bool nsOuterWindowProxy::getOwnEnumerablePropertyKeys(
JSContext* cx, JS::Handle<JSObject*> proxy,
JS::MutableHandleVector<jsid> props) const { // We could just stop overring getOwnEnumerablePropertyKeys and let our // superclasses deal (by falling back on the BaseProxyHandler implementation // that uses a combination of ownPropertyKeys and getOwnPropertyDescriptor to // only return the enumerable ones. But maybe there's value in having // somewhat faster for-in iteration on Window objects...
// Like ownPropertyKeys, our indexed stuff followed by our "normal" enumerable // own property names. if (!AppendIndexedPropertyNames(proxy, props)) { returnfalse;
}
if (!IsPlatformObjectSameOrigin(cx, proxy)) { // All the cross-origin properties other than the indexed props are // non-enumerable, so we're done here. returntrue;
}
// When forwarding to js::Wrapper, we should just enter the Realm of proxy // for now. That's what js::Wrapper expects, and since we're same-origin // anyway this is not changing any security behavior.
JS::RootedVector<jsid> innerProps(cx);
{ // Scope for JSAutoRealm so we can mark the ids once we exit it.
JSAutoRealm ar(cx, proxy); if (!js::Wrapper::getOwnEnumerablePropertyKeys(cx, proxy, &innerProps)) { returnfalse;
}
}
for (auto& id : innerProps) {
JS_MarkCrossZoneId(cx, id);
}
nsGlobalWindowOuter* outer = GetOuterWindow(proxy);
nsGlobalWindowInner* inner =
nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow()); if (!inner) { // No print method to expose. returntrue;
}
nsCOMPtr<nsIPrincipal> targetPrincipal = GetNoPDFJSPrincipal(inner); if (!targetPrincipal) { // Nothing special to be done. returntrue;
}
if (!nsContentUtils::SubjectPrincipal(cx)->Equals(targetPrincipal)) { // Not our origin's PDF document. returntrue;
}
// Get the function we plan to actually call.
JS::Rooted<JSObject*> innerObj(cx, inner->GetGlobalJSObject()); if (!innerObj) { // Really should not happen, but ok, let's just return. returntrue;
}
if (!targetFunc.isObject()) { // Who knows what's going on. Just return. returntrue;
}
// The Realm of cx is the realm our caller is in and the realm we // should create our function in. Note that we can't use the // standard XPConnect function forwarder machinery because our // "this" is cross-origin, so we have to do thus by hand.
// Make sure targetFunc is wrapped into the right compartment. if (!MaybeWrapValue(cx, &targetFunc)) { returnfalse;
}
JSFunction* fun =
js::NewFunctionWithReserved(cx, PDFJSPrintMethod, 0, 0, "print"); if (!fun) { returnfalse;
}
// { value: <print>, writable: true, enumerable: true, configurable: true } // because that's what it would have been in the same-origin case without // the PDF viewer messing with things.
desc.set(Some(JS::PropertyDescriptor::Data(
JS::ObjectValue(*funObj),
{JS::PropertyAttribute::Configurable, JS::PropertyAttribute::Enumerable,
JS::PropertyAttribute::Writable}))); returntrue;
}
JS::Rooted<JSObject*> realCallee(
cx, &js::GetFunctionNativeReserved(&args.callee(), PDFJS_SLOT_CALLEE)
.toObject()); // Unchecked unwrap, because we want to extract the thing we really had // before.
realCallee = js::UncheckedUnwrap(realCallee);
JS::Rooted<JS::Value> thisv(cx, args.thisv()); if (thisv.isNullOrUndefined()) { // Replace it with the global of our stashed callee, simulating the // global-assuming behavior of DOM methods.
JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(realCallee)); if (!MaybeWrapObject(cx, &global)) { returnfalse;
}
thisv.setObject(*global);
} elseif (!thisv.isObject()) { return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
}
// We want to do an UncheckedUnwrap here, because we're going to directly // examine the principal of the inner window, if we have an inner window.
JS::Rooted<JSObject*> unwrappedObj(cx,
js::UncheckedUnwrap(&thisv.toObject()));
nsGlobalWindowInner* inner = nullptr;
{ // Do the unwrap in the Realm of the object we're looking at.
JSAutoRealm ar(cx, unwrappedObj);
UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Window, &unwrappedObj, inner, cx);
} if (!inner) { return ThrowInvalidThis(cx, args, false, prototypes::id::Window);
}
nsIPrincipal* callerPrincipal = nsContentUtils::SubjectPrincipal(cx); if (!callerPrincipal->SubsumesConsideringDomain(inner->GetPrincipal())) { // Check whether it's a PDF viewer from our origin.
nsCOMPtr<nsIPrincipal> pdfPrincipal = GetNoPDFJSPrincipal(inner); if (!pdfPrincipal || !callerPrincipal->Equals(pdfPrincipal)) { // Security error. return ThrowInvalidThis(cx, args, true, prototypes::id::Window);
}
}
// Go ahead and enter the Realm of our real callee to call it. We'll pass it // our "thisv", just in case someone grabs a "print" method off one PDF // document and .call()s it on another one.
{
JSAutoRealm ar(cx, realCallee); if (!MaybeWrapValue(cx, &thisv)) { returnfalse;
}
// Don't bother passing through the args; they will get ignored anyway.
if (!JS::Call(cx, thisv, realCallee, JS::HandleValueArray::empty(),
args.rval())) { returnfalse;
}
}
// Wrap the return value (not that there should be any!) into the right // compartment. return MaybeWrapValue(cx, args.rval());
}
MOZ_LOG(gDOMLeakPRLogOuter, LogLevel::Debug,
("DOMWINDOW %p created outer=nullptr", this));
// Add ourselves to the outer windows list.
MOZ_ASSERT(sOuterWindowsById, "Outer Windows hash table must be created!");
// |this| is an outer window, add to the outer windows list.
MOZ_ASSERT(!sOuterWindowsById->Contains(mWindowID), "This window shouldn't be in the hash table yet!"); // We seem to see crashes in release builds because of null // |sOuterWindowsById|. if (sOuterWindowsById) {
sOuterWindowsById->InsertOrUpdate(mWindowID, this);
}
}
if (sOuterWindowsById) {
sOuterWindowsById->Remove(mWindowID);
}
nsContentUtils::InnerOrOuterWindowDestroyed();
#ifdef DEBUG if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
nsAutoCString url; if (mLastOpenedURI) {
url = mLastOpenedURI->GetSpecOrDefault();
// Data URLs can be very long, so truncate to avoid flooding the log. const uint32_t maxURLLength = 1000; if (url.Length() > maxURLLength) {
url.Truncate(maxURLLength);
}
}
JSObject* proxy = GetWrapperMaybeDead(); if (proxy) { if (mBrowsingContext && mBrowsingContext->GetUnbarrieredWindowProxy()) {
nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
mBrowsingContext->GetUnbarrieredWindowProxy()); // Check that the current WindowProxy object corresponds to this // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if // we've replaced it with a cross-process WindowProxy. if (outer == this) {
mBrowsingContext->ClearWindowProxy();
}
}
js::SetProxyReservedSlot(proxy, OUTER_WINDOW_SLOT,
JS::PrivateValue(nullptr));
}
// An outer window is destroyed with inner windows still possibly // alive, iterate through the inner windows and null out their // back pointer to this outer, and pull them out of the list of // inner windows. // // Our linked list of inner windows both contains (an nsGlobalWindowOuter), // and our inner windows (nsGlobalWindowInners). This means that we need to // use PRCList*. We can then compare that PRCList* to `this` to see if its an // inner or outer window.
PRCList* w; while ((w = PR_LIST_HEAD(this)) != this) {
PR_REMOVE_AND_INIT_LINK(w);
}
DropOuterWindowDocs();
// Outer windows are always supposed to call CleanUp before letting themselves // be destroyed.
MOZ_ASSERT(mCleanedUp);
nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID); if (ac) ac->RemoveWindowAsListener(this);
// Unlink stuff from nsPIDOMWindow
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) if (tmp->mBrowsingContext) { if (tmp->mBrowsingContext->GetUnbarrieredWindowProxy()) {
nsGlobalWindowOuter* outer = nsOuterWindowProxy::GetOuterWindow(
tmp->mBrowsingContext->GetUnbarrieredWindowProxy()); // Check that the current WindowProxy object corresponds to this // nsGlobalWindowOuter, because we don't want to clear the WindowProxy if // we've replaced it with a cross-process WindowProxy. if (outer == tmp) {
tmp->mBrowsingContext->ClearWindowProxy();
}
}
tmp->mBrowsingContext = nullptr;
}
tmp->UnlinkObjectsInGlobal();
if (tmp->IsChromeWindow()) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeFields.mBrowserDOMWindow)
}
bool nsGlobalWindowOuter::IsBlackForCC(bool aTracingNeeded) { if (!nsCCUncollectableMarker::sGeneration) { returnfalse;
}
// Unlike most wrappers, the outer window wrapper is not a wrapper for // the outer window. Instead, the outer window wrapper holds the inner // window binding object, which in turn holds the nsGlobalWindowInner, which // has a strong reference to the nsGlobalWindowOuter. We're using the // mInnerWindow pointer as a flag for that whole chain. return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
(mInnerWindow && HasKnownLiveWrapper())) &&
(!aTracingNeeded || HasNothingToTrace(ToSupports(this)));
}
bool nsGlobalWindowOuter::ShouldResistFingerprinting(RFPTarget aTarget) const { if (mDoc) { return mDoc->ShouldResistFingerprinting(aTarget);
} return nsContentUtils::ShouldResistFingerprinting( "If we do not have a document then we do not have any context" "to make an informed RFP choice, so we fall back to the global pref",
aTarget);
}
nsresult nsGlobalWindowOuter::EnsureScriptEnvironment() { if (GetWrapperPreserveColor()) { return NS_OK;
}
NS_ENSURE_STATE(!mCleanedUp);
NS_ASSERTION(!GetCurrentInnerWindowInternal(this), "No cached wrapper, but we have an inner window?");
NS_ASSERTION(!mContext, "Will overwrite mContext!");
// If this window is an [i]frame, don't bother GC'ing when the frame's context // is destroyed since a GC will happen when the frameset or host document is // destroyed anyway.
mContext = new nsJSContext(mBrowsingContext->IsTop(), this); return NS_OK;
}
bool nsGlobalWindowOuter::WouldReuseInnerWindow(Document* 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(). // -- The new document has the same origin as what we have loaded right now.
// Great, we're the original document, check for one of the other // conditions.
if (mDoc == aNewDocument) { returntrue;
}
if (aNewDocument->IsStaticDocument()) { returnfalse;
}
if (BasePrincipal::Cast(mDoc->NodePrincipal())
->FastEqualsConsideringDomain(aNewDocument->NodePrincipal())) { // The origin is the same. returntrue;
}
returnfalse;
}
void nsGlobalWindowOuter::SetInitialPrincipal(
nsIPrincipal* aNewWindowPrincipal, nsIContentSecurityPolicy* aCSP, const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP) { // We should never create windows with an expanded principal. // If we have a system principal, make sure we're not using it for a content // docshell. // NOTE: Please keep this logic in sync with // nsAppShellService::JustCreateTopWindow if (nsContentUtils::IsExpandedPrincipal(aNewWindowPrincipal) ||
(aNewWindowPrincipal->IsSystemPrincipal() &&
GetBrowsingContext()->IsContent())) {
aNewWindowPrincipal = nullptr;
}
// If there's an existing document, bail if it either: if (mDoc) { // (a) is not an initial about:blank document, or if (!mDoc->IsInitialDocument()) return; // (b) already has the correct principal. if (mDoc->NodePrincipal() == aNewWindowPrincipal) return;
#ifdef DEBUG // If we have a document loaded at this point, it had better be about:blank. // Otherwise, something is really weird. An about:blank page has a // NullPrincipal. bool isNullPrincipal;
MOZ_ASSERT(NS_SUCCEEDED(mDoc->NodePrincipal()->GetIsNullPrincipal(
&isNullPrincipal)) &&
isNullPrincipal); #endif
}
// Use the subject (or system) principal as the storage principal too until // the new window finishes navigating and gets a real storage principal.
nsDocShell::Cast(GetDocShell())
->CreateAboutBlankDocumentViewer(aNewWindowPrincipal, aNewWindowPrincipal,
aCSP, nullptr, /* aIsInitialDocument */ true, aCOEP);
if (mDoc) {
MOZ_ASSERT(mDoc->IsInitialDocument(), "document should be initial document");
}
RefPtr<PresShell> presShell = GetDocShell()->GetPresShell(); if (presShell && !presShell->DidInitialize()) { // Ensure that if someone plays with this document they will get // layout happening.
presShell->Initialize();
}
}
nsGlobalWindowInner* mInnerWindow; // We hold onto this to make sure the inner window doesn't go away. The outer // window ends up recalculating it anyway.
JS::PersistentRooted<JSObject*> mInnerWindowReflector;
};
// When a global goes into the bfcache, we disable script.
xpc::Scriptability::Get(mInnerWindowReflector).SetWindowAllowsScript(false);
}
WindowStateHolder::~WindowStateHolder() { if (mInnerWindow) { // This window was left in the bfcache and is now going away. We need to // free it up. // Note that FreeInnerObjects may already have been called on the // inner window if its outer has already had SetDocShell(null) // called.
mInnerWindow->FreeInnerObjects();
}
}
if (WindowContext* parentWindow =
GetBrowsingContext()->GetParentWindowContext()) {
hadNonSecureContextCreator = !parentWindow->GetIsSecureContext();
}
if (hadNonSecureContextCreator) { returnfalse;
}
if (nsContentUtils::HttpsStateIsModern(aDocument)) { returntrue;
}
if (principal->GetIsNullPrincipal()) { // If the NullPrincipal has a valid precursor URI we want to use it to // construct the principal otherwise we fall back to the original document // URI.
nsCOMPtr<nsIPrincipal> precursorPrin = principal->GetPrecursorPrincipal();
nsCOMPtr<nsIURI> uri = precursorPrin ? precursorPrin->GetURI() : nullptr; if (!uri) {
uri = aDocument->GetOriginalURI();
} // IsOriginPotentiallyTrustworthy doesn't care about origin attributes so // it doesn't actually matter what we use here, but reusing the document // principal's attributes is convenient. const OriginAttributes& attrs = principal->OriginAttributesRef(); // CreateContentPrincipal correctly gets a useful principal for blob: and // other URI_INHERITS_SECURITY_CONTEXT URIs.
principal = BasePrincipal::CreateContentPrincipal(uri, attrs); if (NS_WARN_IF(!principal)) { returnfalse;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.