From 524e4f032a4116b418bd43cbd013dd433f9686c7 Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Wed, 5 Jun 2024 00:06:48 +0000 Subject: [PATCH] Bug 1681457 - Allow non-auxiliary BrowsingContexts created by script to close themselves, r=smaug Differential Revision: https://phabricator.services.mozilla.com/D211792 --- docshell/base/BrowsingContext.cpp | 29 ++++++++++++------- docshell/base/BrowsingContext.h | 16 +++++++++- docshell/base/CanonicalBrowsingContext.cpp | 1 + dom/base/Document.cpp | 2 +- dom/base/nsFrameLoader.cpp | 14 ++++++--- dom/base/nsGlobalWindowOuter.cpp | 6 ++-- dom/base/test/test_window_close.html | 4 +-- dom/ipc/ContentChild.cpp | 13 ++++++--- dom/ipc/ContentParent.cpp | 24 ++++++++------- dom/ipc/ContentParent.h | 17 ++++++----- dom/ipc/PContent.ipdl | 2 ++ .../close_noopener_beforeunload-1.html | 8 +++++ .../close_noopener_beforeunload-2.html | 6 ++++ .../close_noopener_beforeunload.html | 24 +++++++++++++++ .../windowwatcher/nsIOpenWindowInfo.idl | 4 +++ .../windowwatcher/nsOpenWindowInfo.cpp | 6 ++++ .../windowwatcher/nsOpenWindowInfo.h | 1 + .../windowwatcher/nsWindowWatcher.cpp | 8 +++++ .../exthandler/nsExternalHelperAppService.cpp | 2 +- 19 files changed, 143 insertions(+), 44 deletions(-) create mode 100644 testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-1.html create mode 100644 testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-2.html create mode 100644 testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload.html diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp index 6e1a1b689398..db5b5b990727 100644 --- a/docshell/base/BrowsingContext.cpp +++ b/docshell/base/BrowsingContext.cpp @@ -325,7 +325,7 @@ bool BrowsingContext::SameOriginWithTop() { already_AddRefed BrowsingContext::CreateDetached( nsGlobalWindowInner* aParent, BrowsingContext* aOpener, BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType, - bool aIsPopupRequested, bool aCreatedDynamically) { + CreateDetachedOptions aOptions) { if (aParent) { MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext()); MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType); @@ -461,7 +461,10 @@ already_AddRefed BrowsingContext::CreateDetached( fields.Get() = inherit ? inherit->GetAllowJavascript() : true; - fields.Get() = aIsPopupRequested; + fields.Get() = aOptions.isPopupRequested; + + fields.Get() = + aOptions.topLevelCreatedByWebContent; if (!parentBC) { fields.Get() = @@ -480,7 +483,7 @@ already_AddRefed BrowsingContext::CreateDetached( } context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent; - context->mCreatedDynamically = aCreatedDynamically; + context->mCreatedDynamically = aOptions.createdDynamically; if (inherit) { context->mPrivateBrowsingId = inherit->mPrivateBrowsingId; context->mUseRemoteTabs = inherit->mUseRemoteTabs; @@ -507,7 +510,7 @@ already_AddRefed BrowsingContext::CreateIndependent( "BCs created in the content process must be related to " "some BrowserChild"); RefPtr bc( - CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, false)); + CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, {})); bc->mWindowless = bc->IsContent(); bc->mEmbeddedByThisProcess = true; bc->EnsureAttached(); @@ -3162,8 +3165,8 @@ bool BrowsingContext::CanSet(FieldIndex, } auto BrowsingContext::CanSet(FieldIndex, - const nsString& aUserAgent, ContentParent* aSource) - -> CanSetResult { + const nsString& aUserAgent, + ContentParent* aSource) -> CanSetResult { if (!IsTop()) { return CanSetResult::Deny; } @@ -3172,8 +3175,8 @@ auto BrowsingContext::CanSet(FieldIndex, } auto BrowsingContext::CanSet(FieldIndex, - const nsString& aPlatform, ContentParent* aSource) - -> CanSetResult { + const nsString& aPlatform, + ContentParent* aSource) -> CanSetResult { if (!IsTop()) { return CanSetResult::Deny; } @@ -3207,8 +3210,8 @@ bool BrowsingContext::CanSet(FieldIndex, } auto BrowsingContext::CanSet(FieldIndex, - const uint64_t& aValue, ContentParent* aSource) - -> CanSetResult { + const uint64_t& aValue, + ContentParent* aSource) -> CanSetResult { // Generally allow clearing this. We may want to be more precise about this // check in the future. if (aValue == 0) { @@ -3534,6 +3537,12 @@ bool BrowsingContext::CanSet(FieldIndex, return IsTop() && GetPendingInitialization() && !aNewValue; } +bool BrowsingContext::CanSet(FieldIndex, + const bool& aNewValue, ContentParent* aSource) { + // Should only be set after creation in the parent process. + return XRE_IsParentProcess() && !aSource && IsTop(); +} + bool BrowsingContext::CanSet(FieldIndex, bool aNewValue, ContentParent* aSource) { return IsTop(); diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h index 5ec95a61e4d3..61135ab0d789 100644 --- a/docshell/base/BrowsingContext.h +++ b/docshell/base/BrowsingContext.h @@ -135,6 +135,10 @@ struct EmbedderColorSchemes { FIELD(EmbedderInnerWindowId, uint64_t) \ FIELD(CurrentInnerWindowId, uint64_t) \ FIELD(HadOriginalOpener, bool) \ + /* Was this window created by a webpage through window.open or an anchor \ + * link? In general, windows created this way may be manipulated (e.g. \ + * closed, resized or moved) by content JS. */ \ + FIELD(TopLevelCreatedByWebContent, bool) \ FIELD(IsPopupSpam, bool) \ /* Hold the audio muted state and should be used on top level browsing \ * contexts only */ \ @@ -331,6 +335,13 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { // it. static already_AddRefed CreateIndependent(Type aType); + // Options which can be passed to CreateDetached. + struct CreateDetachedOptions { + bool isPopupRequested = false; + bool createdDynamically = false; + bool topLevelCreatedByWebContent = false; + }; + // Create a brand-new BrowsingContext object, but does not immediately attach // it. State such as OriginAttributes and PrivateBrowsingId may be customized // to configure the BrowsingContext before it is attached. @@ -340,7 +351,7 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { static already_AddRefed CreateDetached( nsGlobalWindowInner* aParent, BrowsingContext* aOpener, BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType, - bool aIsPopupRequested, bool aCreatedDynamically = false); + CreateDetachedOptions aOptions); void EnsureAttached(); @@ -1211,6 +1222,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { bool CanSet(FieldIndex, bool aNewValue, ContentParent* aSource); + bool CanSet(FieldIndex, + const bool& aNewValue, ContentParent* aSource); + bool CanSet(FieldIndex, uint32_t aNewValue, ContentParent* aSource); void DidSet(FieldIndex, uint32_t aOldValue); diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp index 78de7474b009..beae24fbd122 100644 --- a/docshell/base/CanonicalBrowsingContext.cpp +++ b/docshell/base/CanonicalBrowsingContext.cpp @@ -317,6 +317,7 @@ void CanonicalBrowsingContext::ReplacedBy( txn.SetBrowserId(GetBrowserId()); txn.SetIsAppTab(GetIsAppTab()); txn.SetHasSiblings(GetHasSiblings()); + txn.SetTopLevelCreatedByWebContent(GetTopLevelCreatedByWebContent()); txn.SetHistoryID(GetHistoryID()); txn.SetExplicitActive(GetExplicitActive()); txn.SetEmbedderColorSchemes(GetEmbedderColorSchemes()); diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index e277e6e37507..450e83209fb2 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -18980,7 +18980,7 @@ bool Document::IsLikelyContentInaccessibleTopLevelAboutBlank() const { // really reliable but doesn't affect the correctness of our page probes, so // it's not too terrible. BrowsingContext* bc = GetBrowsingContext(); - return bc && bc->IsTop() && !bc->HadOriginalOpener(); + return bc && bc->IsTop() && !bc->GetTopLevelCreatedByWebContent(); } bool Document::ShouldIncludeInTelemetry() const { diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 162ff2ed9483..e4973191be3d 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -337,10 +337,16 @@ static already_AddRefed CreateBrowsingContext( // it will wind up attached as a child of the currently active inner window // for the BrowsingContext, and cause no end of trouble. if (IsTopContent(parentBC, aOwner)) { + BrowsingContext::CreateDetachedOptions options; + if (aOpenWindowInfo) { + options.topLevelCreatedByWebContent = + aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(); + } + // Create toplevel context without a parent & as Type::Content. return BrowsingContext::CreateDetached( nullptr, opener, aSpecificGroup, frameName, - BrowsingContext::Type::Content, false); + BrowsingContext::Type::Content, options); } MOZ_ASSERT(!aOpenWindowInfo, @@ -348,9 +354,9 @@ static already_AddRefed CreateBrowsingContext( MOZ_ASSERT(!aSpecificGroup, "Can't force BrowsingContextGroup for non-toplevel context"); - return BrowsingContext::CreateDetached(parentInner, nullptr, nullptr, - frameName, parentBC->GetType(), false, - !aNetworkCreated); + return BrowsingContext::CreateDetached( + parentInner, nullptr, nullptr, frameName, parentBC->GetType(), + {.createdDynamically = !aNetworkCreated}); } static bool InitialLoadIsRemote(Element* aOwner) { diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp index ec9c89cb63e3..460ccc17f2cd 100644 --- a/dom/base/nsGlobalWindowOuter.cpp +++ b/dom/base/nsGlobalWindowOuter.cpp @@ -4642,7 +4642,7 @@ bool nsGlobalWindowOuter::CanMoveResizeWindows(CallerType aCallerType) { if (aCallerType != CallerType::System) { // Don't allow scripts to move or resize windows that were not opened by a // script. - if (!mBrowsingContext->HadOriginalOpener()) { + if (!mBrowsingContext->GetTopLevelCreatedByWebContent()) { return false; } @@ -6001,8 +6001,8 @@ void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) { NS_ENSURE_SUCCESS_VOID(rv); if (!StringBeginsWith(url, u"about:neterror"_ns) && - !mBrowsingContext->HadOriginalOpener() && !aTrustedCaller && - !IsOnlyTopLevelDocumentInSHistory()) { + !mBrowsingContext->GetTopLevelCreatedByWebContent() && + !aTrustedCaller && !IsOnlyTopLevelDocumentInSHistory()) { bool allowClose = mAllowScriptsToClose || Preferences::GetBool("dom.allow_scripts_to_close_windows", true); diff --git a/dom/base/test/test_window_close.html b/dom/base/test/test_window_close.html index 5b0d7e6fa695..3dea56502579 100644 --- a/dom/base/test/test_window_close.html +++ b/dom/base/test/test_window_close.html @@ -16,7 +16,7 @@ type: windowopen, noopener: true, shouldCloseWithoutHistory: true, - shouldCloseWithHistory: false + shouldCloseWithHistory: true }, { type: windowopen, @@ -28,7 +28,7 @@ type: link, noopener: true, shouldCloseWithoutHistory: true, - shouldCloseWithHistory: false + shouldCloseWithHistory: true }, { type: link, diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index c5c576517996..6cb0f9f0e257 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1034,7 +1034,8 @@ nsresult ContentChild::ProvideWindowCommon( MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(name)); Unused << SendCreateWindowInDifferentProcess( - aTabOpener, parent, aChromeFlags, aCalledFromJS, aURI, features, + aTabOpener, parent, aChromeFlags, aCalledFromJS, + aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(), aURI, features, aModifiers, name, triggeringPrincipal, csp, referrerInfo, aOpenWindowInfo->GetOriginAttributes()); @@ -1056,7 +1057,8 @@ nsresult ContentChild::ProvideWindowCommon( RefPtr browsingContext = BrowsingContext::CreateDetached( nullptr, openerBC, nullptr, aName, BrowsingContext::Type::Content, - aIsPopupRequested); + {.isPopupRequested = aIsPopupRequested, + .topLevelCreatedByWebContent = true}); MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteTabs(true)); MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteSubframes(useRemoteSubframes)); MOZ_ALWAYS_SUCCEEDS(browsingContext->SetOriginAttributes( @@ -1181,9 +1183,11 @@ nsresult ContentChild::ProvideWindowCommon( if (aForceNoOpener || !parent) { MOZ_DIAGNOSTIC_ASSERT(!browsingContext->HadOriginalOpener()); + MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent()); MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == 0); } else { MOZ_DIAGNOSTIC_ASSERT(browsingContext->HadOriginalOpener()); + MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent()); MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == parent->Id()); } @@ -1236,8 +1240,9 @@ nsresult ContentChild::ProvideWindowCommon( SendCreateWindow(aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS, aOpenWindowInfo->GetIsForPrinting(), - aOpenWindowInfo->GetIsForWindowDotPrint(), aURI, features, - aModifiers, triggeringPrincipal, csp, referrerInfo, + aOpenWindowInfo->GetIsForWindowDotPrint(), + aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(), aURI, + features, aModifiers, triggeringPrincipal, csp, referrerInfo, aOpenWindowInfo->GetOriginAttributes(), std::move(resolve), std::move(reject)); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index a2d0f78323b0..6e72098459be 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5567,8 +5567,8 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener, const uint32_t& aChromeFlags, const bool& aCalledFromJS, const bool& aForPrinting, const bool& aForWindowDotPrint, - nsIURI* aURIToLoad, const nsACString& aFeatures, - const UserActivation::Modifiers& aModifiers, + const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad, + const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, BrowserParent* aNextRemoteBrowser, const nsAString& aName, nsresult& aResult, nsCOMPtr& aNewRemoteTab, bool* aWindowIsNew, int32_t& aOpenLocation, @@ -5592,6 +5592,7 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow( openInfo->mIsForWindowDotPrint = aForWindowDotPrint; openInfo->mNextRemoteBrowser = aNextRemoteBrowser; openInfo->mOriginAttributes = aOriginAttributes; + openInfo->mIsTopLevelCreatedByWebContent = aIsTopLevelCreatedByWebContent; MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting); @@ -5794,8 +5795,9 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow( PBrowserParent* aThisTab, const MaybeDiscarded& aParent, PBrowserParent* aNewTab, const uint32_t& aChromeFlags, const bool& aCalledFromJS, const bool& aForPrinting, - const bool& aForWindowDotPrint, nsIURI* aURIToLoad, - const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, + const bool& aForWindowDotPrint, const bool& aIsTopLevelCreatedByWebContent, + nsIURI* aURIToLoad, const nsACString& aFeatures, + const UserActivation::Modifiers& aModifiers, nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes, CreateWindowResolver&& aResolve) { @@ -5880,10 +5882,10 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow( int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW; mozilla::ipc::IPCResult ipcResult = CommonCreateWindow( aThisTab, *parent, newBCOpenerId != 0, aChromeFlags, aCalledFromJS, - aForPrinting, aForWindowDotPrint, aURIToLoad, aFeatures, aModifiers, - newTab, VoidString(), rv, newRemoteTab, &cwi.windowOpened(), openLocation, - aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ false, aCsp, - aOriginAttributes); + aForPrinting, aForWindowDotPrint, aIsTopLevelCreatedByWebContent, + aURIToLoad, aFeatures, aModifiers, newTab, VoidString(), rv, newRemoteTab, + &cwi.windowOpened(), openLocation, aTriggeringPrincipal, aReferrerInfo, + /* aLoadUri = */ false, aCsp, aOriginAttributes); if (!ipcResult) { return ipcResult; } @@ -5916,7 +5918,8 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow( mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess( PBrowserParent* aThisTab, const MaybeDiscarded& aParent, - const uint32_t& aChromeFlags, const bool& aCalledFromJS, nsIURI* aURIToLoad, + const uint32_t& aChromeFlags, const bool& aCalledFromJS, + const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad, const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, const nsAString& aName, nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, @@ -5962,7 +5965,8 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess( mozilla::ipc::IPCResult ipcResult = CommonCreateWindow( aThisTab, *parent, /* aSetOpener = */ false, aChromeFlags, aCalledFromJS, /* aForPrinting = */ false, - /* aForPrintPreview = */ false, aURIToLoad, aFeatures, aModifiers, + /* aForWindowDotPrint = */ false, aIsTopLevelCreatedByWebContent, + aURIToLoad, aFeatures, aModifiers, /* aNextRemoteBrowser = */ nullptr, aName, rv, newRemoteTab, &windowIsNew, openLocation, aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ true, aCsp, aOriginAttributes); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 8040f4a07b47..e0e24cd9db14 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -474,8 +474,9 @@ class ContentParent final : public PContentParent, PBrowserParent* aThisTab, const MaybeDiscarded& aParent, PBrowserParent* aNewTab, const uint32_t& aChromeFlags, const bool& aCalledFromJS, const bool& aForPrinting, - const bool& aForWindowDotPrint, nsIURI* aURIToLoad, - const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, + const bool& aForWindowDotPrint, const bool& aTopLevelCreatedByWebContent, + nsIURI* aURIToLoad, const nsACString& aFeatures, + const UserActivation::Modifiers& aModifiers, nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes, CreateWindowResolver&& aResolve); @@ -483,10 +484,10 @@ class ContentParent final : public PContentParent, mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess( PBrowserParent* aThisTab, const MaybeDiscarded& aParent, const uint32_t& aChromeFlags, const bool& aCalledFromJS, - nsIURI* aURIToLoad, const nsACString& aFeatures, - const UserActivation::Modifiers& aModifiers, const nsAString& aName, - nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp, - nsIReferrerInfo* aReferrerInfo, + const bool& aTopLevelCreatedByWebContent, nsIURI* aURIToLoad, + const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, + const nsAString& aName, nsIPrincipal* aTriggeringPrincipal, + nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes); static void BroadcastBlobURLRegistration( @@ -700,8 +701,8 @@ class ContentParent final : public PContentParent, PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener, const uint32_t& aChromeFlags, const bool& aCalledFromJS, const bool& aForPrinting, const bool& aForWindowDotPrint, - nsIURI* aURIToLoad, const nsACString& aFeatures, - const UserActivation::Modifiers& aModifiers, + const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad, + const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers, BrowserParent* aNextRemoteBrowser, const nsAString& aName, nsresult& aResult, nsCOMPtr& aNewRemoteTab, bool* aWindowIsNew, int32_t& aOpenLocation, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 0aaef08b7bbb..e8509345367b 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -1533,6 +1533,7 @@ parent: bool aCalledFromJS, bool aForPrinting, bool aForWindowDotPrint, + bool aTopLevelCreatedByContent, nullable nsIURI aURIToLoad, nsCString aFeatures, Modifiers aModifiers, @@ -1547,6 +1548,7 @@ parent: MaybeDiscardedBrowsingContext aParent, uint32_t aChromeFlags, bool aCalledFromJS, + bool aTopLevelCreatedByContent, nullable nsIURI aURIToLoad, nsCString aFeatures, Modifiers aModifiers, diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-1.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-1.html new file mode 100644 index 000000000000..fc114bc443e5 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-1.html @@ -0,0 +1,8 @@ + + diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-2.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-2.html new file mode 100644 index 000000000000..4cce1e2e6f7d --- /dev/null +++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload-2.html @@ -0,0 +1,6 @@ + + diff --git a/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload.html b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload.html new file mode 100644 index 000000000000..6862a70b8c14 --- /dev/null +++ b/testing/web-platform/tests/html/browsers/the-window-object/open-close/close_noopener_beforeunload.html @@ -0,0 +1,24 @@ + +Running beforeunload handler in window.close() for noopener window + + +
+ diff --git a/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl b/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl index c0edb1853651..9685498153f1 100644 --- a/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl +++ b/toolkit/components/windowwatcher/nsIOpenWindowInfo.idl @@ -60,6 +60,10 @@ interface nsIOpenWindowInfo : nsISupports { [infallible] readonly attribute boolean isForWindowDotPrint; + /** Whether this new window creation was prompted by web content */ + [infallible] + readonly attribute boolean isTopLevelCreatedByWebContent; + /** BrowserParent instance to use in the new window */ [notxpcom, nostdcall] BrowserParent getNextRemoteBrowser(); diff --git a/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp b/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp index 042ee45f4e0f..87044b94f393 100644 --- a/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp +++ b/toolkit/components/windowwatcher/nsOpenWindowInfo.cpp @@ -37,6 +37,12 @@ NS_IMETHODIMP nsOpenWindowInfo::GetForceNoOpener(bool* aForceNoOpener) { return NS_OK; } +NS_IMETHODIMP nsOpenWindowInfo::GetIsTopLevelCreatedByWebContent( + bool* aIsTopLevelCreatedByWebContent) { + *aIsTopLevelCreatedByWebContent = mIsTopLevelCreatedByWebContent; + return NS_OK; +} + NS_IMETHODIMP nsOpenWindowInfo::GetScriptableOriginAttributes( JSContext* aCx, JS::MutableHandle aAttrs) { bool ok = ToJSValue(aCx, mOriginAttributes, aAttrs); diff --git a/toolkit/components/windowwatcher/nsOpenWindowInfo.h b/toolkit/components/windowwatcher/nsOpenWindowInfo.h index 8a353dda6bfa..c7ed5a59c691 100644 --- a/toolkit/components/windowwatcher/nsOpenWindowInfo.h +++ b/toolkit/components/windowwatcher/nsOpenWindowInfo.h @@ -22,6 +22,7 @@ class nsOpenWindowInfo : public nsIOpenWindowInfo { bool mIsRemote = false; bool mIsForPrinting = false; bool mIsForWindowDotPrint = false; + bool mIsTopLevelCreatedByWebContent = false; RefPtr mNextRemoteBrowser; mozilla::OriginAttributes mOriginAttributes; RefPtr mParent; diff --git a/toolkit/components/windowwatcher/nsWindowWatcher.cpp b/toolkit/components/windowwatcher/nsWindowWatcher.cpp index b482214d31fe..0767cb1539f9 100644 --- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp +++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp @@ -884,6 +884,8 @@ nsresult nsWindowWatcher::OpenWindowInternal( openWindowInfo->mParent = parentBC; openWindowInfo->mIsForPrinting = aPrintKind != PRINT_NONE; openWindowInfo->mIsForWindowDotPrint = aPrintKind == PRINT_WINDOW_DOT_PRINT; + openWindowInfo->mIsTopLevelCreatedByWebContent = + !nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal); // We're going to want the window to be immediately available, meaning we // want it to match the current remoteness. @@ -1077,6 +1079,12 @@ nsresult nsWindowWatcher::OpenWindowInternal( return rv; } + MOZ_DIAGNOSTIC_ASSERT( + !windowIsNew || !targetBC->IsContent() || + nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal) || + targetBC->GetTopLevelCreatedByWebContent(), + "New BC not marked as created by web content, but it was"); + // If our parent is sandboxed, set it as the one permitted sandboxed navigator // on the new window we're opening. if (activeDocsSandboxFlags && parentBC) { diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index bbef8c0aa504..b9120ededd25 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -1061,7 +1061,7 @@ nsExternalHelperAppService::LoadURI(nsIURI* aURI, // Also allow this load if the target is a toplevel BC and contains a // non-web-controlled about:blank document - if (bc->IsTop() && !bc->HadOriginalOpener() && wgp) { + if (bc->IsTop() && !bc->GetTopLevelCreatedByWebContent() && wgp) { RefPtr uri = wgp->GetDocumentURI(); foundAccessibleFrame = uri && uri->GetSpecOrDefault().EqualsLiteral("about:blank");