From 1a8e54667c7cd7ebad184b8a0034db877ecfe2d0 Mon Sep 17 00:00:00 2001 From: "jst%mozilla.jstenback.com" Date: Tue, 11 Jan 2005 19:36:40 +0000 Subject: [PATCH] Fixing bug 103638 (and bug 273699). Targets with same name in different windows open in the wrong window. Patch by bzbarsky@mit.edu and myself, r=danm.moz@gmail.com, bzbarsky@mit.edu, sr=dveditz@cruzio.com, a=asa@mozilla.org --- docshell/base/nsDocShell.cpp | 545 +++++++++++------- docshell/base/nsDocShell.h | 3 - docshell/base/nsIDocShellTreeItem.idl | 8 +- docshell/base/nsIDocShellTreeNode.idl | 11 +- docshell/base/nsIDocShellTreeOwner.idl | 8 +- dom/src/base/nsDOMClassInfo.cpp | 2 +- dom/src/base/nsDOMWindowList.cpp | 2 +- dom/src/base/nsGlobalWindow.cpp | 127 ++-- dom/src/base/nsGlobalWindow.h | 2 + .../webBrowser/nsDocShellTreeOwner.cpp | 17 +- .../browser/webBrowser/nsDocShellTreeOwner.h | 4 +- embedding/browser/webBrowser/nsWebBrowser.cpp | 6 +- .../windowwatcher/public/nsIWindowWatcher.idl | 4 + .../windowwatcher/src/nsWWJSUtils.cpp | 9 + .../windowwatcher/src/nsWindowWatcher.cpp | 93 ++- .../windowwatcher/src/nsWindowWatcher.h | 3 +- mailnews/base/src/nsMessenger.cpp | 2 +- mailnews/base/src/nsMsgPrintEngine.cpp | 5 +- mailnews/base/src/nsMsgWindow.cpp | 5 +- webshell/tests/viewer/nsWebBrowserChrome.cpp | 32 - xpfe/appshell/src/nsChromeTreeOwner.cpp | 13 +- xpfe/appshell/src/nsContentTreeOwner.cpp | 13 +- xpfe/appshell/src/nsWebShellWindow.cpp | 2 +- 23 files changed, 541 insertions(+), 375 deletions(-) diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index a5959fe190e..20e5fbb0b0f 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -185,6 +185,11 @@ static PRInt32 gDocShellCount = 0; // Global reference to the URI fixup service. nsIURIFixup *nsDocShell::sURIFixup = 0; +// True means we validate window targets to prevent frameset +// spoofing. Initialize this to a non-bolean value so we know to check +// the pref on the creation of the first docshell. +static PRBool gValidateOrigin = (PRBool)0xffffffff; + // Hint for native dispatch of plevents on how long to delay after // all documents have loaded in milliseconds before favoring normal // native event dispatch priorites over performance @@ -245,7 +250,6 @@ nsDocShell::nsDocShell(): mEODForCurrentDocument(PR_FALSE), mURIResultedInDocument(PR_FALSE), mIsBeingDestroyed(PR_FALSE), - mValidateOrigin(PR_TRUE), // validate frame origins by default mIsExecutingOnLoadHandler(PR_FALSE), mIsPrintingOrPP(PR_FALSE), mAppType(nsIDocShell::APP_TYPE_UNKNOWN), @@ -375,15 +379,9 @@ NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink) NS_SUCCEEDED(EnsureScriptEnvironment())) { *aSink = mScriptGlobal; } - else if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) && - NS_SUCCEEDED(EnsureScriptEnvironment())) { - return mScriptGlobal->QueryInterface(aIID, aSink); - } - else if (aIID.Equals(NS_GET_IID(nsPIDOMWindow)) && - NS_SUCCEEDED(EnsureScriptEnvironment())) { - return mScriptGlobal->QueryInterface(aIID, aSink); - } - else if (aIID.Equals(NS_GET_IID(nsIDOMWindow)) && + else if ((aIID.Equals(NS_GET_IID(nsIDOMWindowInternal)) || + aIID.Equals(NS_GET_IID(nsPIDOMWindow)) || + aIID.Equals(NS_GET_IID(nsIDOMWindow))) && NS_SUCCEEDED(EnsureScriptEnvironment())) { return mScriptGlobal->QueryInterface(aIID, aSink); } @@ -863,8 +861,9 @@ nsDocShell::FireUnloadNotification() // to the host PL_strcmp, it accepts a subdomain (nsHTMLDocument::SetDomain) // if the document.domain was set. // -static -PRBool SameOrSubdomainOfTarget(nsIURI* aOriginURI, nsIURI* aTargetURI, PRBool aDocumentDomainSet) +static PRBool +SameOrSubdomainOfTarget(nsIURI* aOriginURI, nsIURI* aTargetURI, + PRBool aDocumentDomainSet) { nsCAutoString targetScheme; nsresult rv = aTargetURI->GetScheme(targetScheme); @@ -939,51 +938,80 @@ PRBool SameOrSubdomainOfTarget(nsIURI* aOriginURI, nsIURI* aTargetURI, PRBool aD // // Bug 13871: Prevent frameset spoofing // -// This routine answers: 'Is origin's document from same domain as target's document?' +// This routine answers: 'Is origin's document from same domain as +// target's document?' // Be optimistic that domain is same - error cases all answer 'yes'. // -// We have to compare the URI of the actual document loaded in the origin, -// ignoring any document.domain that was set, with the principal URI of the -// target (including any document.domain that was set). This puts control -// of loading in the hands of the target, which is more secure. (per Nav 4.x) +// We have to compare the URI of the actual document loaded in the +// origin, ignoring any document.domain that was set, with the +// principal URI of the target (including any document.domain that was +// set). This puts control of loading in the hands of the target, +// which is more secure. (per Nav 4.x) // -static -PRBool ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem, - nsIDocShellTreeItem* aTargetTreeItem) +static PRBool +ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem, + nsIDocShellTreeItem* aTargetTreeItem) { - // Get origin document uri (ignoring document.domain) - nsCOMPtr originWebNav(do_QueryInterface(aOriginTreeItem)); - NS_ENSURE_TRUE(originWebNav, PR_TRUE); + nsCOMPtr securityManager = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); + NS_ENSURE_TRUE(securityManager, PR_FALSE); - nsCOMPtr originDocumentURI; - nsresult rv = originWebNav->GetCurrentURI(getter_AddRefs(originDocumentURI)); - NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && originDocumentURI, PR_TRUE); + nsCOMPtr subjectPrincipal; + nsresult rv = + securityManager->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)); + NS_ENSURE_SUCCESS(rv, PR_TRUE); - // Get target principal uri (including document.domain) - nsCOMPtr targetDOMDocument(do_GetInterface(aTargetTreeItem)); - nsCOMPtr targetDocument(do_QueryInterface(targetDOMDocument)); - NS_ENSURE_TRUE(targetDocument, PR_TRUE); + if (subjectPrincipal) { + // We're called from JS, check if UniversalBrowserWrite is + // enabled. + PRBool ubwEnabled = PR_FALSE; + rv = securityManager->IsCapabilityEnabled("UniversalBrowserWrite", + &ubwEnabled); + NS_ENSURE_SUCCESS(rv, PR_FALSE); - nsIPrincipal *targetPrincipal = targetDocument->GetPrincipal(); - NS_ENSURE_TRUE(targetPrincipal, PR_TRUE); + if (ubwEnabled) { + return PR_TRUE; + } + } - nsCOMPtr targetPrincipalURI; - rv = targetPrincipal->GetURI(getter_AddRefs(targetPrincipalURI)); - NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && targetPrincipalURI, PR_TRUE); + // Get origin document uri (ignoring document.domain) + nsCOMPtr originWebNav = + do_QueryInterface(aOriginTreeItem); + NS_ENSURE_TRUE(originWebNav, PR_TRUE); - // Find out if document.domain was set for HTML documents - PRBool documentDomainSet = PR_FALSE; - nsCOMPtr targetHTMLDocument(do_QueryInterface(targetDocument)); + nsCOMPtr originDocumentURI; + rv = originWebNav->GetCurrentURI(getter_AddRefs(originDocumentURI)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && originDocumentURI, PR_TRUE); - // If we don't have an HTML document, fall through with documentDomainSet false - if (targetHTMLDocument) { - documentDomainSet = targetHTMLDocument->WasDomainSet(); - } + // Get target principal uri (including document.domain) + nsCOMPtr targetDOMDocument = + do_GetInterface(aTargetTreeItem); + nsCOMPtr targetDocument(do_QueryInterface(targetDOMDocument)); + NS_ENSURE_TRUE(targetDocument, PR_TRUE); - // Is origin same principal or a subdomain of target's document.domain - // Compare actual URI of origin document, not origin principal's URI. (Per Nav 4.x) - return SameOrSubdomainOfTarget(originDocumentURI, targetPrincipalURI, - documentDomainSet); + nsIPrincipal *targetPrincipal = targetDocument->GetPrincipal(); + NS_ENSURE_TRUE(targetPrincipal, PR_TRUE); + + nsCOMPtr targetPrincipalURI; + rv = targetPrincipal->GetURI(getter_AddRefs(targetPrincipalURI)); + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && targetPrincipalURI, PR_TRUE); + + // Find out if document.domain was set for HTML documents + PRBool documentDomainSet = PR_FALSE; + nsCOMPtr targetHTMLDocument = + do_QueryInterface(targetDocument); + + // If we don't have an HTML document, fall through with + // documentDomainSet false + if (targetHTMLDocument) { + documentDomainSet = targetHTMLDocument->WasDomainSet(); + } + + // Is origin same principal or a subdomain of target's + // document.domain Compare actual URI of origin document, not origin + // principal's URI. (Per Nav 4.x) + return SameOrSubdomainOfTarget(originDocumentURI, targetPrincipalURI, + documentDomainSet); } nsresult nsDocShell::FindTarget(const PRUnichar *aWindowTarget, @@ -991,135 +1019,36 @@ nsresult nsDocShell::FindTarget(const PRUnichar *aWindowTarget, nsIDocShell **aResult) { nsresult rv; - nsAutoString name(aWindowTarget); - nsCOMPtr treeItem; - PRBool mustMakeNewWindow = PR_FALSE; *aResult = nsnull; *aIsNewWindow = PR_FALSE; - if(!name.Length() || name.LowerCaseEqualsLiteral("_self")) - { - *aResult = this; - } - else if (name.LowerCaseEqualsLiteral("_blank") || name.LowerCaseEqualsLiteral("_new")) - { - mustMakeNewWindow = PR_TRUE; - name.Truncate(); - } - else if(name.LowerCaseEqualsLiteral("_parent")) - { - GetSameTypeParent(getter_AddRefs(treeItem)); - if(!treeItem) - *aResult = this; - } - else if(name.LowerCaseEqualsLiteral("_top")) - { - GetSameTypeRootTreeItem(getter_AddRefs(treeItem)); - if(!treeItem) - *aResult = this; - } - // _main is an IE target which should be case-insensitive but isn't - // see bug 217886 for details - else if(name.LowerCaseEqualsLiteral("_content") || name.EqualsLiteral("_main")) - { - if (mTreeOwner) { - mTreeOwner->FindItemWithName(name.get(), nsnull, - getter_AddRefs(treeItem)); - } else { - NS_ERROR("Someone isn't setting up the tree owner. " - "You might like to try that. " - "Things will.....you know, work."); - } - // _content should always exist. If the nsIDocShellTreeOwner did - // not find one, then create one... - if (!treeItem) { - mustMakeNewWindow = PR_TRUE; - } - } - else - { - // Try to locate the target window... - FindItemWithName(name.get(), nsnull, getter_AddRefs(treeItem)); - - // The named window cannot be found so it must be created to receive - // the channel data. - - if (!treeItem) { - mustMakeNewWindow = PR_TRUE; - } - - // Bug 13871: Prevent frameset spoofing - // See BugSplat 336170, 338737 and XP_FindNamedContextInList in - // the classic codebase - // Per Nav's behaviour: - // - pref controlled: "browser.frame.validate_origin" - // (mValidateOrigin) - // - allow load if host of target or target's parent is same - // as host of origin - // - allow load if target is a top level window - - // Check to see if pref is true - if (mValidateOrigin && treeItem) - { - nsCOMPtr tmp; - treeItem->GetSameTypeRootTreeItem(getter_AddRefs(tmp)); - - nsCOMPtr sameTypeRoot; - GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); - - if (sameTypeRoot != tmp && treeItem != tmp) { - // The load was targeted at a frame and initiated in - // another toplevel window. Assume we'll need to make - // a new window until we find that the target, or one - // of its ancestors, are from the same origin as the - // loading docshell. - mustMakeNewWindow = PR_TRUE; - - tmp = treeItem; - - do { - // Is origin frame from the same domain as target frame? - if (ValidateOrigin(this, tmp)) { - mustMakeNewWindow = PR_FALSE; - - break; - } - - nsCOMPtr t; - tmp->GetSameTypeParent(getter_AddRefs(t)); - tmp.swap(t); - } while (tmp); - - if (mustMakeNewWindow) { - // Origin mismatch, open the URL in a new blank - // window. - treeItem = nsnull; - name.Truncate(); - } - } - } - } + // Try to locate the target window... + nsCOMPtr treeItem; + FindItemWithName(aWindowTarget, nsnull, this, getter_AddRefs(treeItem)); PRInt32 linkPref = nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW; - if (mustMakeNewWindow) { + // XXXbz this should live inside FindItemWithName, I think... + if (!treeItem) { mPrefs->GetIntPref("browser.link.open_newwindow", &linkPref); if (linkPref == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { // force new window to go to _top - GetSameTypeRootTreeItem(getter_AddRefs(treeItem)); - if(!treeItem) - *aResult = this; - mustMakeNewWindow = PR_FALSE; + FindItemWithName(NS_LITERAL_STRING("_top").get(), + nsnull, this, getter_AddRefs(treeItem)); + NS_ASSERTION(treeItem, + "We better get a treeitem when asking for '_top' " + "with |this| as the original requestor"); } } - if (mustMakeNewWindow) + if (!treeItem) { + /// XXXbz this should really happen in FindItemWithName too, I think... nsCOMPtr newWindow; nsCOMPtr parentWindow; // This DocShell is the parent window - parentWindow = do_GetInterface(NS_STATIC_CAST(nsIDocShell*, this)); + parentWindow = do_GetInterface(GetAsSupports(this)); if (!parentWindow) { NS_ASSERTION(0, "Can't get nsIDOMWindowInternal from nsDocShell!"); return NS_ERROR_FAILURE; @@ -1169,12 +1098,19 @@ nsresult nsDocShell::FindTarget(const PRUnichar *aWindowTarget, // the appropriate measures will be taken when the popup fails } - if (!newWindow) - rv = parentWindow->Open(EmptyString(), // URL to load - name, // Window name - EmptyString(), // Window features - getter_AddRefs(newWindow)); - + if (!newWindow) { + nsAutoString name(aWindowTarget); + // XXXbz this should be handled somewhere else.... and in fact, it + // may be safe to just pass those through here. Check. + if (name.LowerCaseEqualsLiteral("_blank") || + name.LowerCaseEqualsLiteral("_new")) { + name.Truncate(); + } + rv = parentWindow->Open(EmptyString(), // URL to load + name, // Window name + EmptyString(), // Window features + getter_AddRefs(newWindow)); + } if (NS_FAILED(rv)) return rv; // Get the DocShell from the new window... @@ -1877,6 +1813,94 @@ nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem ** aRootTreeItem) return NS_OK; } +static PRBool +CanAccessItem(nsIDocShellTreeItem* aTargetItem, + nsIDocShellTreeItem* aAccessingItem, + PRBool aConsiderOpener = PR_TRUE) +{ + NS_PRECONDITION(aTargetItem, "Must have target item!"); + + if (!gValidateOrigin || !aAccessingItem) { + // Good to go + return PR_TRUE; + } + + // XXXbz should we care if aAccessingItem or the document therein is + // chrome? Should those get extra privileges? + + // Now do a security check + // Bug 13871: Prevent frameset spoofing + // See BugSplat 336170, 338737 and XP_FindNamedContextInList in + // the classic codebase + // Nav's behaviour was: + // - pref controlled: "browser.frame.validate_origin" + // (gValidateOrigin) + // - allow load if host of target or target's parent is same + // as host of origin + // - allow load if target is a top level window + + // We are going to be a little more restrictive, with the + // following algorithm: + // - pref controlled in the same way + // - allow access if the two treeitems are in the same tree + // - allow access if the aTargetItem or one of its ancestors + // has the same origin as aAccessingItem + // - allow access if the target is a toplevel window and we can + // access its opener. Note that we only allow one level of + // recursion there. + + nsCOMPtr targetRoot; + aTargetItem->GetSameTypeRootTreeItem(getter_AddRefs(targetRoot)); + + nsCOMPtr accessingRoot; + aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(accessingRoot)); + + if (targetRoot == accessingRoot) { + return PR_TRUE; + } + + nsCOMPtr target = aTargetItem; + do { + if (ValidateOrigin(aAccessingItem, target)) { + return PR_TRUE; + } + + nsCOMPtr parent; + target->GetSameTypeParent(getter_AddRefs(parent)); + parent.swap(target); + } while (target); + + if (aTargetItem != targetRoot) { + // target is a subframe, not in accessor's frame hierarchy, and all its + // ancestors have origins different from that of the accessor. Don't + // allow access. + return PR_FALSE; + } + + if (!aConsiderOpener) { + // All done here + return PR_FALSE; + } + + nsCOMPtr targetWindow(do_GetInterface(aTargetItem)); + nsCOMPtr targetInternal(do_QueryInterface(targetWindow)); + if (!targetInternal) { + NS_ERROR("This should not happen, really"); + return PR_FALSE; + } + + nsCOMPtr targetOpener; + targetInternal->GetOpener(getter_AddRefs(targetOpener)); + nsCOMPtr openerWebNav(do_GetInterface(targetOpener)); + nsCOMPtr openerItem(do_QueryInterface(openerWebNav)); + + if (!openerItem) { + return PR_FALSE; + } + + return CanAccessItem(openerItem, aAccessingItem, PR_FALSE); +} + static PRBool ItemIsActive(nsIDocShellTreeItem *aItem) { @@ -1897,38 +1921,110 @@ ItemIsActive(nsIDocShellTreeItem *aItem) NS_IMETHODIMP nsDocShell::FindItemWithName(const PRUnichar * aName, nsISupports * aRequestor, + nsIDocShellTreeItem * aOriginalRequestor, nsIDocShellTreeItem ** _retval) { NS_ENSURE_ARG(aName); NS_ENSURE_ARG_POINTER(_retval); - *_retval = nsnull; // if we don't find one, we return NS_OK and a null result + // If we don't find one, we return NS_OK and a null result + *_retval = nsnull; - // This QI may fail, but the places where we want to compare, comparing - // against nsnull serves the same purpose. - nsCOMPtr - reqAsTreeItem(do_QueryInterface(aRequestor)); + if (!aRequestor) + { + nsCOMPtr foundItem; + // This is the entry point into the target-finding algorithm. Check + // for special names. This should only be done once, hence the check + // for a null aRequestor. + + nsDependentString name(aName); + if (name.IsEmpty() || name.LowerCaseEqualsLiteral("_self")) { + foundItem = this; + } + else if (name.LowerCaseEqualsLiteral("_blank") || + name.LowerCaseEqualsLiteral("_new")) + { + // Just return null. Caller must handle creating a new window with + // a blank name himself. + return NS_OK; + } + else if (name.LowerCaseEqualsLiteral("_parent")) + { + GetSameTypeParent(getter_AddRefs(foundItem)); + if(!foundItem) + foundItem = this; + } + else if (name.LowerCaseEqualsLiteral("_top")) + { + GetSameTypeRootTreeItem(getter_AddRefs(foundItem)); + if(!foundItem) + foundItem = this; + } + // _main is an IE target which should be case-insensitive but isn't + // see bug 217886 for details + else if (name.LowerCaseEqualsLiteral("_content") || + name.EqualsLiteral("_main")) + { + if (mTreeOwner) + mTreeOwner->GetPrimaryContentShell(getter_AddRefs(foundItem)); +#ifdef DEBUG + else { + NS_ERROR("Someone isn't setting up the tree owner. " + "You might like to try that. " + "Things will.....you know, work."); + // Note: _content should always exist. If we don't have one + // hanging off the treeowner, just create a named window.... + // so don't return here, in case we did that and can now find + // it. + } +#endif + } + + if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) { + foundItem = nsnull; + } + + if (foundItem) { + // We return foundItem here even if it's not an active + // item since all the names we've dealt with so far are + // special cases that we won't bother looking for further. + + foundItem.swap(*_retval); + return NS_OK; + } + } + + // Keep looking + // First we check our name. - if (mName.Equals(aName) && ItemIsActive(this)) { - *_retval = this; - NS_ADDREF(*_retval); + if (mName.Equals(aName) && ItemIsActive(this) && + CanAccessItem(this, aOriginalRequestor)) { + NS_ADDREF(*_retval = this); return NS_OK; } - // Second we check our children making sure not to ask a child if it - // is the aRequestor. - NS_ENSURE_SUCCESS(FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem, - _retval), NS_ERROR_FAILURE); + // This QI may fail, but the places where we want to compare, comparing + // against nsnull serves the same purpose. + nsCOMPtr reqAsTreeItem(do_QueryInterface(aRequestor)); + + // Second we check our children making sure not to ask a child if + // it is the aRequestor. +#ifdef DEBUG + nsresult rv = +#endif + FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem, + aOriginalRequestor, _retval); + NS_ASSERTION(NS_SUCCEEDED(rv), + "FindChildWithName should not be failing here."); if (*_retval) return NS_OK; - - // Third if we have a parent and it isn't the requestor then we should ask - // it to do the search. If it is the requestor we should just stop here - // and let the parent do the rest. - // If we don't have a parent, then we should ask the docShellTreeOwner to do - // the search. - + + // Third if we have a parent and it isn't the requestor then we + // should ask it to do the search. If it is the requestor we + // should just stop here and let the parent do the rest. If we + // don't have a parent, then we should ask the + // docShellTreeOwner to do the search. nsCOMPtr parentAsTreeItem = do_QueryInterface(GetAsSupports(mParent)); if (parentAsTreeItem) { @@ -1938,12 +2034,15 @@ nsDocShell::FindItemWithName(const PRUnichar * aName, PRInt32 parentType; parentAsTreeItem->GetItemType(&parentType); if (parentType == mItemType) { - return parentAsTreeItem->FindItemWithName(aName, - NS_STATIC_CAST - (nsIDocShellTreeItem *, - this), _retval); + return parentAsTreeItem-> + FindItemWithName(aName, + NS_STATIC_CAST(nsIDocShellTreeItem*, + this), + aOriginalRequestor, + _retval); } } + // If the parent is null or not of the same type fall through and ask tree // owner. @@ -1951,12 +2050,13 @@ nsDocShell::FindItemWithName(const PRUnichar * aName, nsCOMPtr reqAsTreeOwner(do_GetInterface(aRequestor)); - if (mTreeOwner && (mTreeOwner != reqAsTreeOwner.get())) { - NS_ENSURE_SUCCESS(mTreeOwner->FindItemWithName(aName, - NS_STATIC_CAST - (nsIDocShellTreeItem *, - this), _retval), - NS_ERROR_FAILURE); + if (mTreeOwner && mTreeOwner != reqAsTreeOwner) { + return mTreeOwner-> + FindItemWithName(aName, + NS_STATIC_CAST(nsIDocShellTreeItem*, + this), + aOriginalRequestor, + _retval); } return NS_OK; @@ -2275,6 +2375,7 @@ NS_IMETHODIMP nsDocShell::FindChildWithName(const PRUnichar * aName, PRBool aRecurse, PRBool aSameType, nsIDocShellTreeItem * aRequestor, + nsIDocShellTreeItem * aOriginalRequestor, nsIDocShellTreeItem ** _retval) { NS_ENSURE_ARG(aName); @@ -2295,9 +2396,9 @@ nsDocShell::FindChildWithName(const PRUnichar * aName, PRBool childNameEquals = PR_FALSE; child->NameEquals(aName, &childNameEquals); - if (childNameEquals && ItemIsActive(child)) { - *_retval = child; - NS_ADDREF(*_retval); + if (childNameEquals && ItemIsActive(child) && + CanAccessItem(child, aOriginalRequestor)) { + child.swap(*_retval); break; } @@ -2309,18 +2410,22 @@ nsDocShell::FindChildWithName(const PRUnichar * aName, // See if child contains the shell with the given name nsCOMPtr childAsNode(do_QueryInterface(child)); - if (child) { - NS_ENSURE_SUCCESS(childAsNode->FindChildWithName(aName, PR_TRUE, - aSameType, - NS_STATIC_CAST - (nsIDocShellTreeItem - *, this), - _retval), - NS_ERROR_FAILURE); + if (childAsNode) { +#ifdef DEBUG + nsresult rv = +#endif + childAsNode->FindChildWithName(aName, PR_TRUE, + aSameType, + NS_STATIC_CAST(nsIDocShellTreeItem*, + this), + aOriginalRequestor, + _retval); + NS_ASSERTION(NS_SUCCEEDED(rv), + "FindChildWithName should not fail here"); + if (*_retval) // found it + return NS_OK; } } - if (*_retval) // found it - return NS_OK; } return NS_OK; } @@ -3145,17 +3250,22 @@ nsDocShell::Create() rv = mPrefs->GetBoolPref("browser.frames.enabled", &tmpbool); if (NS_SUCCEEDED(rv)) - mAllowSubframes = tmpbool; + mAllowSubframes = tmpbool; - // Check pref to see if we should prevent frameset spoofing - rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool); - if (NS_SUCCEEDED(rv)) - mValidateOrigin = tmpbool; + if (gValidateOrigin == (PRBool)0xffffffff) { + // Check pref to see if we should prevent frameset spoofing + rv = mPrefs->GetBoolPref("browser.frame.validate_origin", &tmpbool); + if (NS_SUCCEEDED(rv)) { + gValidateOrigin = tmpbool; + } else { + gValidateOrigin = PR_TRUE; + } + } // Should we use XUL error pages instead of alerts if possible? rv = mPrefs->GetBoolPref("browser.xul.error_pages.enabled", &tmpbool); if (NS_SUCCEEDED(rv)) - mUseErrorPages = tmpbool; + mUseErrorPages = tmpbool; return NS_OK; } @@ -4929,9 +5039,16 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) nsresult nsDocShell::CheckLoadingPermissions() { + // This method checks whether the caller may load content into + // this docshell. Even though we've done our best to hide windows + // from code that doesn't have the right to access them, it's + // still possible for an evil site to open a window and access + // frames in the new window through window.frames[] (which is + // allAccess for historic reasons), so we still need to do this + // check on load. nsresult rv = NS_OK, sameOrigin = NS_OK; - if (!mValidateOrigin || !IsFrame()) { + if (!gValidateOrigin || !IsFrame()) { // Origin validation was turned off, or we're not a frame. // Permit all loads. @@ -5139,6 +5256,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, // created. It really doesn't belong here, but until there is a // way for embeddors to get involved in window targeting, this is // as good a place as any... + // XXXbz no, it's not.... fixme!!!! // PRInt32 linkPref = nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW; mPrefs->GetIntPref("browser.link.open_newwindow", &linkPref); @@ -5166,6 +5284,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, nsCOMPtr targetTreeItem; FindItemWithName(name.get(), nsnull, + this, getter_AddRefs(targetTreeItem)); if (targetTreeItem) targetDocShell = do_QueryInterface(targetTreeItem); diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index 585ce5f72f8..81d9eba3e85 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -368,9 +368,6 @@ protected: PRPackedBool mIsBeingDestroyed; - // Validate window targets to prevent frameset spoofing - PRPackedBool mValidateOrigin; - PRPackedBool mIsExecutingOnLoadHandler; // Indicates that a DocShell in this "docshell tree" is printing diff --git a/docshell/base/nsIDocShellTreeItem.idl b/docshell/base/nsIDocShellTreeItem.idl index 3a5dfa68c12..31f23889954 100644 --- a/docshell/base/nsIDocShellTreeItem.idl +++ b/docshell/base/nsIDocShellTreeItem.idl @@ -48,7 +48,7 @@ interface nsIDocShellTreeOwner; * node or a leaf. */ -[scriptable, uuid(1b3416f3-0ec6-49e6-8bdf-77bfdbc4a102)] +[scriptable, uuid(7d935d63-6d2a-4600-afb5-9a4f7d68b825)] interface nsIDocShellTreeItem : nsISupports { /* @@ -130,8 +130,12 @@ interface nsIDocShellTreeItem : nsISupports a shell by the specified name. Inversely the child uses it to ensure it does not ask its parent to do the search if its parent is the one that asked it to search. Children also use this to test against the treeOwner; + aOriginalRequestor - The original treeitem that made the request, if any. + This is used to ensure that we don't run into cross-site issues. */ - nsIDocShellTreeItem findItemWithName(in wstring name, in nsISupports aRequestor); + nsIDocShellTreeItem findItemWithName(in wstring name, + in nsISupports aRequestor, + in nsIDocShellTreeItem aOriginalRequestor); /* The owner of the DocShell Tree. This interface will be called upon when diff --git a/docshell/base/nsIDocShellTreeNode.idl b/docshell/base/nsIDocShellTreeNode.idl index f01f2f107e0..01d67240c59 100644 --- a/docshell/base/nsIDocShellTreeNode.idl +++ b/docshell/base/nsIDocShellTreeNode.idl @@ -50,7 +50,7 @@ // XXXbz this interface should probably inherit from nsIDocShellTreeItem, and // some methods should move from there to here... -[scriptable, uuid(C094F810-A8AB-11d3-AFC6-00A024FFC08C)] +[scriptable, uuid(37f1ab73-f224-44b1-82f0-d2834ab1cec0)] interface nsIDocShellTreeNode : nsISupports { /* @@ -87,11 +87,16 @@ interface nsIDocShellTreeNode : nsISupports aRequestor - This is the docshellTreeItem that is requesting the find. This parameter is used when recursion is being used to avoid searching the same tree again when a child has asked a parent to search for children. + aOriginalRequestor - The original treeitem that made the request, if any. + This is used to ensure that we don't run into cross-site issues. Note the search is depth first when recursing. // XXXbz this should return an nsIDocShellTreeNode, I think. */ - nsIDocShellTreeItem findChildWithName(in wstring aName, in boolean aRecurse, - in boolean aSameType, in nsIDocShellTreeItem aRequestor); + nsIDocShellTreeItem findChildWithName(in wstring aName, + in boolean aRecurse, + in boolean aSameType, + in nsIDocShellTreeItem aRequestor, + in nsIDocShellTreeItem aOriginalRequestor); }; diff --git a/docshell/base/nsIDocShellTreeOwner.idl b/docshell/base/nsIDocShellTreeOwner.idl index f9820fe3fbd..b829b67d34a 100644 --- a/docshell/base/nsIDocShellTreeOwner.idl +++ b/docshell/base/nsIDocShellTreeOwner.idl @@ -45,7 +45,7 @@ interface nsIDocShellTreeItem; -[scriptable, uuid(80F30E10-A7CF-11d3-AFC5-00A024FFC08C)] +[scriptable, uuid(9e508466-5ebb-4618-abfa-9ad47bed0b2e)] interface nsIDocShellTreeOwner : nsISupports { /* @@ -58,9 +58,13 @@ interface nsIDocShellTreeOwner : nsISupports a shell by the specified name. Inversely the child uses it to ensure it does not ask its parent to do the search if its parent is the one that asked it to search. + aOriginalRequestor - The original treeitem that made the request, if any. + This is used to ensure that we don't run into cross-site issues. + */ nsIDocShellTreeItem findItemWithName(in wstring name, - in nsIDocShellTreeItem aRequestor); + in nsIDocShellTreeItem aRequestor, + in nsIDocShellTreeItem aOriginalRequestor); /* Called when a content shell is added to the the docShell Tree. diff --git a/dom/src/base/nsDOMClassInfo.cpp b/dom/src/base/nsDOMClassInfo.cpp index 97ada7f83cc..42711cb58a3 100644 --- a/dom/src/base/nsDOMClassInfo.cpp +++ b/dom/src/base/nsDOMClassInfo.cpp @@ -4712,7 +4712,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, const jschar *chars = ::JS_GetStringChars(str); dsn->FindChildWithName(NS_REINTERPRET_CAST(const PRUnichar*, chars), - PR_FALSE, PR_TRUE, nsnull, + PR_FALSE, PR_TRUE, nsnull, nsnull, getter_AddRefs(child)); nsCOMPtr child_win(do_GetInterface(child)); diff --git a/dom/src/base/nsDOMWindowList.cpp b/dom/src/base/nsDOMWindowList.cpp index a131152bb86..3914218f894 100644 --- a/dom/src/base/nsDOMWindowList.cpp +++ b/dom/src/base/nsDOMWindowList.cpp @@ -175,7 +175,7 @@ nsDOMWindowList::NamedItem(const nsAString& aName, nsIDOMWindow** aReturn) if (mDocShellNode) { mDocShellNode->FindChildWithName(PromiseFlatString(aName).get(), - PR_FALSE, PR_FALSE, + PR_FALSE, PR_FALSE, nsnull, nsnull, getter_AddRefs(item)); nsCOMPtr globalObject(do_GetInterface(item)); diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index c8efc956ac5..2e09eb2d3e2 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -2109,6 +2109,71 @@ nsGlobalWindow::DispatchCustomEvent(const char *aEventName) return preventDefault; } +static already_AddRefed +GetCallerDocShellTreeItem() +{ + nsCOMPtr stack = + do_GetService(sJSStackContractID); + + JSContext *cx = nsnull; + + if (stack) { + stack->Peek(&cx); + } + + nsIDocShellTreeItem *callerItem = nsnull; + + if (cx) { + nsCOMPtr callerWebNav = + do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx)); + + if (callerWebNav) { + CallQueryInterface(callerWebNav, &callerItem); + } + } + + return callerItem; +} + +PRBool +nsGlobalWindow::WindowExists(const nsAString& aName) +{ + nsCOMPtr caller = GetCallerDocShellTreeItem(); + PRBool foundWindow = PR_FALSE; + + if (!caller) { + // If we can't reach a caller, try to use our own docshell + caller = do_QueryInterface(mDocShell); + } + + nsCOMPtr docShell(do_QueryInterface(mDocShell)); + + if (docShell) { + nsCOMPtr namedItem; + + docShell->FindItemWithName(PromiseFlatString(aName).get(), nsnull, caller, + getter_AddRefs(namedItem)); + + foundWindow = !!namedItem; + } else { + // No caller reachable and we don't have a docshell any more. Fall + // back to using the windowwatcher service to find any window by + // name. + + nsCOMPtr wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID); + if (wwatch) { + nsCOMPtr namedWindow; + wwatch->GetWindowByName(PromiseFlatString(aName).get(), nsnull, + getter_AddRefs(namedWindow)); + + foundWindow = !!namedWindow; + } + } + + return foundWindow; +} + NS_IMETHODIMP nsGlobalWindow::SetFullScreen(PRBool aFullScreen) { @@ -2431,11 +2496,11 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial, // Test whether title needs to prefixed with [script] nsAutoString title; if (!IsCallerChrome()) { - MakeScriptDialogTitle(aTitle, title); + MakeScriptDialogTitle(aTitle, title); } else { - NS_WARNING("chrome shouldn't be calling prompt(), use the prompt " - "service"); - title.Assign(aTitle); + NS_WARNING("chrome shouldn't be calling prompt(), use the prompt " + "service"); + title.Assign(aTitle); } nsresult rv = prompter->Prompt(title.get(), @@ -3116,24 +3181,17 @@ nsGlobalWindow::CheckOpenAllow(PopupControlState aAbuseLevel, allowWindow = allowWhitelisted; else { // Special case items that don't actually open new windows. - nsAutoString name(aName); - if (!name.IsEmpty()) { + if (!aName.IsEmpty()) { // _main is an IE target which should be case-insensitive but isn't // see bug 217886 for details - if (name.LowerCaseEqualsLiteral("_top") || - name.LowerCaseEqualsLiteral("_self") || - name.LowerCaseEqualsLiteral("_content") || - name.EqualsLiteral("_main")) + if (aName.LowerCaseEqualsLiteral("_top") || + aName.LowerCaseEqualsLiteral("_self") || + aName.LowerCaseEqualsLiteral("_content") || + aName.EqualsLiteral("_main")) allowWindow = allowSelf; else { - nsCOMPtr wwatch = - do_GetService(NS_WINDOWWATCHER_CONTRACTID); - if (wwatch) { - nsCOMPtr namedWindow; - wwatch->GetWindowByName(PromiseFlatString(aName).get(), this, - getter_AddRefs(namedWindow)); - if (namedWindow) - allowWindow = allowExtant; + if (WindowExists(aName)) { + allowWindow = allowExtant; } } } @@ -4627,33 +4685,12 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, if (NS_FAILED(rv)) return rv; - nsAutoString nameString(aName); - // determine whether we must divert the open window to a new tab. - PRBool divertOpen = PR_TRUE; // at first, assume we will divert + PRBool divertOpen = !WindowExists(aName); - // first, does the named window already exist? (see nsWindowWatcher) - - if (nameString.EqualsIgnoreCase("_top") || - nameString.EqualsIgnoreCase("_self") || - nameString.EqualsIgnoreCase("_content") || - nameString.EqualsIgnoreCase("_parent") || - nameString.Equals(NS_LITERAL_STRING("_main"))) - divertOpen = PR_FALSE; - else { - nsCOMPtr docOwner; - GetTreeOwner(getter_AddRefs(docOwner)); - if (docOwner) { - nsCOMPtr namedWindow; - docOwner->FindItemWithName(nameString.get(), 0, - getter_AddRefs(namedWindow)); - if (namedWindow) - divertOpen = PR_FALSE; - } - } - - // second, what do the prefs prescribe? + // also check what the prefs prescribe? + // XXXbz this duplicates docshell code. Need to consolidate. PRInt32 containerPref = nsIBrowserDOMWindow::OPEN_NEWWINDOW; @@ -4671,8 +4708,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, PRBool chromeTab = PR_FALSE; if (tabURI) tabURI->SchemeIs("chrome", &chromeTab); - if (!thisChrome && !chromeTab) { + if (!thisChrome && !chromeTab) { containerPref=nsContentUtils::GetIntPref("browser.link.open_newwindow", nsIBrowserDOMWindow::OPEN_NEWWINDOW); PRInt32 restrictionPref = nsContentUtils::GetIntPref( @@ -4738,8 +4775,8 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, GetTop(getter_AddRefs(domReturn)); } - if (domReturn && !nameString.EqualsIgnoreCase("_blank") && - !nameString.EqualsIgnoreCase("_new")) + if (domReturn && !aName.LowerCaseEqualsLiteral("_blank") && + !aName.LowerCaseEqualsLiteral("_new")) domReturn->SetName(aName); } diff --git a/dom/src/base/nsGlobalWindow.h b/dom/src/base/nsGlobalWindow.h index c3a4f8ea337..8254ed827e1 100644 --- a/dom/src/base/nsGlobalWindow.h +++ b/dom/src/base/nsGlobalWindow.h @@ -294,6 +294,8 @@ protected: PRBool DispatchCustomEvent(const char *aEventName); + PRBool WindowExists(const nsAString& aName); + // When adding new member variables, be careful not to create cycles // through JavaScript. If there is any chance that a member variable // could own objects that are implemented in JavaScript, then those diff --git a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp index f9f84de64e9..779c055ed4a 100644 --- a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp +++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp @@ -209,6 +209,7 @@ nsDocShellTreeOwner::GetInterface(const nsIID& aIID, void** aSink) NS_IMETHODIMP nsDocShellTreeOwner::FindItemWithName(const PRUnichar* aName, nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aFoundItem) { NS_ENSURE_ARG(aName); @@ -250,7 +251,8 @@ nsDocShellTreeOwner::FindItemWithName(const PRUnichar* aName, } // next, check our children - rv = FindChildWithName(aName, PR_TRUE, aRequestor, aFoundItem); + rv = FindChildWithName(aName, PR_TRUE, aRequestor, aOriginalRequestor, + aFoundItem); if(NS_FAILED(rv) || *aFoundItem) return rv; @@ -260,20 +262,21 @@ nsDocShellTreeOwner::FindItemWithName(const PRUnichar* aName, if(mTreeOwner) { if (mTreeOwner != reqAsTreeOwner) return mTreeOwner->FindItemWithName(aName, mWebBrowser->mDocShellAsItem, - aFoundItem); + aOriginalRequestor, aFoundItem); return NS_OK; } // finally, failing everything else, search all windows, if we're not already if (mWebBrowser->mDocShellAsItem != aRequestor) - return FindItemWithNameAcrossWindows(aName, aFoundItem); + return FindItemWithNameAcrossWindows(aName, aOriginalRequestor, aFoundItem); return NS_OK; // failed } nsresult nsDocShellTreeOwner::FindChildWithName(const PRUnichar *aName, PRBool aRecurse, - nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem **aFoundItem) { if (!mWebBrowser) @@ -302,7 +305,8 @@ nsDocShellTreeOwner::FindChildWithName(const PRUnichar *aName, PRBool aRecurse, nsCOMPtr item = do_QueryInterface(sgo->GetDocShell()); if (item && item != aRequestor) { - rv = item->FindItemWithName(aName, mWebBrowser->mDocShellAsItem, aFoundItem); + rv = item->FindItemWithName(aName, mWebBrowser->mDocShellAsItem, + aOriginalRequestor, aFoundItem); if (NS_FAILED(rv) || *aFoundItem) break; } @@ -314,6 +318,7 @@ nsDocShellTreeOwner::FindChildWithName(const PRUnichar *aName, PRBool aRecurse, nsresult nsDocShellTreeOwner::FindItemWithNameAcrossWindows(const PRUnichar* aName, + nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aFoundItem) { // search for the item across the list of top-level windows @@ -341,7 +346,7 @@ nsDocShellTreeOwner::FindItemWithNameAcrossWindows(const PRUnichar* aName, nsCOMPtr item = do_QueryInterface(sgo->GetDocShell()); if (item) { - rv = item->FindItemWithName(aName, item, aFoundItem); + rv = item->FindItemWithName(aName, item, aOriginalRequestor, aFoundItem); if (NS_FAILED(rv) || *aFoundItem) break; } diff --git a/embedding/browser/webBrowser/nsDocShellTreeOwner.h b/embedding/browser/webBrowser/nsDocShellTreeOwner.h index bd42caf4a0e..5013d5ba1fb 100644 --- a/embedding/browser/webBrowser/nsDocShellTreeOwner.h +++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.h @@ -123,9 +123,11 @@ protected: NS_IMETHOD RemoveChromeListeners(); nsresult FindChildWithName(const PRUnichar *aName, - PRBool aRecurse, nsIDocShellTreeItem* aRequestor, + PRBool aRecurse, nsIDocShellTreeItem* aRequestor, + nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem **aFoundItem); nsresult FindItemWithNameAcrossWindows(const PRUnichar* aName, + nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem **aFoundItem); void EnsurePrompter(); diff --git a/embedding/browser/webBrowser/nsWebBrowser.cpp b/embedding/browser/webBrowser/nsWebBrowser.cpp index cf0819d9542..ecfe32aee0b 100644 --- a/embedding/browser/webBrowser/nsWebBrowser.cpp +++ b/embedding/browser/webBrowser/nsWebBrowser.cpp @@ -563,13 +563,15 @@ NS_IMETHODIMP nsWebBrowser::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootT } NS_IMETHODIMP nsWebBrowser::FindItemWithName(const PRUnichar *aName, - nsISupports* aRequestor, nsIDocShellTreeItem **_retval) + nsISupports* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem **_retval) { NS_ENSURE_STATE(mDocShell); NS_ASSERTION(mDocShellTreeOwner, "This should always be set when in this situation"); return mDocShellAsItem->FindItemWithName(aName, - NS_STATIC_CAST(nsIDocShellTreeOwner*, mDocShellTreeOwner), _retval); + NS_STATIC_CAST(nsIDocShellTreeOwner*, mDocShellTreeOwner), + aOriginalRequestor, _retval); } NS_IMETHODIMP nsWebBrowser::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) diff --git a/embedding/components/windowwatcher/public/nsIWindowWatcher.idl b/embedding/components/windowwatcher/public/nsIWindowWatcher.idl index 3c80243a137..9969c87dee3 100644 --- a/embedding/components/windowwatcher/public/nsIWindowWatcher.idl +++ b/embedding/components/windowwatcher/public/nsIWindowWatcher.idl @@ -160,6 +160,10 @@ interface nsIWindowWatcher : nsISupports { @param aCurrentWindow a starting point in the window hierarchy to begin the search. If null, each toplevel window will be searched. + + Note: This method will search all open windows for any window or + frame with the given window name. Make sure you understand the + security implications of this before using this method! */ nsIDOMWindow getWindowByName(in wstring aTargetName, in nsIDOMWindow aCurrentWindow); diff --git a/embedding/components/windowwatcher/src/nsWWJSUtils.cpp b/embedding/components/windowwatcher/src/nsWWJSUtils.cpp index c015618fb89..ef51d6274b1 100644 --- a/embedding/components/windowwatcher/src/nsWWJSUtils.cpp +++ b/embedding/components/windowwatcher/src/nsWWJSUtils.cpp @@ -91,6 +91,15 @@ nsWWJSUtils::GetDynamicScriptContext(JSContext *aContext) return GetScriptContextFromJSContext(aContext); } +nsIScriptGlobalObject * +nsWWJSUtils::GetDynamicScriptGlobal(JSContext* aContext) +{ + nsIScriptContext *scriptCX = GetDynamicScriptContext(aContext); + if (!scriptCX) + return nsnull; + return scriptCX->GetGlobalObject(); +} + nsIScriptContext * nsWWJSUtils::GetStaticScriptContext(JSContext* aContext, JSObject* aObj) diff --git a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp index c040dac7376..5d16955a89e 100644 --- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp @@ -480,7 +480,7 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent, uriToLoadIsChrome = PR_FALSE; PRUint32 chromeFlags; nsAutoString name; // string version of aName - nsCString features; // string version of aFeatures + nsCAutoString features; // string version of aFeatures nsCOMPtr uriToLoad; // from aUrl, if any nsCOMPtr parentTreeOwner; // from the parent window, if any nsCOMPtr newDocShellItem; // from the new window @@ -522,41 +522,37 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent, // try to find an extant window with the given name if (nameSpecified) { - /* Oh good. special target names are now handled in multiple places: - Here and within FindItemWithName, just below. I put _top here because - here it's able to do what it should: get the topmost shell of the same - (content/chrome) type as the docshell. treeOwner is always chrome, so - this scheme doesn't work there, where a lot of other special case - targets are handled. (treeOwner is, however, a good place to look - for browser windows by name, as it does.) - */ - if (aParent) { - if (name.LowerCaseEqualsLiteral("_self")) { - GetWindowTreeItem(aParent, getter_AddRefs(newDocShellItem)); - } else if (name.LowerCaseEqualsLiteral("_top")) { - nsCOMPtr shelltree; - GetWindowTreeItem(aParent, getter_AddRefs(shelltree)); - if (shelltree) - shelltree->GetSameTypeRootTreeItem(getter_AddRefs(newDocShellItem)); - } else if (name.LowerCaseEqualsLiteral("_parent")) { - nsCOMPtr shelltree; - GetWindowTreeItem(aParent, getter_AddRefs(shelltree)); - if (shelltree) - shelltree->GetSameTypeParent(getter_AddRefs(newDocShellItem)); - // If there is no real parent then _self acts as _parent - if (!newDocShellItem) - newDocShellItem = shelltree; - } else { - /* parent is being simultaneously torn down (probably because of - the code that keeps an old docshell alive but disconnected while - we load a new one). not much to do but open the new window - without a parent. */ - if (parentTreeOwner) - parentTreeOwner->FindItemWithName(name.get(), nsnull, - getter_AddRefs(newDocShellItem)); - } + nsCOMPtr stack = + do_GetService(sJSStackContractID); + + JSContext *cx = nsnull; + + if (stack) { + stack->Peek(&cx); + } + + nsCOMPtr callerItem; + + if (cx) { + nsCOMPtr callerWebNav = + do_GetInterface(nsWWJSUtils::GetDynamicScriptGlobal(cx)); + + callerItem = do_QueryInterface(callerWebNav); + } + + nsCOMPtr parentItem; + GetWindowTreeItem(aParent, getter_AddRefs(parentItem)); + + if (!callerItem) { + callerItem = parentItem; + } + + if (parentItem) { + parentItem->FindItemWithName(name.get(), nsnull, callerItem, + getter_AddRefs(newDocShellItem)); } else - FindItemWithName(name.get(), getter_AddRefs(newDocShellItem)); + FindItemWithName(name.get(), callerItem, + getter_AddRefs(newDocShellItem)); } // no extant window? make a new one. @@ -1055,14 +1051,15 @@ nsWindowWatcher::GetWindowByName(const PRUnichar *aTargetName, docShellTreeItem = do_QueryInterface(webNav); if (docShellTreeItem) { - docShellTreeItem->FindItemWithName(aTargetName, nsnull, + // XXXbz sort out original requestor? + docShellTreeItem->FindItemWithName(aTargetName, nsnull, nsnull, getter_AddRefs(treeItem)); } } // Next, see if the TargetName exists in any window hierarchy if (!treeItem) { - FindItemWithName(aTargetName, getter_AddRefs(treeItem)); + FindItemWithName(aTargetName, nsnull, getter_AddRefs(treeItem)); } if (treeItem) { @@ -1382,19 +1379,18 @@ nsWindowWatcher::WinHasOption(const char *aOptions, const char *aName, necessarily return a failure method value. check aFoundItem. */ nsresult -nsWindowWatcher::FindItemWithName( - const PRUnichar* aName, - nsIDocShellTreeItem** aFoundItem) +nsWindowWatcher::FindItemWithName(const PRUnichar* aName, + nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** aFoundItem) { - PRBool more; - nsresult rv; - nsAutoString name(aName); - *aFoundItem = 0; /* special cases */ - if(name.IsEmpty()) + if(!aName || !*aName) return NS_OK; + + nsDependentString name(aName); + if(name.LowerCaseEqualsLiteral("_blank") || name.LowerCaseEqualsLiteral("_new")) return NS_OK; // _content will be handled by individual windows, below @@ -1404,7 +1400,9 @@ nsWindowWatcher::FindItemWithName( if (!windows) return NS_ERROR_FAILURE; - rv = NS_OK; + PRBool more; + nsresult rv = NS_OK; + do { windows->HasMoreElements(&more); if (!more) @@ -1417,7 +1415,8 @@ nsWindowWatcher::FindItemWithName( nsCOMPtr treeItem; GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem)); if (treeItem) { - rv = treeItem->FindItemWithName(aName, treeItem, aFoundItem); + rv = treeItem->FindItemWithName(aName, treeItem, aOriginalRequestor, + aFoundItem); if (NS_FAILED(rv) || *aFoundItem) break; } diff --git a/embedding/components/windowwatcher/src/nsWindowWatcher.h b/embedding/components/windowwatcher/src/nsWindowWatcher.h index 670693462cb..bbd87a32ae3 100644 --- a/embedding/components/windowwatcher/src/nsWindowWatcher.h +++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -85,6 +85,7 @@ private: nsresult RemoveWindow(nsWatcherWindowEntry *inInfo); nsresult FindItemWithName(const PRUnichar *aName, + nsIDocShellTreeItem *aOriginalRequestor, nsIDocShellTreeItem **aFoundItem); static JSContext *GetJSContextFromWindow(nsIDOMWindow *aWindow); diff --git a/mailnews/base/src/nsMessenger.cpp b/mailnews/base/src/nsMessenger.cpp index 655bae67447..560be291ee6 100644 --- a/mailnews/base/src/nsMessenger.cpp +++ b/mailnews/base/src/nsMessenger.cpp @@ -376,7 +376,7 @@ nsMessenger::SetWindow(nsIDOMWindowInternal *aWin, nsIMsgWindow *aMsgWindow) { nsCOMPtr childAsItem; nsresult rv = rootDocShellAsNode->FindChildWithName(NS_LITERAL_STRING("messagepane").get(), - PR_TRUE, PR_FALSE, nsnull, getter_AddRefs(childAsItem)); + PR_TRUE, PR_FALSE, nsnull, nsnull, getter_AddRefs(childAsItem)); mDocShell = do_QueryInterface(childAsItem); diff --git a/mailnews/base/src/nsMsgPrintEngine.cpp b/mailnews/base/src/nsMsgPrintEngine.cpp index 26007daad8d..bbdc3d5879a 100644 --- a/mailnews/base/src/nsMsgPrintEngine.cpp +++ b/mailnews/base/src/nsMsgPrintEngine.cpp @@ -282,8 +282,9 @@ nsMsgPrintEngine::SetWindow(nsIDOMWindowInternal *aWin) NS_ENSURE_TRUE(rootAsNode, NS_ERROR_FAILURE); nsCOMPtr childItem; - rootAsNode->FindChildWithName(NS_LITERAL_STRING("content").get(), PR_TRUE, PR_FALSE, nsnull, - getter_AddRefs(childItem)); + rootAsNode->FindChildWithName(NS_LITERAL_STRING("content").get(), PR_TRUE, + PR_FALSE, nsnull, nsnull, + getter_AddRefs(childItem)); mDocShell = do_QueryInterface(childItem); diff --git a/mailnews/base/src/nsMsgWindow.cpp b/mailnews/base/src/nsMsgWindow.cpp index 8f57c228202..2da5e9edfb1 100644 --- a/mailnews/base/src/nsMsgWindow.cpp +++ b/mailnews/base/src/nsMsgWindow.cpp @@ -123,8 +123,9 @@ void nsMsgWindow::GetMessageWindowDocShell(nsIDocShell ** aDocShell) nsCOMPtr msgDocShellItem; if(rootAsNode) - rootAsNode->FindChildWithName(NS_LITERAL_STRING("messagepane").get(), PR_TRUE, PR_FALSE, - nsnull, getter_AddRefs(msgDocShellItem)); + rootAsNode->FindChildWithName(NS_LITERAL_STRING("messagepane").get(), + PR_TRUE, PR_FALSE, nsnull, nsnull, + getter_AddRefs(msgDocShellItem)); docShell = do_QueryInterface(msgDocShellItem); // we don't own mMessageWindowDocShell so don't try to keep a reference to it! diff --git a/webshell/tests/viewer/nsWebBrowserChrome.cpp b/webshell/tests/viewer/nsWebBrowserChrome.cpp index 93075b447a2..34c1e7fdb37 100644 --- a/webshell/tests/viewer/nsWebBrowserChrome.cpp +++ b/webshell/tests/viewer/nsWebBrowserChrome.cpp @@ -174,38 +174,6 @@ NS_IMETHODIMP nsWebBrowserChrome::DestroyBrowserWindow() return mBrowserWindow->Destroy(); } -#if 0 -/* Just commenting out for now because it looks like somebody went to - a lot of work here. This method has been removed from nsIWebBrowserChrome - per the 5 Feb 01 API review, to be handled one level further down - in nsDocShellTreeOwner. -*/ -NS_IMETHODIMP nsWebBrowserChrome::FindNamedBrowserItem(const PRUnichar* aName, - nsIDocShellTreeItem** aBrowserItem) -{ - NS_ENSURE_ARG_POINTER(aBrowserItem); - *aBrowserItem = nsnull; - - PRInt32 i = 0; - PRInt32 n = mBrowserWindow->gBrowsers.Count(); - - nsString aNameStr(aName); - - for (i = 0; i < n; i++) - { - nsBrowserWindow* bw = (nsBrowserWindow*)mBrowserWindow->gBrowsers.ElementAt(i); - nsCOMPtr docShellAsItem(do_QueryInterface(bw->mWebBrowser)); - NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE); - - docShellAsItem->FindItemWithName(aName, NS_STATIC_CAST(nsIWebBrowserChrome*, this), aBrowserItem); - - if(!*aBrowserItem) - return NS_OK; - } - return NS_OK; -} -#endif - NS_IMETHODIMP nsWebBrowserChrome::SizeBrowserTo(PRInt32 aCX, PRInt32 aCY) { mSizeSet = PR_TRUE; diff --git a/xpfe/appshell/src/nsChromeTreeOwner.cpp b/xpfe/appshell/src/nsChromeTreeOwner.cpp index bf0e5cb3bbb..fb0bbf6e23e 100644 --- a/xpfe/appshell/src/nsChromeTreeOwner.cpp +++ b/xpfe/appshell/src/nsChromeTreeOwner.cpp @@ -165,19 +165,21 @@ NS_IMETHODIMP nsChromeTreeOwner::GetInterface(const nsIID& aIID, void** aSink) //***************************************************************************** NS_IMETHODIMP nsChromeTreeOwner::FindItemWithName(const PRUnichar* aName, - nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem** aFoundItem) + nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** aFoundItem) { NS_ENSURE_ARG_POINTER(aFoundItem); *aFoundItem = nsnull; - nsAutoString name(aName); - PRBool fIs_Content = PR_FALSE; /* Special Cases */ - if(name.IsEmpty()) + if(!aName || !*aName) return NS_OK; + + nsDependentString name(aName); + if(name.LowerCaseEqualsLiteral("_blank")) return NS_OK; // _main is an IE target which should be case-insensitive but isn't @@ -229,7 +231,8 @@ NS_IMETHODIMP nsChromeTreeOwner::FindItemWithName(const PRUnichar* aName, shellAsTreeItem->GetTreeOwner(getter_AddRefs(shellOwner)); nsCOMPtr shellOwnerSupports(do_QueryInterface(shellOwner)); - shellAsTreeItem->FindItemWithName(aName, shellOwnerSupports, aFoundItem); + shellAsTreeItem->FindItemWithName(aName, shellOwnerSupports, + aOriginalRequestor, aFoundItem); } } if(*aFoundItem) diff --git a/xpfe/appshell/src/nsContentTreeOwner.cpp b/xpfe/appshell/src/nsContentTreeOwner.cpp index b79d1c095d9..66f3640d8c8 100644 --- a/xpfe/appshell/src/nsContentTreeOwner.cpp +++ b/xpfe/appshell/src/nsContentTreeOwner.cpp @@ -167,19 +167,21 @@ NS_IMETHODIMP nsContentTreeOwner::GetInterface(const nsIID& aIID, void** aSink) //***************************************************************************** NS_IMETHODIMP nsContentTreeOwner::FindItemWithName(const PRUnichar* aName, - nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem** aFoundItem) + nsIDocShellTreeItem* aRequestor, nsIDocShellTreeItem* aOriginalRequestor, + nsIDocShellTreeItem** aFoundItem) { NS_ENSURE_ARG_POINTER(aFoundItem); *aFoundItem = nsnull; - nsAutoString name(aName); - PRBool fIs_Content = PR_FALSE; /* Special Cases */ - if(name.IsEmpty()) + if(!aName || !*aName) return NS_OK; + + nsDependentString name(aName); + if(name.LowerCaseEqualsLiteral("_blank")) return NS_OK; // _main is an IE target which should be case-insensitive but isn't @@ -227,7 +229,8 @@ NS_IMETHODIMP nsContentTreeOwner::FindItemWithName(const PRUnichar* aName, shellAsTreeItem->GetTreeOwner(getter_AddRefs(shellOwner)); nsCOMPtr shellOwnerSupports(do_QueryInterface(shellOwner)); - shellAsTreeItem->FindItemWithName(aName, shellOwnerSupports, aFoundItem); + shellAsTreeItem->FindItemWithName(aName, shellOwnerSupports, + aOriginalRequestor, aFoundItem); } if(*aFoundItem) return NS_OK; diff --git a/xpfe/appshell/src/nsWebShellWindow.cpp b/xpfe/appshell/src/nsWebShellWindow.cpp index 98dbcf927b6..6b622b6bfc6 100644 --- a/xpfe/appshell/src/nsWebShellWindow.cpp +++ b/xpfe/appshell/src/nsWebShellWindow.cpp @@ -1329,7 +1329,7 @@ nsCOMPtr nsWebShellWindow::GetNamedDOMDoc(const nsAString & aDoc nsCOMPtr docShellAsItem; nsCOMPtr docShellAsNode(do_QueryInterface(mDocShell)); docShellAsNode->FindChildWithName(PromiseFlatString(aDocShellName).get(), - PR_TRUE, PR_FALSE, nsnull, getter_AddRefs(docShellAsItem)); + PR_TRUE, PR_FALSE, nsnull, nsnull, getter_AddRefs(docShellAsItem)); childDocShell = do_QueryInterface(docShellAsItem); if (!childDocShell) return domDoc;