diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 1e2aa26286b8..50afc84eded1 100755 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -4250,10 +4250,10 @@ var XULBrowserWindow = { return initBrowser.frameLoader.tabParent; }, - forceInitialBrowserNonRemote: function() { + forceInitialBrowserNonRemote: function(aOpener) { let initBrowser = document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser"); - gBrowser.updateBrowserRemoteness(initBrowser, false); + gBrowser.updateBrowserRemoteness(initBrowser, false, aOpener); }, setDefaultStatus: function (status) { @@ -4901,7 +4901,8 @@ nsBrowserAccess.prototype = { _openURIInNewTab: function(aURI, aReferrer, aReferrerPolicy, aIsPrivate, aIsExternal, aForceNotRemote=false, - aUserContextId=Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID) { + aUserContextId=Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID, + aOpener=null) { let win, needToFocusWin; // try the current window. if we're in a popup, fall back on the most recent browser window @@ -4931,7 +4932,9 @@ nsBrowserAccess.prototype = { userContextId: aUserContextId, fromExternal: aIsExternal, inBackground: loadInBackground, - forceNotRemote: aForceNotRemote}); + forceNotRemote: aForceNotRemote, + opener: aOpener, + }); let browser = win.gBrowser.getBrowserForTab(tab); if (needToFocusWin || (!loadInBackground && aIsExternal)) @@ -5006,7 +5009,8 @@ nsBrowserAccess.prototype = { : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID; let browser = this._openURIInNewTab(aURI, referrer, referrerPolicy, isPrivate, isExternal, - forceNotRemote, userContextId); + forceNotRemote, userContextId, + aOpener); if (browser) newWindow = browser.contentWindow; break; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index f5da7a9d6344..cbcf73cfa4f9 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1503,6 +1503,7 @@ var aUserContextId; var aRelatedBrowser; var aOriginPrincipal; + var aOpener; if (arguments.length == 2 && typeof arguments[1] == "object" && !(arguments[1] instanceof Ci.nsIURI)) { @@ -1522,6 +1523,7 @@ aUserContextId = params.userContextId; aRelatedBrowser = params.relatedBrowser; aOriginPrincipal = params.originPrincipal; + aOpener = params.opener; } var bgLoad = (aLoadInBackground != null) ? aLoadInBackground : @@ -1542,7 +1544,8 @@ noReferrer: aNoReferrer, userContextId: aUserContextId, originPrincipal: aOriginPrincipal, - relatedBrowser: aRelatedBrowser }); + relatedBrowser: aRelatedBrowser, + opener: aOpener }); if (!bgLoad) this.selectedTab = tab; @@ -1655,11 +1658,27 @@ + DropDocumentReference(); } + if (mDocGroup) { + mDocGroup->RemoveDocument(this); + } + UnlinkOriginalDocumentIfStatic(); } @@ -2848,6 +2854,36 @@ nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal) } } mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); + +#ifdef DEBUG + // Validate that the docgroup is set correctly by calling its getter and + // triggering its sanity check. + // + // If we're setting the principal to null, we don't want to perform the check, + // as the document is entering an intermediate state where it does not have a + // principal. It will be given another real principal shortly which we will + // check. It's not unsafe to have a document which has a null principal in the + // same docgroup as another document, so this should not be a problem. + if (aNewPrincipal) { + GetDocGroup(); + } +#endif +} + +mozilla::dom::DocGroup* +nsIDocument::GetDocGroup() +{ +#ifdef DEBUG + // Sanity check that we have an up-to-date and accurate docgroup + if (mDocGroup) { + nsAutoCString docGroupKey; + mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey); + MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey)); + // XXX: Check that the TabGroup is correct as well! + } +#endif + + return mDocGroup; } NS_IMETHODIMP @@ -4254,6 +4290,23 @@ nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) mScopeObject = do_GetWeakReference(aGlobal); if (aGlobal) { mHasHadScriptHandlingObject = true; + + nsCOMPtr window = do_QueryInterface(aGlobal); + if (window) { + // We want to get the tabgroup unconditionally, such that we can make + // certain that it is cached in the inner window early enough. + mozilla::dom::TabGroup* tabgroup = nsGlobalWindow::Cast(window)->TabGroup(); + // We should already have the principal, and now that we have been added to a + // window, we should be able to join a DocGroup! + nsAutoCString docGroupKey; + mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey); + if (mDocGroup) { + MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey)); + } else { + mDocGroup = tabgroup->AddDocument(docGroupKey, this); + MOZ_ASSERT(mDocGroup); + } + } } } @@ -4442,11 +4495,10 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) mScriptGlobalObject = aScriptGlobalObject; if (aScriptGlobalObject) { - mHasHadScriptHandlingObject = true; - mHasHadDefaultView = true; // Go back to using the docshell for the layout history state mLayoutHistoryState = nullptr; - mScopeObject = do_GetWeakReference(aScriptGlobalObject); + SetScopeObject(aScriptGlobalObject); + mHasHadDefaultView = true; #ifdef DEBUG if (!mWillReparent) { // We really shouldn't have a wrapper here but if we do we need to make sure @@ -4583,8 +4635,7 @@ nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) mScriptGlobalObject == aScriptObject, "Wrong script object!"); if (aScriptObject) { - mScopeObject = do_GetWeakReference(aScriptObject); - mHasHadScriptHandlingObject = true; + SetScopeObject(aScriptObject); mHasHadDefaultView = false; } } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 50faf61bf9de..c00dabe1d262 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -81,6 +81,7 @@ #include "nsAboutProtocolUtils.h" #include "nsCharTraits.h" // NS_IS_HIGH/LOW_SURROGATE #include "PostMessageEvent.h" +#include "DocGroup.h" // Interfaces Needed #include "nsIFrame.h" @@ -1226,9 +1227,10 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mCleanedUp(false), mDialogAbuseCount(0), mAreDialogsEnabled(true), - mCanSkipCCGeneration(0), - mStaticConstellation(0), - mConstellation(NullCString()) +#ifdef DEBUG + mIsValidatingTabGroup(false), +#endif + mCanSkipCCGeneration(0) { AssertIsOnMainThread(); @@ -1262,11 +1264,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) // |this| is an outer window. Outer windows start out frozen and // remain frozen until they get an inner window. MOZ_ASSERT(IsFrozen()); - - // As an outer window, we may be the root of a constellation. This initial - // static constellation may be overridden as this window is given a parent - // window or an opener. - mStaticConstellation = WindowID(); } // We could have failed the first time through trying @@ -1430,6 +1427,11 @@ nsGlobalWindow::~nsGlobalWindow() } } + // We don't have to leave the tab group if we are an inner window. + if (mTabGroup && IsOuterWindow()) { + mTabGroup->Leave(AsOuter()); + } + // Outer windows are always supposed to call CleanUp before letting themselves // be destroyed. And while CleanUp generally seems to be intended to clean up // outers, we've historically called it for both. Changing this would probably @@ -2856,6 +2858,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, nsCOMPtr kungFuDeathGrip(mContext); aDocument->SetScriptGlobalObject(newInnerWindow); + MOZ_ASSERT(newInnerWindow->mTabGroup, + "We must have a TabGroup cached at this point"); if (!aState) { if (reUseInnerWindow) { @@ -3032,11 +3036,8 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) mDocShell = aDocShell; // Weak Reference - // Copy over the static constellation from our new parent. - nsCOMPtr parentWindow = GetParent(); - if (parentWindow) { - mStaticConstellation = Cast(parentWindow)->mStaticConstellation; - } + nsCOMPtr parentWindow = GetScriptableParentOrNull(); + MOZ_RELEASE_ASSERT(!parentWindow || !mTabGroup || mTabGroup == Cast(parentWindow)->mTabGroup); NS_ASSERTION(!mNavigator, "Non-null mNavigator in outer window!"); @@ -3141,19 +3142,24 @@ nsGlobalWindow::SetOpenerWindow(nsPIDOMWindowOuter* aOpener, { FORWARD_TO_OUTER_VOID(SetOpenerWindow, (aOpener, aOriginalOpener)); + nsWeakPtr opener = do_GetWeakReference(aOpener); + if (opener == mOpener) { + return; + } + NS_ASSERTION(!aOriginalOpener || !mSetOpenerWindowCalled, "aOriginalOpener is true, but not first call to " "SetOpenerWindow!"); NS_ASSERTION(aOpener || !aOriginalOpener, "Shouldn't set mHadOriginalOpener if aOpener is null"); - mOpener = do_GetWeakReference(aOpener); + mOpener = opener.forget(); NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!"); - // Copy over the static constellation from our new opener - if (aOpener) { - mStaticConstellation = Cast(aOpener)->mStaticConstellation; - } + // Check that the js visible opener matches! + nsPIDOMWindowOuter* contentOpener = GetSanitizedOpener(aOpener); + MOZ_RELEASE_ASSERT(!contentOpener || !mTabGroup || + mTabGroup == Cast(contentOpener)->mTabGroup); if (aOriginalOpener) { MOZ_ASSERT(!mHadOriginalOpener, @@ -4726,26 +4732,13 @@ nsGlobalWindow::GetControllers(nsIControllers** aResult) } nsPIDOMWindowOuter* -nsGlobalWindow::GetOpenerWindowOuter() +nsGlobalWindow::GetSanitizedOpener(nsPIDOMWindowOuter* aOpener) { - MOZ_RELEASE_ASSERT(IsOuterWindow()); - - nsCOMPtr opener = do_QueryReferent(mOpener); - if (!opener) { + if (!aOpener) { return nullptr; } - nsGlobalWindow* win = nsGlobalWindow::Cast(opener); - - // First, check if we were called from a privileged chrome script - if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { - // Catch the case where we're chrome but the opener is not... - if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() && - win->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) { - return nullptr; - } - return opener; - } + nsGlobalWindow* win = nsGlobalWindow::Cast(aOpener); // First, ensure that we're not handing back a chrome window to content: if (win->IsChromeWindow()) { @@ -4755,7 +4748,7 @@ nsGlobalWindow::GetOpenerWindowOuter() // We don't want to reveal the opener if the opener is a mail window, // because opener can be used to spoof the contents of a message (bug 105050). // So, we look in the opener's root docshell to see if it's a mail window. - nsCOMPtr openerDocShell = opener->GetDocShell(); + nsCOMPtr openerDocShell = aOpener->GetDocShell(); if (openerDocShell) { nsCOMPtr openerRootItem; @@ -4765,7 +4758,7 @@ nsGlobalWindow::GetOpenerWindowOuter() uint32_t appType; nsresult rv = openerRootDocShell->GetAppType(&appType); if (NS_SUCCEEDED(rv) && appType != nsIDocShell::APP_TYPE_MAIL) { - return opener; + return aOpener; } } } @@ -4773,6 +4766,30 @@ nsGlobalWindow::GetOpenerWindowOuter() return nullptr; } +nsPIDOMWindowOuter* +nsGlobalWindow::GetOpenerWindowOuter() +{ + MOZ_RELEASE_ASSERT(IsOuterWindow()); + + nsCOMPtr opener = do_QueryReferent(mOpener); + + if (!opener) { + return nullptr; + } + + // First, check if we were called from a privileged chrome script + if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { + // Catch the case where we're chrome but the opener is not... + if (GetPrincipal() == nsContentUtils::GetSystemPrincipal() && + nsGlobalWindow::Cast(opener)->GetPrincipal() != nsContentUtils::GetSystemPrincipal()) { + return nullptr; + } + return opener; + } + + return GetSanitizedOpener(opener); +} + nsPIDOMWindowOuter* nsGlobalWindow::GetOpenerWindow(ErrorResult& aError) { @@ -4797,11 +4814,9 @@ nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle aRetval, already_AddRefed nsGlobalWindow::GetOpener() { - FORWARD_TO_INNER(GetOpener, (), nullptr); + FORWARD_TO_OUTER(GetOpener, (), nullptr); - ErrorResult dummy; - nsCOMPtr opener = GetOpenerWindow(dummy); - dummy.SuppressException(); + nsCOMPtr opener = GetOpenerWindowOuter(); return opener.forget(); } @@ -13627,6 +13642,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGlobalChromeWindow, NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserDOMWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGroupMessageManagers) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerForInitialContentBrowser) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -13640,6 +13656,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow, } tmp->DisconnectAndClearGroupMessageManagers(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroupMessageManagers) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerForInitialContentBrowser) NS_IMPL_CYCLE_COLLECTION_UNLINK_END // QueryInterface implementation for nsGlobalChromeWindow @@ -14110,6 +14127,24 @@ nsGlobalWindow::GetGroupMessageManager(const nsAString& aGroup, return messageManager; } +nsresult +nsGlobalChromeWindow::SetOpenerForInitialContentBrowser(mozIDOMWindowProxy* aOpenerWindow) +{ + MOZ_RELEASE_ASSERT(IsOuterWindow()); + MOZ_ASSERT(!mOpenerForInitialContentBrowser); + mOpenerForInitialContentBrowser = aOpenerWindow; + return NS_OK; +} + +nsresult +nsGlobalChromeWindow::TakeOpenerForInitialContentBrowser(mozIDOMWindowProxy** aOpenerWindow) +{ + MOZ_RELEASE_ASSERT(IsOuterWindow()); + // Intentionally forget our own member + mOpenerForInitialContentBrowser.forget(aOpenerWindow); + return NS_OK; +} + // nsGlobalModalWindow implementation // QueryInterface implementation for nsGlobalModalWindow @@ -14593,44 +14628,105 @@ nsGlobalWindow::CheckForDPIChange() } } -void -nsGlobalWindow::GetConstellation(nsACString& aConstellation) +mozilla::dom::TabGroup* +nsGlobalWindow::TabGroupOuter() { - FORWARD_TO_INNER_VOID(GetConstellation, (aConstellation)); + MOZ_RELEASE_ASSERT(IsOuterWindow()); + + // This method is valid both on inner and outer windows, which is a + // Outer windows lazily join TabGroups when requested. This is usually done + // because a document is getting its NodePrincipal, and asking for the + // TabGroup to determine its DocGroup. + if (!mTabGroup) { + // Get mOpener ourselves, instead of relying on GetOpenerWindowOuter, + // because that way we dodge the LegacyIsCallerChromeOrNativeCode() call + // which we want to return false. + nsCOMPtr piOpener = do_QueryReferent(mOpener); + nsGlobalWindow* opener = Cast(GetSanitizedOpener(piOpener)); + nsGlobalWindow* parent = Cast(GetScriptableParentOrNull()); + MOZ_ASSERT(!parent || !opener, "Only one of parent and opener may be provided"); + + mozilla::dom::TabGroup* toJoin = nullptr; + if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) { + toJoin = TabGroup::GetChromeTabGroup(); + } else if (opener) { + toJoin = opener->TabGroup(); + } else if (parent) { + toJoin = parent->TabGroup(); + } + mTabGroup = mozilla::dom::TabGroup::Join(AsOuter(), toJoin); + } + MOZ_ASSERT(mTabGroup); #ifdef DEBUG - RefPtr outer = GetOuterWindowInternal(); - MOZ_ASSERT(outer, "We should have an outer window"); - RefPtr top = outer->GetTopInternal(); - RefPtr opener = outer->GetOpener(); - MOZ_ASSERT(!top || (top->mStaticConstellation == - outer->mStaticConstellation)); - MOZ_ASSERT(!opener || (Cast(opener)->mStaticConstellation == - outer->mStaticConstellation)); + // Ensure that we don't recurse forever + if (!mIsValidatingTabGroup) { + mIsValidatingTabGroup = true; + // We only need to do this check if we aren't in the chrome tab group + if (GetDocShell()->ItemType() == nsIDocShellTreeItem::typeChrome) { + MOZ_ASSERT(mTabGroup == TabGroup::GetChromeTabGroup()); + } else { + // Sanity check that our tabgroup matches our opener or parent. + RefPtr parent = GetScriptableParentOrNull(); + MOZ_ASSERT_IF(parent, Cast(parent)->TabGroup() == mTabGroup); + nsCOMPtr piOpener = do_QueryReferent(mOpener); + nsGlobalWindow* opener = Cast(GetSanitizedOpener(piOpener)); + MOZ_ASSERT_IF(opener && opener != this, opener->TabGroup() == mTabGroup); + } + mIsValidatingTabGroup = false; + } #endif - if (mConstellation.IsVoid()) { - mConstellation.Truncate(); - // The dynamic constellation part comes from the eTLD+1 for the principal's URI. - nsCOMPtr principal = GetPrincipal(); - nsCOMPtr uri; - nsresult rv = principal->GetURI(getter_AddRefs(uri)); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr tldService = - do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); - if (tldService) { - rv = tldService->GetBaseDomain(uri, 0, mConstellation); - if (NS_FAILED(rv)) { - mConstellation.Truncate(); - } - } - } + return mTabGroup; +} - // Get the static constellation from the outer window object. - mConstellation.AppendPrintf("^%llu", GetOuterWindowInternal()->mStaticConstellation); +mozilla::dom::TabGroup* +nsGlobalWindow::TabGroupInner() +{ + MOZ_RELEASE_ASSERT(IsInnerWindow()); + + // If we don't have a TabGroup yet, try to get it from the outer window and + // cache it. + if (!mTabGroup) { + nsGlobalWindow* outer = GetOuterWindowInternal(); + // This will never be called without either an outer window, or a cached tab group. + // This is because of the following: + // * This method is only called on inner windows + // * This method is called as a document is attached to it's script global + // by the document + // * Inner windows are created in nsGlobalWindow::SetNewDocument, which + // immediately sets a document, which will call this method, causing + // the TabGroup to be cached. + MOZ_RELEASE_ASSERT(outer, "Inner window without outer window has no cached tab group!"); + mTabGroup = outer->TabGroup(); } + MOZ_ASSERT(mTabGroup); - aConstellation.Assign(mConstellation); +#ifdef DEBUG + nsGlobalWindow* outer = GetOuterWindowInternal(); + MOZ_ASSERT_IF(outer, outer->TabGroup() == mTabGroup); +#endif + + return mTabGroup; +} + +mozilla::dom::TabGroup* +nsGlobalWindow::TabGroup() +{ + if (IsInnerWindow()) { + return TabGroupInner(); + } + return TabGroupOuter(); +} + +mozilla::dom::DocGroup* +nsGlobalWindow::GetDocGroup() +{ + nsIDocument* doc = GetExtantDoc(); + if (doc) { + return doc->GetDocGroup(); + } + return nullptr; } nsGlobalWindow::TemporarilyDisableDialogs::TemporarilyDisableDialogs( diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 90a043f0ab76..1dd8612063de 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -86,6 +86,7 @@ class nsIJSID; class nsIScriptContext; class nsIScriptTimeoutHandler; class nsIWebBrowserChrome; +class mozIDOMWindowProxy; class nsDOMWindowList; class nsScreen; @@ -106,6 +107,7 @@ struct ChannelPixelLayout; class Console; class Crypto; class CustomElementRegistry; +class DocGroup; class External; class Function; class Gamepad; @@ -121,6 +123,7 @@ struct RequestInit; class RequestOrUSVString; class Selection; class SpeechSynthesis; +class TabGroup; class U2F; class VRDisplay; class VREventObserver; @@ -920,6 +923,9 @@ protected: // Initializes the mWasOffline member variable void InitWasOffline(); public: + nsPIDOMWindowOuter* + GetSanitizedOpener(nsPIDOMWindowOuter* aOpener); + nsPIDOMWindowOuter* GetOpenerWindow(mozilla::ErrorResult& aError); void GetOpener(JSContext* aCx, JS::MutableHandle aRetval, mozilla::ErrorResult& aError); @@ -1739,9 +1745,12 @@ private: // IsSecureContext() for the inner window that corresponds to aDocument. bool ComputeIsSecureContext(nsIDocument* aDocument); -public: + mozilla::dom::TabGroup* TabGroupInner(); + mozilla::dom::TabGroup* TabGroupOuter(); - void GetConstellation(nsACString& aConstellation); +public: + mozilla::dom::TabGroup* TabGroup(); + mozilla::dom::DocGroup* GetDocGroup(); protected: // These members are only used on outer window objects. Make sure @@ -1946,6 +1955,13 @@ protected: RefPtr mSpeechSynthesis; #endif + RefPtr mTabGroup; // Outer window only +#ifdef DEBUG + // This member is used in the debug only assertions in TabGroup() + // to catch cyclic parent/opener trees and not overflow the stack. + bool mIsValidatingTabGroup; +#endif + // This is the CC generation the last time we called CanSkip. uint32_t mCanSkipCCGeneration; @@ -1954,9 +1970,6 @@ protected: nsAutoPtr mVREventObserver; - uint64_t mStaticConstellation; // Only used on outer windows - nsCString mConstellation; // Only used on inner windows - friend class nsDOMScriptableHelper; friend class nsDOMWindowUtils; friend class mozilla::dom::PostMessageEvent; @@ -2053,6 +2066,7 @@ public: // The pointer being set indicates we've set the IsInFullscreenChange // flag on this pres shell. nsWeakPtr mFullscreenPresShell; + nsCOMPtr mOpenerForInitialContentBrowser; }; /* diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index e25e8b1b8236..6a4c2c4d4252 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -123,6 +123,7 @@ class BoxObject; class CDATASection; class Comment; struct CustomElementDefinition; +class DocGroup; class DocumentFragment; class DocumentTimeline; class DocumentType; @@ -2849,11 +2850,13 @@ public: mozilla::dom::DOMIntersectionObserver* aObserver) = 0; virtual void RemoveIntersectionObserver( mozilla::dom::DOMIntersectionObserver* aObserver) = 0; - + virtual void UpdateIntersectionObservations() = 0; virtual void ScheduleIntersectionObserverNotification() = 0; virtual void NotifyIntersectionObservers() = 0; + mozilla::dom::DocGroup* GetDocGroup(); + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { @@ -3310,6 +3313,8 @@ protected: bool mUserHasInteracted; mozilla::TimeStamp mPageUnloadingEventTimeStamp; + + RefPtr mDocGroup; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index ea362167489e..3b78af2bc03e 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -1258,6 +1258,12 @@ nsObjectLoadingContent::GetParentApplication(mozIApplication** aApplication) return NS_OK; } +void +nsObjectLoadingContent::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, mozilla::ErrorResult& aRv) +{ + aRv.Throw(NS_ERROR_FAILURE); +} + NS_IMETHODIMP nsObjectLoadingContent::SetIsPrerendered() { diff --git a/dom/base/nsObjectLoadingContent.h b/dom/base/nsObjectLoadingContent.h index f6a4b10bce50..cfd3bf381b84 100644 --- a/dom/base/nsObjectLoadingContent.h +++ b/dom/base/nsObjectLoadingContent.h @@ -256,6 +256,8 @@ class nsObjectLoadingContent : public nsImageLoadingContent return mRewrittenYoutubeEmbed; } + void PresetOpenerWindow(mozIDOMWindowProxy* aOpenerWindow, mozilla::ErrorResult& aRv); + protected: /** * Begins loading the object when called diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp index 1edf125d6b9b..63f49b118aaf 100644 --- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -283,6 +283,10 @@ BrowserElementParent::OpenWindowInProcess(nsPIDOMWindowOuter* aOpenerWindow, aURI->GetSpec(spec); } + ErrorResult res; + popupFrameElement->PresetOpenerWindow(aOpenerWindow, res); + MOZ_ASSERT(!res.Failed()); + OpenWindowResult opened = DispatchOpenWindowEvent(openerFrameElement, popupFrameElement, NS_ConvertUTF8toUTF16(spec), diff --git a/dom/html/nsGenericHTMLFrameElement.cpp b/dom/html/nsGenericHTMLFrameElement.cpp index 3132f544142a..763c98ce0a05 100644 --- a/dom/html/nsGenericHTMLFrameElement.cpp +++ b/dom/html/nsGenericHTMLFrameElement.cpp @@ -36,6 +36,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericHTMLFrameElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpenerWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAudioChannels) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -47,6 +48,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement, } NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpenerWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI) NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAudioChannels) NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -153,6 +155,15 @@ nsGenericHTMLFrameElement::EnsureFrameLoader() if (mIsPrerendered) { mFrameLoader->SetIsPrerendered(); } + if (mOpenerWindow) { + nsCOMPtr docShell; + mFrameLoader->GetDocShell(getter_AddRefs(docShell)); + if (docShell) { + nsCOMPtr outerWindow = docShell->GetWindow(); + outerWindow->SetOpenerWindow(nsPIDOMWindowOuter::From(mOpenerWindow), true); + mOpenerWindow = nullptr; + } + } } nsresult @@ -212,6 +223,13 @@ nsGenericHTMLFrameElement::GetParentApplication(mozIApplication** aApplication) return NS_OK; } +void +nsGenericHTMLFrameElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv) +{ + MOZ_ASSERT(!mFrameLoader); + mOpenerWindow = nsPIDOMWindowOuter::From(aWindow); +} + void nsGenericHTMLFrameElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner, ErrorResult& rv) diff --git a/dom/html/nsGenericHTMLFrameElement.h b/dom/html/nsGenericHTMLFrameElement.h index 03b3d78cd0b1..5e23fcf4bb63 100644 --- a/dom/html/nsGenericHTMLFrameElement.h +++ b/dom/html/nsGenericHTMLFrameElement.h @@ -83,6 +83,9 @@ public: void SwapFrameLoaders(RefPtr& aOtherLoader, mozilla::ErrorResult& rv); + void PresetOpenerWindow(mozIDOMWindowProxy* aOpenerWindow, + mozilla::ErrorResult& aRv); + static bool BrowserFramesEnabled(); /** @@ -107,6 +110,7 @@ protected: already_AddRefed GetContentWindow(); RefPtr mFrameLoader; + nsCOMPtr mOpenerWindow; /** * True when the element is created by the parser using the diff --git a/dom/interfaces/base/nsIDOMChromeWindow.idl b/dom/interfaces/base/nsIDOMChromeWindow.idl index 9118a0f801f0..600aa46eb6e2 100644 --- a/dom/interfaces/base/nsIDOMChromeWindow.idl +++ b/dom/interfaces/base/nsIDOMChromeWindow.idl @@ -9,6 +9,7 @@ interface nsIBrowserDOMWindow; interface nsIDOMElement; interface nsIDOMEvent; interface nsIMessageBroadcaster; +interface mozIDOMWindowProxy; [scriptable, uuid(78bdcb41-1efa-409f-aaba-70842213f80f)] interface nsIDOMChromeWindow : nsISupports @@ -63,4 +64,18 @@ interface nsIDOMChromeWindow : nsISupports * doesn't support this. */ void beginWindowMove(in nsIDOMEvent mouseDownEvent, [optional] in nsIDOMElement panel); + + /** + * These methods provide a way to specify the opener value for the content in + * the window before the content itself is created. This is important in order + * to set the DocGroup of a document, as the opener must be set before the + * document is created. + * + * SetOpenerForInitialContentBrowser is used to set which opener will be used, + * and TakeOpenerForInitialContentBrowser is used by nsXULElement in order to + * take the value set earlier, and null out the value in the + * nsIDOMChromeWindow. + */ + void setOpenerForInitialContentBrowser(in mozIDOMWindowProxy aOpener); + mozIDOMWindowProxy takeOpenerForInitialContentBrowser(); }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 60cf1831e153..f86b63ec0d56 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -826,6 +826,17 @@ ContentChild::ProvideWindowCommon(TabChild* aTabOpener, aTabOpener->mDefaultScale); } + // Set the opener window for this window before we start loading the document + // inside of it. We have to do this before loading the remote scripts, because + // they can poke at the document and cause the nsDocument to be created before + // the openerwindow + nsCOMPtr windowProxy = do_GetInterface(newChild->WebNavigation()); + if (windowProxy && aParent) { + nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy); + nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent); + outer->SetOpenerWindow(parent, *aWindowIsNew); + } + // Unfortunately we don't get a window unless we've shown the frame. That's // pretty bogus; see bug 763602. newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame, diff --git a/dom/webidl/XULElement.webidl b/dom/webidl/XULElement.webidl index f121fa805101..28c79b9a7bac 100644 --- a/dom/webidl/XULElement.webidl +++ b/dom/webidl/XULElement.webidl @@ -125,6 +125,9 @@ interface MozFrameLoaderOwner { [ChromeOnly] void setIsPrerendered(); + [ChromeOnly, Throws] + void presetOpenerWindow(WindowProxy? window); + [ChromeOnly, Throws] void swapFrameLoaders(XULElement aOtherLoaderOwner); diff --git a/dom/xml/XMLDocument.cpp b/dom/xml/XMLDocument.cpp index ba7b46893ee2..b001857a8346 100644 --- a/dom/xml/XMLDocument.cpp +++ b/dom/xml/XMLDocument.cpp @@ -128,12 +128,6 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, return rv; } - if (nsCOMPtr sgo = do_QueryInterface(aEventObject)) { - d->SetScriptHandlingObject(sgo); - } else if (aEventObject){ - d->SetScopeObject(aEventObject); - } - if (isHTML) { nsCOMPtr htmlDoc = do_QueryInterface(d); NS_ASSERTION(htmlDoc, "HTML Document doesn't implement nsIHTMLDocument?"); @@ -147,6 +141,14 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, doc->SetPrincipal(aPrincipal); doc->SetBaseURI(aBaseURI); + // We need to set the script handling object after we set the principal such + // that the doc group is assigned correctly. + if (nsCOMPtr sgo = do_QueryInterface(aEventObject)) { + d->SetScriptHandlingObject(sgo); + } else if (aEventObject){ + d->SetScopeObject(aEventObject); + } + // XMLDocuments and documents "created in memory" get to be UTF-8 by default, // unlike the legacy HTML mess doc->SetDocumentCharacterSet(NS_LITERAL_CSTRING("UTF-8")); diff --git a/dom/xslt/xslt/txMozillaXMLOutput.cpp b/dom/xslt/xslt/txMozillaXMLOutput.cpp index 02947f896289..5f8295e927bf 100644 --- a/dom/xslt/xslt/txMozillaXMLOutput.cpp +++ b/dom/xslt/xslt/txMozillaXMLOutput.cpp @@ -803,7 +803,6 @@ txMozillaXMLOutput::createResultDocument(const nsSubstring& aName, int32_t aNsID nsIScriptGlobalObject* sgo = source->GetScriptHandlingObject(hasHadScriptObject); NS_ENSURE_STATE(sgo || !hasHadScriptObject); - mDocument->SetScriptHandlingObject(sgo); mCurrentNode = mDocument; mNodeInfoManager = mDocument->NodeInfoManager(); @@ -811,6 +810,10 @@ txMozillaXMLOutput::createResultDocument(const nsSubstring& aName, int32_t aNsID // Reset and set up the document URIUtils::ResetWithSource(mDocument, aSourceDocument); + // Make sure we set the script handling object after resetting with the + // source, so that we have the right principal. + mDocument->SetScriptHandlingObject(sgo); + // Set the charset if (!mOutputFormat.mEncoding.IsEmpty()) { nsAutoCString canonicalCharset; diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 6c0a9a3d328c..0b4233ee1517 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -95,6 +95,7 @@ #include "nsIController.h" #include "nsQueryObject.h" #include +#include "nsIDOMChromeWindow.h" // The XUL doc interface #include "nsIDOMXULDocument.h" @@ -191,6 +192,8 @@ nsXULElement::nsXULSlots::Traverse(nsCycleCollectionTraversalCallback &cb) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mFrameLoader"); cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIFrameLoader*, mFrameLoader)); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mOpener"); + cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(mozIDOMWindowProxy*, mOpener)); } nsINode::nsSlots* @@ -1585,6 +1588,30 @@ nsXULElement::LoadSrc() slots->mFrameLoader = nsFrameLoader::Create(this, false); NS_ENSURE_TRUE(slots->mFrameLoader, NS_OK); + nsCOMPtr opener; + if (slots->mOpener) { + opener = nsPIDOMWindowOuter::From(slots->mOpener); + slots->mOpener = nullptr; + } else { + // If we are a content-primary xul-browser, we want to take the opener property! + nsCOMPtr chromeWindow = do_QueryInterface(OwnerDoc()->GetWindow()); + if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + NS_LITERAL_STRING("content-primary"), eIgnoreCase) && + chromeWindow) { + nsCOMPtr wp; + chromeWindow->TakeOpenerForInitialContentBrowser(getter_AddRefs(wp)); + opener = nsPIDOMWindowOuter::From(wp); + } + } + + if (opener) { + nsCOMPtr docShell; + nsresult rv = slots->mFrameLoader->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr outerWindow = docShell->GetWindow(); + outerWindow->SetOpenerWindow(opener, true); + } + (new AsyncEventDispatcher(this, NS_LITERAL_STRING("XULFrameLoaderCreated"), /* aBubbles */ true))->RunDOMEventWhenSafe(); @@ -1628,6 +1655,15 @@ nsXULElement::GetParentApplication(mozIApplication** aApplication) return NS_OK; } +void +nsXULElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv) +{ + nsXULSlots* slots = static_cast(Slots()); + MOZ_ASSERT(!slots->mFrameLoader, "Cannot SetOpenerWindow when a frame loader is present"); + + slots->mOpener = aWindow; +} + nsresult nsXULElement::SetIsPrerendered() { diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h index 048c2b0d4b5a..594021de28bd 100644 --- a/dom/xul/nsXULElement.h +++ b/dom/xul/nsXULElement.h @@ -412,6 +412,7 @@ public: nsresult GetFrameLoaderXPCOM(nsIFrameLoader** aFrameLoader); nsresult GetParentApplication(mozIApplication** aApplication); + void PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv); nsresult SetIsPrerendered(); virtual void RecompileScriptEventListeners() override; @@ -616,6 +617,7 @@ protected: void Traverse(nsCycleCollectionTraversalCallback &cb); RefPtr mFrameLoader; + nsCOMPtr mOpener; }; virtual nsINode::nsSlots* CreateSlots() override; diff --git a/embedding/components/windowwatcher/nsWindowWatcher.cpp b/embedding/components/windowwatcher/nsWindowWatcher.cpp index 24ba66d1e15e..2bed7c9ef5eb 100644 --- a/embedding/components/windowwatcher/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp @@ -65,8 +65,10 @@ #include "mozilla/dom/DOMStorage.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/TabParent.h" +#include "mozilla/dom/DocGroup.h" #include "nsIXULWindow.h" #include "nsIXULBrowserWindow.h" +#include "nsGlobalWindow.h" #ifdef USEWEAKREFS #include "nsIWeakReference.h" @@ -483,6 +485,7 @@ nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures, uint32_t aChromeFlags, uint32_t aContextFlags, nsITabParent* aOpeningTabParent, + mozIDOMWindowProxy* aOpener, nsIWebBrowserChrome** aResult) { nsCOMPtr windowCreator2(do_QueryInterface(mWindowCreator)); @@ -501,7 +504,7 @@ nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures, nsCOMPtr newWindowChrome; nsresult rv = windowCreator2->CreateChromeWindow2(aParentChrome, aChromeFlags, aContextFlags, - aOpeningTabParent, &cancel, + aOpeningTabParent, aOpener, &cancel, getter_AddRefs(newWindowChrome)); if (NS_SUCCEEDED(rv) && cancel) { @@ -621,7 +624,7 @@ nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent, nsCOMPtr newWindowChrome; CreateChromeWindow(aFeatures, parentChrome, chromeFlags, contextFlags, - aOpeningTabParent, getter_AddRefs(newWindowChrome)); + aOpeningTabParent, nullptr, getter_AddRefs(newWindowChrome)); if (NS_WARN_IF(!newWindowChrome)) { return NS_ERROR_UNEXPECTED; @@ -996,7 +999,7 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, } rv = CreateChromeWindow(features, parentChrome, chromeFlags, contextFlags, - nullptr, getter_AddRefs(newChrome)); + nullptr, aParent, getter_AddRefs(newChrome)); } else { rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags, @@ -1009,7 +1012,8 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, nsCOMPtr xulBrowserWin; xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin)); if (xulBrowserWin) { - xulBrowserWin->ForceInitialBrowserNonRemote(); + nsPIDOMWindowOuter* openerWindow = aForceNoOpener ? nullptr : parentWindow; + xulBrowserWin->ForceInitialBrowserNonRemote(openerWindow); } } /* It might be a chrome nsXULWindow, in which case it won't have @@ -2182,28 +2186,30 @@ nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem, *aOpenedWindow = 0; nsCOMPtr piOpenedWindow = aOpenedItem->GetWindow(); if (piOpenedWindow) { - if (aParent) { - if (!aForceNoOpener) { - piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit - } + if (!aForceNoOpener) { + piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit + } else if (aParent) { + MOZ_ASSERT(nsGlobalWindow::Cast(piOpenedWindow)->TabGroup() != + nsGlobalWindow::Cast(aParent)->TabGroup(), + "If we're forcing no opener, they should be in different tab groups"); + } - if (aWindowIsNew) { + if (aWindowIsNew) { #ifdef DEBUG - // Assert that we're not loading things right now. If we are, when - // that load completes it will clobber whatever principals we set up - // on this new window! - nsCOMPtr docloader = do_QueryInterface(aOpenedItem); - NS_ASSERTION(docloader, "How can we not have a docloader here?"); + // Assert that we're not loading things right now. If we are, when + // that load completes it will clobber whatever principals we set up + // on this new window! + nsCOMPtr docloader = do_QueryInterface(aOpenedItem); + NS_ASSERTION(docloader, "How can we not have a docloader here?"); - nsCOMPtr chan; - docloader->GetDocumentChannel(getter_AddRefs(chan)); - NS_ASSERTION(!chan, "Why is there a document channel?"); + nsCOMPtr chan; + docloader->GetDocumentChannel(getter_AddRefs(chan)); + NS_ASSERTION(!chan, "Why is there a document channel?"); #endif - nsCOMPtr doc = piOpenedWindow->GetExtantDoc(); - if (doc) { - doc->SetIsInitialDocument(true); - } + nsCOMPtr doc = piOpenedWindow->GetExtantDoc(); + if (doc) { + doc->SetIsInitialDocument(true); } } rv = CallQueryInterface(piOpenedWindow, aOpenedWindow); diff --git a/embedding/components/windowwatcher/nsWindowWatcher.h b/embedding/components/windowwatcher/nsWindowWatcher.h index de79f2c09485..f2728bc161fc 100644 --- a/embedding/components/windowwatcher/nsWindowWatcher.h +++ b/embedding/components/windowwatcher/nsWindowWatcher.h @@ -130,6 +130,7 @@ private: uint32_t aChromeFlags, uint32_t aContextFlags, nsITabParent* aOpeningTabParent, + mozIDOMWindowProxy* aOpener, nsIWebBrowserChrome** aResult); void MaybeDisablePersistence(const nsACString& aFeatures, diff --git a/embedding/nsIWindowCreator2.idl b/embedding/nsIWindowCreator2.idl index eafeea8fb622..c07a794a0ae4 100644 --- a/embedding/nsIWindowCreator2.idl +++ b/embedding/nsIWindowCreator2.idl @@ -20,6 +20,7 @@ interface nsITabParent; interface nsIURI; interface nsIWebBrowserChrome; +interface mozIDOMWindowProxy; [scriptable, uuid(b6c44689-f97e-4f32-a723-29eeddfbdc53)] @@ -42,6 +43,8 @@ interface nsIWindowCreator2 : nsIWindowCreator { @param contextFlags Flags about the context of the window being created. @param aOpeningTab The TabParent that is trying to open this new chrome window. Can be nullptr. + @param aOpener The window which is trying to open this new chrome window. + Can be nullptr @param cancel Return |true| to reject window creation. If true the implementation has determined the window should not be created at all. The caller should not default @@ -52,6 +55,7 @@ interface nsIWindowCreator2 : nsIWindowCreator { in uint32_t chromeFlags, in uint32_t contextFlags, in nsITabParent aOpeningTab, + in mozIDOMWindowProxy aOpener, out boolean cancel); /** diff --git a/testing/talos/talos/pageloader/chrome/pageloader.js b/testing/talos/talos/pageloader/chrome/pageloader.js index 6d3f8592c4eb..fbe67b4941d2 100644 --- a/testing/talos/talos/pageloader/chrome/pageloader.js +++ b/testing/talos/talos/pageloader/chrome/pageloader.js @@ -244,7 +244,7 @@ function plInit() { // to this reinitialization on the switch. if (browserWindow.gMultiProcessBrowser) { if (!firstPageCanLoadAsRemote()) - browserWindow.XULBrowserWindow.forceInitialBrowserNonRemote(); + browserWindow.XULBrowserWindow.forceInitialBrowserNonRemote(null); // Implicit else: initial browser in e10s is remote by default. } diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp index 477478e49706..85d5afdf97c8 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -611,7 +611,7 @@ nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent, nsIWebBrowserChrome **_retval) { bool cancel; - return CreateChromeWindow2(aParent, aChromeFlags, 0, nullptr, &cancel, _retval); + return CreateChromeWindow2(aParent, aChromeFlags, 0, nullptr, nullptr, &cancel, _retval); } @@ -635,6 +635,7 @@ nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent, uint32_t aChromeFlags, uint32_t aContextFlags, nsITabParent *aOpeningTab, + mozIDOMWindowProxy* aOpener, bool *aCancel, nsIWebBrowserChrome **_retval) { @@ -654,7 +655,7 @@ nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent, NS_ASSERTION(xulParent, "window created using non-XUL parent. that's unexpected, but may work."); if (xulParent) - xulParent->CreateNewWindow(aChromeFlags, aOpeningTab, getter_AddRefs(newWindow)); + xulParent->CreateNewWindow(aChromeFlags, aOpeningTab, aOpener, getter_AddRefs(newWindow)); // And if it fails, don't try again without a parent. It could fail // intentionally (bug 115969). } else { // try using basic methods: @@ -671,7 +672,7 @@ nsAppStartup::CreateChromeWindow2(nsIWebBrowserChrome *aParent, appShell->CreateTopLevelWindow(0, 0, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT, nsIAppShellService::SIZE_TO_CONTENT, - aOpeningTab, + aOpeningTab, aOpener, getter_AddRefs(newWindow)); } diff --git a/xpfe/appshell/nsAppShellService.cpp b/xpfe/appshell/nsAppShellService.cpp index 955e6d26d6dd..7e1ddf16c112 100644 --- a/xpfe/appshell/nsAppShellService.cpp +++ b/xpfe/appshell/nsAppShellService.cpp @@ -135,7 +135,7 @@ nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) if (!aIsPrivate) { rv = JustCreateTopWindow(nullptr, url, chromeMask, initialWidth, initialHeight, - true, nullptr, getter_AddRefs(newWindow)); + true, nullptr, nullptr, getter_AddRefs(newWindow)); NS_ENSURE_SUCCESS(rv, rv); mHiddenWindow.swap(newWindow); @@ -145,7 +145,7 @@ nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) rv = JustCreateTopWindow(nullptr, url, chromeMask, initialWidth, initialHeight, - true, nullptr, getter_AddRefs(newWindow)); + true, nullptr, nullptr, getter_AddRefs(newWindow)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr docShell; @@ -190,6 +190,7 @@ nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent, int32_t aInitialWidth, int32_t aInitialHeight, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, nsIXULWindow **aResult) { @@ -197,12 +198,12 @@ nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent, StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW); - nsWebShellWindow *newWindow = nullptr; + RefPtr newWindow; rv = JustCreateTopWindow(aParent, aUrl, aChromeMask, aInitialWidth, aInitialHeight, - false, aOpeningTab, &newWindow); // addrefs - - *aResult = newWindow; // transfer ref + false, aOpeningTab, aOpenerWindow, + getter_AddRefs(newWindow)); + newWindow.forget(aResult); if (NS_SUCCEEDED(rv)) { // the addref resulting from this is the owning addref for this window @@ -626,6 +627,7 @@ nsAppShellService::JustCreateTopWindow(nsIXULWindow *aParent, int32_t aInitialHeight, bool aIsHiddenWindow, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, nsWebShellWindow **aResult) { *aResult = nullptr; @@ -739,7 +741,8 @@ nsAppShellService::JustCreateTopWindow(nsIXULWindow *aParent, nsresult rv = window->Initialize(parent, center ? aParent : nullptr, aUrl, aInitialWidth, aInitialHeight, - aIsHiddenWindow, aOpeningTab, widgetInitData); + aIsHiddenWindow, aOpeningTab, + aOpenerWindow, widgetInitData); NS_ENSURE_SUCCESS(rv, rv); diff --git a/xpfe/appshell/nsAppShellService.h b/xpfe/appshell/nsAppShellService.h index 7977adab837f..feb7b9947ca0 100644 --- a/xpfe/appshell/nsAppShellService.h +++ b/xpfe/appshell/nsAppShellService.h @@ -42,6 +42,7 @@ protected: int32_t aInitialWidth, int32_t aInitialHeight, bool aIsHiddenWindow, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, nsWebShellWindow **aResult); uint32_t CalculateWindowZLevel(nsIXULWindow *aParent, uint32_t aChromeMask); diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp index 3f68f61e6ff6..21ef5562b759 100644 --- a/xpfe/appshell/nsContentTreeOwner.cpp +++ b/xpfe/appshell/nsContentTreeOwner.cpp @@ -994,6 +994,9 @@ nsContentTreeOwner::ProvideWindow(mozIDOMWindowProxy* aParent, // Get a new rendering area from the browserDOMWin. We don't want // to be starting any loads here, so get it with a null URI. + // + // This method handles setting the opener for us, so we don't need to set it + // ourselves. return browserDOMWin->OpenURI(nullptr, aParent, openLocation, nsIBrowserDOMWindow::OPEN_NEW, aReturn); } diff --git a/xpfe/appshell/nsIAppShellService.idl b/xpfe/appshell/nsIAppShellService.idl index ef72a5995038..e7d96a5d18eb 100644 --- a/xpfe/appshell/nsIAppShellService.idl +++ b/xpfe/appshell/nsIAppShellService.idl @@ -38,6 +38,8 @@ interface nsIAppShellService : nsISupports * @param aInitialHeight like aInitialWidth, but subtly different. * @param aOpeningTab The TabParent that requested that this window be opened. * Can be left null. + * @param aOpenerWindow The Window Proxy which requested that this window be opened. + * Can be left null. */ const long SIZE_TO_CONTENT = -1; nsIXULWindow createTopLevelWindow(in nsIXULWindow aParent, @@ -45,7 +47,8 @@ interface nsIAppShellService : nsISupports in uint32_t aChromeMask, in long aInitialWidth, in long aInitialHeight, - in nsITabParent aOpeningTab); + in nsITabParent aOpeningTab, + in mozIDOMWindowProxy aOpenerWindow); /** * This is the constructor for creating an invisible DocShell. diff --git a/xpfe/appshell/nsIXULBrowserWindow.idl b/xpfe/appshell/nsIXULBrowserWindow.idl index 44231a384a1e..40f1898c8f4d 100644 --- a/xpfe/appshell/nsIXULBrowserWindow.idl +++ b/xpfe/appshell/nsIXULBrowserWindow.idl @@ -13,6 +13,7 @@ interface nsIDOMElement; interface nsIInputStream; interface nsIDocShell; interface nsITabParent; +interface mozIDOMWindowProxy; /** * The nsIXULBrowserWindow supplies the methods that may be called from the @@ -48,7 +49,7 @@ interface nsIXULBrowserWindow : nsISupports * */ nsITabParent forceInitialBrowserRemote(); - void forceInitialBrowserNonRemote(); + void forceInitialBrowserNonRemote(in mozIDOMWindowProxy openerWindow); /** * Determines whether a load should continue. diff --git a/xpfe/appshell/nsIXULWindow.idl b/xpfe/appshell/nsIXULWindow.idl index a15345f6bbb4..8db12adb6b72 100644 --- a/xpfe/appshell/nsIXULWindow.idl +++ b/xpfe/appshell/nsIXULWindow.idl @@ -17,6 +17,7 @@ interface nsIDocShell; interface nsIDocShellTreeItem; interface nsIXULBrowserWindow; interface nsITabParent; +interface mozIDOMWindowProxy; [scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)] interface nsIXULWindow : nsISupports @@ -120,10 +121,12 @@ interface nsIXULWindow : nsISupports * @param aChromeFlags see nsIWebBrowserChrome * @param aOpeningTab the TabParent that requested this new window be opened. * Can be left null. + * @param aOpener The window which is requesting that this new window be opened. * @return the newly minted window */ nsIXULWindow createNewWindow(in int32_t aChromeFlags, - in nsITabParent aOpeningTab); + in nsITabParent aOpeningTab, + in mozIDOMWindowProxy aOpener); attribute nsIXULBrowserWindow XULBrowserWindow; diff --git a/xpfe/appshell/nsWebShellWindow.cpp b/xpfe/appshell/nsWebShellWindow.cpp index 600377d43bfc..ed6bbc50c51b 100644 --- a/xpfe/appshell/nsWebShellWindow.cpp +++ b/xpfe/appshell/nsWebShellWindow.cpp @@ -113,6 +113,7 @@ nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent, int32_t aInitialHeight, bool aIsHiddenWindow, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, nsWidgetInitData& widgetInitData) { nsresult rv; @@ -209,6 +210,12 @@ nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent, webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK); } + if (aOpenerWindow) { + nsPIDOMWindowOuter* window = mDocShell->GetWindow(); + MOZ_ASSERT(window); + window->SetOpenerWindow(nsPIDOMWindowOuter::From(aOpenerWindow), true); + } + // Eagerly create an about:blank content viewer with the right principal here, // rather than letting it happening in the upcoming call to // SetInitialPrincipalToSubject. This avoids creating the about:blank document diff --git a/xpfe/appshell/nsWebShellWindow.h b/xpfe/appshell/nsWebShellWindow.h index 3d531bdb4e29..cf2b0da9ceba 100644 --- a/xpfe/appshell/nsWebShellWindow.h +++ b/xpfe/appshell/nsWebShellWindow.h @@ -39,6 +39,7 @@ public: int32_t aInitialWidth, int32_t aInitialHeight, bool aIsHiddenWindow, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWIndow, nsWidgetInitData& widgetInitData); nsresult Toolbar(); diff --git a/xpfe/appshell/nsXULWindow.cpp b/xpfe/appshell/nsXULWindow.cpp index c174d784650f..ae0cbdedb0ae 100644 --- a/xpfe/appshell/nsXULWindow.cpp +++ b/xpfe/appshell/nsXULWindow.cpp @@ -1920,29 +1920,30 @@ NS_IMETHODIMP nsXULWindow::ExitModalLoop(nsresult aStatus) // top-level function to create a new window NS_IMETHODIMP nsXULWindow::CreateNewWindow(int32_t aChromeFlags, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, nsIXULWindow **_retval) { NS_ENSURE_ARG_POINTER(_retval); if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) - return CreateNewChromeWindow(aChromeFlags, aOpeningTab, _retval); - return CreateNewContentWindow(aChromeFlags, aOpeningTab, _retval); + return CreateNewChromeWindow(aChromeFlags, aOpeningTab, aOpener, _retval); + return CreateNewContentWindow(aChromeFlags, aOpeningTab, aOpener, _retval); } NS_IMETHODIMP nsXULWindow::CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, nsIXULWindow **_retval) { nsCOMPtr appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); // Just do a normal create of a window and return. - nsCOMPtr newWindow; appShell->CreateTopLevelWindow(this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT, nsIAppShellService::SIZE_TO_CONTENT, - aOpeningTab, + aOpeningTab, aOpener, getter_AddRefs(newWindow)); NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE); @@ -1955,6 +1956,7 @@ NS_IMETHODIMP nsXULWindow::CreateNewChromeWindow(int32_t aChromeFlags, NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags, nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, nsIXULWindow **_retval) { nsCOMPtr appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); @@ -1985,9 +1987,12 @@ NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags, nsCOMPtr newWindow; { AutoNoJSAPI nojsapi; + // We actually want this toplevel window which we are creating to have a + // null opener, as we will be creating the content xul:browser window inside + // of it, so we pass nullptr as our aOpener. appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480, - aOpeningTab, + aOpeningTab, nullptr, getter_AddRefs(newWindow)); NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE); } @@ -1997,6 +2002,17 @@ NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags, (static_cast (newWindow)); + if (aOpener) { + nsCOMPtr docShell; + xulWin->GetDocShell(getter_AddRefs(docShell)); + MOZ_ASSERT(docShell); + nsCOMPtr chromeWindow = + do_QueryInterface(docShell->GetWindow()); + MOZ_ASSERT(chromeWindow); + + chromeWindow->SetOpenerForInitialContentBrowser(aOpener); + } + xulWin->LockUntilChromeLoad(); { diff --git a/xpfe/appshell/nsXULWindow.h b/xpfe/appshell/nsXULWindow.h index 2cd887eb5812..eb059c939ee3 100644 --- a/xpfe/appshell/nsXULWindow.h +++ b/xpfe/appshell/nsXULWindow.h @@ -121,8 +121,8 @@ protected: NS_IMETHOD SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, int32_t aCY); NS_IMETHOD ExitModalLoop(nsresult aStatus); - NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, nsIXULWindow **_retval); - NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, nsIXULWindow **_retval); + NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval); + NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval); NS_IMETHOD GetHasPrimaryContent(bool* aResult); void EnableParent(bool aEnable);