Move the "force into tab" code out of Gecko and allow embeddors to do the same

sort of thing.  Bug 323810, r=bsmedberg, sr=jst
This commit is contained in:
bzbarsky%mit.edu 2006-02-07 20:46:39 +00:00
Родитель e9a0b35561
Коммит 2dbdd7da0d
19 изменённых файлов: 936 добавлений и 693 удалений

Просмотреть файл

@ -62,7 +62,6 @@
#include "nsIPresShell.h"
#include "nsGUIEvent.h"
#include "nsPresContext.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDOMCSSStyleDeclaration.h"
#include "nsIDOMViewCSS.h"
#include "nsIXBLService.h"
@ -221,17 +220,10 @@ nsXMLElement::MaybeTriggerAutoLink(nsIDocShell *aShell)
// XXX Should probably do this using atoms
if (value.EqualsLiteral("new")) {
if (nsContentUtils::GetBoolPref("dom.disable_open_during_load")) {
// disabling open during load
return NS_OK;
}
if (nsContentUtils::GetIntPref("browser.link.open_newwindow",
nsIBrowserDOMWindow::OPEN_NEWWINDOW) ==
nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
verb = eLinkVerb_New;
}
// We should just act like an HTML link with target="_blank" and if
// someone diverts or blocks those, that's fine with us. We don't
// care.
verb = eLinkVerb_New;
} else if (value.EqualsLiteral("replace")) {
// We want to actually stop processing the current document now.
// We do this by returning the correct value so that the one
@ -307,11 +299,7 @@ nsXMLElement::HandleDOMEvent(nsPresContext* aPresContext,
// XXX Should probably do this using atoms
if (show.EqualsLiteral("new")) {
if (nsContentUtils::GetIntPref("browser.link.open_newwindow",
nsIBrowserDOMWindow::OPEN_NEWWINDOW) ==
nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
verb = eLinkVerb_New;
}
verb = eLinkVerb_New;
} else if (show.EqualsLiteral("replace")) {
verb = eLinkVerb_Replace;
} else if (show.EqualsLiteral("embed")) {

Просмотреть файл

@ -1097,160 +1097,6 @@ nsDocShell::ValidateOrigin(nsIDocShellTreeItem* aOriginTreeItem,
documentDomainSet);
}
nsresult nsDocShell::FindTarget(const PRUnichar *aWindowTarget,
PRBool *aIsNewWindow,
nsIDocShell **aResult)
{
nsresult rv = NS_OK;
*aResult = nsnull;
*aIsNewWindow = PR_FALSE;
// Try to locate the target window...
nsCOMPtr<nsIDocShellTreeItem> treeItem;
FindItemWithName(aWindowTarget, nsnull, this, getter_AddRefs(treeItem));
PRInt32 linkPref = nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW;
// 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
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 (!treeItem)
{
/// XXXbz this should really happen in FindItemWithName too, I think...
nsCOMPtr<nsIDOMWindow> newWindow;
nsCOMPtr<nsIDOMWindowInternal> parentWindow;
// This DocShell is the parent window
parentWindow = do_GetInterface(GetAsSupports(this));
if (!parentWindow) {
NS_ASSERTION(0, "Can't get nsIDOMWindowInternal from nsDocShell!");
return NS_ERROR_FAILURE;
}
if (linkPref == nsIBrowserDOMWindow::OPEN_NEWTAB) {
// is it a popup?
PRBool allowTab = PR_TRUE;
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(mScriptGlobal);
if (pWindow) {
// skip the window search-by-name of GetOpenAllow
// by using _self. we don't care about that at this point.
OpenAllowValue allow = pWindow->GetOpenAllow(
NS_LITERAL_STRING("_self"));
if (allow == allowNot || allow == allowSelf)
allowTab = PR_FALSE;
}
// try to get our tab-opening interface
if (allowTab) {
nsCOMPtr<nsIBrowserDOMWindow> bwin;
nsCOMPtr<nsIDocShellTreeItem> rootItem;
GetRootTreeItem(getter_AddRefs(rootItem));
nsCOMPtr<nsIDOMWindow> rootWin(do_GetInterface(rootItem));
nsCOMPtr<nsIDOMChromeWindow> chromeWin(
do_QueryInterface(rootWin));
if (chromeWin)
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
// open a new tab
if (bwin) {
rv = bwin->OpenURI(0, 0, nsIBrowserDOMWindow::OPEN_NEWTAB,
nsIBrowserDOMWindow::OPEN_NEW,
getter_AddRefs(newWindow));
nsCOMPtr<nsPIDOMWindow> newPIWindow =
do_GetInterface(newWindow);
if (newPIWindow)
newPIWindow->SetOpenerWindow(parentWindow);
}
}
// else fall through to the normal Open method, from which
// the appropriate measures will be taken when the popup fails
}
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...
nsCOMPtr<nsPIDOMWindow> piwindow(do_QueryInterface(newWindow));
// *aResult will be AddRef()'ed below...
*aResult = piwindow->GetDocShell();
}
// If all went well, indicate that a new window has been created.
if (*aResult) {
NS_ADDREF(*aResult);
*aIsNewWindow = PR_TRUE;
// if we just open a new window for this link, charset from current docshell
// should be kept, as what we did in js openNewWindowWith(url)
nsCOMPtr<nsIMarkupDocumentViewer> muCV, target_muCV;
nsCOMPtr<nsIContentViewer> cv, target_cv;
this->GetContentViewer(getter_AddRefs(cv));
(*aResult)->GetContentViewer(getter_AddRefs(target_cv));
if (cv && target_cv) {
muCV = do_QueryInterface(cv);
target_muCV = do_QueryInterface(target_cv);
if (muCV && target_muCV) {
nsCAutoString defaultCharset;
nsCAutoString prevDocCharset;
rv = muCV->GetDefaultCharacterSet(defaultCharset);
if(NS_SUCCEEDED(rv)) {
target_muCV->SetDefaultCharacterSet(defaultCharset);
}
rv = muCV->GetPrevDocCharacterSet(prevDocCharset);
if(NS_SUCCEEDED(rv)) {
target_muCV->SetPrevDocCharacterSet(prevDocCharset);
}
}
}
}
return rv;
}
else
{
if (treeItem)
{
NS_ASSERTION(!*aResult, "aResult should be null if treeItem is set!");
treeItem->QueryInterface(NS_GET_IID(nsIDocShell), (void **)aResult);
}
else
{
NS_IF_ADDREF(*aResult);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetEldestPresContext(nsPresContext** aPresContext)
{
@ -6336,68 +6182,38 @@ nsDocShell::InternalLoad(nsIURI * aURI,
// load to it...
//
if (aWindowTarget && *aWindowTarget) {
PRBool bIsNewWindow;
nsCOMPtr<nsIDocShell> targetDocShell;
nsAutoString name(aWindowTarget);
//
// This is a hack to prevent top-level windows from ever being
// 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);
if (linkPref == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
PRBool bIsChromeOrResource = PR_FALSE;
if (mCurrentURI)
mCurrentURI->SchemeIs("chrome", &bIsChromeOrResource);
if (!bIsChromeOrResource) {
aURI->SchemeIs("chrome", &bIsChromeOrResource);
if (!bIsChromeOrResource) {
aURI->SchemeIs("resource", &bIsChromeOrResource);
}
}
if (!bIsChromeOrResource) {
if (name.LowerCaseEqualsLiteral("_blank") ||
name.LowerCaseEqualsLiteral("_new")) {
name.AssignLiteral("_top");
}
// _main is an IE target which should be case-insensitive but isn't
// see bug 217886 for details
else if (!name.LowerCaseEqualsLiteral("_parent") &&
!name.LowerCaseEqualsLiteral("_self") &&
!name.LowerCaseEqualsLiteral("_content") &&
!name.EqualsLiteral("_main")) {
nsCOMPtr<nsIDocShellTreeItem> targetTreeItem;
FindItemWithName(name.get(),
nsnull,
this,
getter_AddRefs(targetTreeItem));
if (targetTreeItem)
targetDocShell = do_QueryInterface(targetTreeItem);
else
name.AssignLiteral("_top");
}
}
}
//
// Locate the target DocShell.
// This may involve creating a new toplevel window - if necessary.
//
if (!targetDocShell) {
rv = FindTarget(name.get(), &bIsNewWindow,
getter_AddRefs(targetDocShell));
}
nsCOMPtr<nsIDocShellTreeItem> targetItem;
FindItemWithName(aWindowTarget, nsnull, this,
getter_AddRefs(targetItem));
NS_ASSERTION(targetDocShell, "No Target docshell could be found!");
nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(targetItem);
PRBool isNewWindow = PR_FALSE;
if (!targetDocShell) {
nsCOMPtr<nsIDOMWindowInternal> win =
do_GetInterface(GetAsSupports(this));
NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
isNewWindow = PR_TRUE;
nsDependentString name(aWindowTarget);
nsCOMPtr<nsIDOMWindow> newWin;
rv = win->Open(EmptyString(), // URL to load
name, // window name
EmptyString(), // Features
getter_AddRefs(newWin));
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
targetDocShell = do_QueryInterface(webNav);
}
//
// Transfer the load to the target DocShell... Pass nsnull as the
// window target name from to prevent recursive retargeting!
//
if (targetDocShell) {
if (NS_SUCCEEDED(rv) && targetDocShell) {
rv = targetDocShell->InternalLoad(aURI,
aReferrer,
owner,
@ -6412,7 +6228,8 @@ nsDocShell::InternalLoad(nsIURI * aURI,
aDocShell,
aRequest);
if (rv == NS_ERROR_NO_CONTENT) {
if (bIsNewWindow) {
// XXXbz except we never reach this code!
if (isNewWindow) {
//
// At this point, a new window has been created, but the
// URI did not have any data associated with it...
@ -6420,18 +6237,10 @@ nsDocShell::InternalLoad(nsIURI * aURI,
// So, the best we can do, is to tear down the new window
// that was just created!
//
nsCOMPtr<nsIDocShellTreeItem> treeItem;
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
treeItem = do_QueryInterface(targetDocShell);
treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
if (treeOwner) {
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin;
treeOwnerAsWin = do_QueryInterface(treeOwner);
if (treeOwnerAsWin) {
treeOwnerAsWin->Destroy();
}
nsCOMPtr<nsIDOMWindowInternal> domWin =
do_GetInterface(targetDocShell);
if (domWin) {
domWin->Close();
}
}
//
@ -6442,12 +6251,16 @@ nsDocShell::InternalLoad(nsIURI * aURI,
//
rv = NS_OK;
}
else if (bIsNewWindow) {
else if (isNewWindow) {
// XXX: Once new windows are created hidden, the new
// window will need to be made visible... For now,
// do nothing.
}
}
// Else we ran out of memory, or were a popup and got blocked,
// or something.
return rv;
}

Просмотреть файл

@ -439,10 +439,6 @@ protected:
return t_sec;
}
nsresult FindTarget(const PRUnichar *aTargetName,
PRBool *aIsNewWindow,
nsIDocShell **aResult);
PRBool IsFrame();
//

Просмотреть файл

@ -768,6 +768,7 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent,
nsresult rv;
switch(aVerb) {
case eLinkVerb_New:
NS_ASSERTION(target.IsEmpty(), "Losing window name information");
target.AssignLiteral("_blank");
// Fall into replace case
case eLinkVerb_Undefined:

Просмотреть файл

@ -66,12 +66,11 @@ enum PopupControlState {
enum OpenAllowValue {
allowNot = 0, // the window opening is denied
allowNoAbuse, // allowed: not a popup
allowSelf, // allowed: it's the same window (_self, _top, et.al.)
allowExtant, // allowed: an already open window
allowWhitelisted // allowed: it's whitelisted or popup blocking is disabled
};
class nsIDocShell;
class nsIDocShellTreeItem;
class nsIFocusController;
class nsIDocument;
struct nsTimeout;
@ -258,6 +257,11 @@ public:
PRBool aForce) const = 0;
virtual void PopPopupControlState(PopupControlState state) const = 0;
virtual PopupControlState GetPopupControlState() const = 0;
// GetOpenAllow must not be called on a window that no longer has a docshell
// This function is deprecated. It will assume that there is no existing
// window with name aName for purposes of its answer. Expect this function
// to get removed soon!
virtual OpenAllowValue GetOpenAllow(const nsAString &aName) = 0;
// Returns an object containing the window's state. This also suspends

Просмотреть файл

@ -44,26 +44,50 @@ interface nsIURI;
/**
* The C++ source has access to the browser script source through
* nsIBrowserWindow. It is intended to be attached to the chrome
* DOMWindow of a browser window. A DOMWindow that does not happen to
* be a browser chrome window will simply have no access to any such
* nsIBrowserDOMWindow. It is intended to be attached to the chrome DOMWindow
* of a toplevel browser window (a XUL window). A DOMWindow that does not
* happen to be a browser chrome window will simply have no access to any such
* interface.
*/
interface nsIBrowserDOMWindow : nsISupports
{
/**
* values for openURI's aWhere parameter
* Values for openURI's aWhere parameter.
*/
/**
* Do whatever the default is based on application state, user preferences,
* and the value of the aContext parameter to openURI.
*/
const short OPEN_DEFAULTWINDOW = 0;
/**
* Open in the "current window". If aOpener is provided, this should be the
* top window in aOpener's window hierarchy, but exact behavior is
* application-dependent. If aOpener is not provided, it's up to the
* application to decide what constitutes a "current window".
*/
const short OPEN_CURRENTWINDOW = 1;
/**
* Open in a new window.
*/
const short OPEN_NEWWINDOW = 2;
/**
* Open in a new content tab in the toplevel browser window corresponding to
* this nsIBrowserDOMWindow.
*/
const short OPEN_NEWTAB = 3;
/**
* values for openURI's aContext parameter
* Values for openURI's aContext parameter. These affect the behavior of
* OPEN_DEFAULTWINDOW.
*/
const short OPEN_EXTERNAL = 1; // external link
const short OPEN_NEW = 2; // internal open new window
/**
* external link (load request from another application, xremote, etc).
*/
const short OPEN_EXTERNAL = 1;
/**
* internal open new window
*/
const short OPEN_NEW = 2;
/**
* Load a URI
@ -80,7 +104,7 @@ interface nsIBrowserDOMWindow : nsISupports
/**
* @param aWindow the window to test.
* @return whether the window is the main content window for any
* currently open tab.
* currently open tab in this toplevel browser window.
*/
boolean isTabContentWindow(in nsIDOMWindow aWindow);
};

Просмотреть файл

@ -77,8 +77,16 @@ interface nsIDOMJSWindow : nsISupports
*/
DOMString prompt();
// This is the script version of nsIDOMWindowInternal::open() that
// takes 3 optional arguments
/**
* These are the scriptable versions of nsIDOMWindowInternal::open() and
* nsIDOMWindowInternal::openDialog() that take 3 optional arguments. Unlike
* the nsIDOMWindowInternal methods, these methods assume that they are
* called from JavaScript and hence will look on the JS context stack to
* determine the caller and hence correct security context for doing their
* search for an existing named window. Also, these methods will set the
* default charset on the newly opened window based on the current document
* charset in the caller.
*/
nsIDOMWindow open();
nsIDOMWindow openDialog();

Просмотреть файл

@ -150,6 +150,13 @@ interface nsIDOMWindowInternal : nsIDOMWindow2
//[noscript] long setInterval(/* in function,
// in unsigned long timeout */);
/**
* Open a new window with this one as the parent. This method will
* NOT examine the JS stack for purposes of determining a caller.
* This window will be used for security checks during the search by
* name and the default character set on the newly opened window
* will just be the default character set of this window.
*/
[noscript] nsIDOMWindow open(in DOMString url, in DOMString name,
in DOMString options);
// This method works like open except that aExtraArgument gets

Просмотреть файл

@ -435,7 +435,7 @@ nsGlobalWindow::CleanUp()
}
mChromeEventHandler = nsnull; // Forces Release
if (IsPopupSpamWindow()) {
if (IsOuterWindow() && IsPopupSpamWindow()) {
SetPopupSpamWindow(PR_FALSE);
--gOpenPopupSpamCount;
}
@ -2960,43 +2960,29 @@ GetCallerDocShellTreeItem()
}
PRBool
nsGlobalWindow::WindowExists(const nsAString& aName)
nsGlobalWindow::WindowExists(const nsAString& aName,
PRBool aLookForCallerOnJSStack)
{
nsCOMPtr<nsIDocShellTreeItem> caller = GetCallerDocShellTreeItem();
PRBool foundWindow = PR_FALSE;
NS_PRECONDITION(IsOuterWindow(), "Must be outer window");
NS_PRECONDITION(mDocShell, "Must have docshell");
nsCOMPtr<nsIDocShellTreeItem> caller;
if (aLookForCallerOnJSStack) {
caller = GetCallerDocShellTreeItem();
}
nsCOMPtr<nsIDocShellTreeItem> docShell = do_QueryInterface(mDocShell);
NS_ASSERTION(docShell,
"Docshell doesn't implement nsIDocShellTreeItem?");
if (!caller) {
// If we can't reach a caller, try to use our own docshell
caller = do_QueryInterface(GetDocShell());
caller = docShell;
}
nsCOMPtr<nsIDocShellTreeItem> docShell =
do_QueryInterface(GetDocShell());
if (docShell) {
nsCOMPtr<nsIDocShellTreeItem> 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<nsIWindowWatcher> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID);
if (wwatch) {
nsCOMPtr<nsIDOMWindow> namedWindow;
wwatch->GetWindowByName(PromiseFlatString(aName).get(), nsnull,
getter_AddRefs(namedWindow));
foundWindow = !!namedWindow;
}
}
return foundWindow;
nsCOMPtr<nsIDocShellTreeItem> namedItem;
docShell->FindItemWithName(PromiseFlatString(aName).get(), nsnull, caller,
getter_AddRefs(namedItem));
return namedItem != nsnull;
}
already_AddRefed<nsIWidget>
@ -4072,15 +4058,16 @@ nsGlobalWindow::CheckForAbusePoint()
{
FORWARD_TO_OUTER(CheckForAbusePoint, (), openAbused);
NS_ASSERTION(mDocShell, "Must have docshell");
nsCOMPtr<nsIDocShellTreeItem> item(do_QueryInterface(mDocShell));
if (item) {
PRInt32 type = nsIDocShellTreeItem::typeChrome;
NS_ASSERTION(item, "Docshell doesn't implenent nsIDocShellTreeItem?");
item->GetItemType(&type);
if (type != nsIDocShellTreeItem::typeContent)
return openAllowed;
}
PRInt32 type = nsIDocShellTreeItem::typeChrome;
item->GetItemType(&type);
if (type != nsIDocShellTreeItem::typeContent)
return openAllowed;
// level of abuse we've detected, initialized to the current popup
// state
@ -4097,37 +4084,21 @@ nsGlobalWindow::CheckForAbusePoint()
}
/* Allow or deny a window open based on whether popups are suppressed.
A popup generally will be allowed if it's from a white-listed domain,
or if its target is an extant window.
A popup generally will be allowed if it's from a white-listed domain.
Returns a value from the CheckOpenAllow enum. */
OpenAllowValue
nsGlobalWindow::CheckOpenAllow(PopupControlState aAbuseLevel,
const nsAString &aName)
nsGlobalWindow::CheckOpenAllow(PopupControlState aAbuseLevel)
{
NS_PRECONDITION(GetDocShell(), "Must have docshell");
OpenAllowValue allowWindow = allowNoAbuse; // (also used for openControlled)
if (aAbuseLevel >= openAbused) {
allowWindow = allowNot;
// However it might still not be blocked.
if (aAbuseLevel == openAbused && !IsPopupBlocked(mDocument))
if (aAbuseLevel == openAbused && !IsPopupBlocked(mDocument)) {
allowWindow = allowWhitelisted;
else {
// Special case items that don't actually open new windows.
if (!aName.IsEmpty()) {
// _main is an IE target which should be case-insensitive but isn't
// see bug 217886 for details
if (aName.LowerCaseEqualsLiteral("_top") ||
aName.LowerCaseEqualsLiteral("_self") ||
aName.LowerCaseEqualsLiteral("_content") ||
aName.EqualsLiteral("_main"))
allowWindow = allowSelf;
else {
if (WindowExists(aName)) {
allowWindow = allowExtant;
}
}
}
}
}
@ -4137,7 +4108,8 @@ nsGlobalWindow::CheckOpenAllow(PopupControlState aAbuseLevel,
OpenAllowValue
nsGlobalWindow::GetOpenAllow(const nsAString &aName)
{
return CheckOpenAllow(CheckForAbusePoint(), aName);
NS_ENSURE_TRUE(GetDocShell(), allowNot);
return CheckOpenAllow(CheckForAbusePoint());
}
/* If a window open is blocked, fire the appropriate DOM events.
@ -4211,29 +4183,12 @@ NS_IMETHODIMP
nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, nsIDOMWindow **_retval)
{
nsresult rv;
PopupControlState abuseLevel = CheckForAbusePoint();
OpenAllowValue allowReason = CheckOpenAllow(abuseLevel, aName);
if (allowReason == allowNot) {
FireAbuseEvents(PR_TRUE, PR_FALSE, aUrl, aName, aOptions);
return NS_ERROR_FAILURE; // unlike the public Open method, return an error
}
rv = OpenInternal(aUrl, aName, aOptions, PR_FALSE, nsnull, 0, nsnull,
_retval);
if (NS_SUCCEEDED(rv)) {
if (abuseLevel >= openControlled && allowReason != allowSelf) {
nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow *, *_retval);
if (!opened->IsPopupSpamWindow()) {
opened->SetPopupSpamWindow(PR_TRUE);
++gOpenPopupSpamCount;
}
}
if (abuseLevel >= openAbused)
FireAbuseEvents(PR_FALSE, PR_TRUE, aUrl, aName, aOptions);
}
return rv;
return OpenInternal(aUrl, aName, aOptions,
PR_FALSE, // aDialog
PR_TRUE, // aCalledNoScript
PR_FALSE, // aDoJSFixups
nsnull, 0, nsnull, // No args
_retval);
}
NS_IMETHODIMP
@ -4275,51 +4230,12 @@ nsGlobalWindow::Open(nsIDOMWindow **_retval)
}
}
PopupControlState abuseLevel = CheckForAbusePoint();
OpenAllowValue allowReason = CheckOpenAllow(abuseLevel, name);
if (allowReason == allowNot) {
FireAbuseEvents(PR_TRUE, PR_FALSE, url, name, options);
return NS_OK; // don't open the window, but also don't throw a JS exception
}
rv = OpenInternal(url, name, options, PR_FALSE, nsnull, 0, nsnull, _retval);
nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*_retval));
if (NS_SUCCEEDED(rv)) {
if (!chrome_win) {
// A new non-chrome window was created from a call to
// window.open() from JavaScript, make sure there's a document in
// the new window. We do this by simply asking the new window for
// its document, this will synchronously create an empty document
// if there is no document in the window.
#ifdef DEBUG_jst
{
nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*_retval));
nsIDOMDocument *temp = pidomwin->GetExtantDocument();
NS_ASSERTION(temp, "No document in new window!!!");
}
#endif
nsCOMPtr<nsIDOMDocument> doc;
(*_retval)->GetDocument(getter_AddRefs(doc));
}
if (abuseLevel >= openControlled && allowReason != allowSelf) {
nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow*, *_retval);
if (!opened->IsPopupSpamWindow()) {
opened->SetPopupSpamWindow(PR_TRUE);
++gOpenPopupSpamCount;
}
}
if (abuseLevel >= openAbused)
FireAbuseEvents(PR_FALSE, PR_TRUE, url, name, options);
}
return rv;
return OpenInternal(url, name, options,
PR_FALSE, // aDialog
PR_FALSE, // aCalledNoScript
PR_TRUE, // aDoJSFixups
nsnull, 0, nsnull, // No args
_retval);
}
// like Open, but attaches to the new window any extra parameters past
@ -4329,8 +4245,12 @@ nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions,
nsISupports* aExtraArgument, nsIDOMWindow** _retval)
{
return OpenInternal(aUrl, aName, aOptions, PR_TRUE, nsnull, 0,
aExtraArgument, _retval);
return OpenInternal(aUrl, aName, aOptions,
PR_TRUE, // aDialog
PR_TRUE, // aCalledNoScript
PR_FALSE, // aDoJSFixups
nsnull, 0, aExtraArgument, // Arguments
_retval);
}
NS_IMETHODIMP
@ -4373,7 +4293,11 @@ nsGlobalWindow::OpenDialog(nsIDOMWindow** _retval)
}
}
return OpenInternal(url, name, options, PR_TRUE, argv, argc, nsnull,
return OpenInternal(url, name, options,
PR_TRUE, // aDialog
PR_FALSE, // aCalledNoScript
PR_FALSE, // aDoJSFixups
argv, argc, nsnull, // Arguments
_retval);
}
@ -5657,17 +5581,28 @@ nsGlobalWindow::GetParentInternal()
return parentInternal;
}
NS_IMETHODIMP
nsresult
nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
const nsAString& aOptions, PRBool aDialog,
PRBool aCalledNoScript, PRBool aDoJSFixups,
jsval *argv, PRUint32 argc,
nsISupports *aExtraArgument,
nsIDOMWindow **aReturn)
{
FORWARD_TO_OUTER(OpenInternal, (aUrl, aName, aOptions, aDialog, argv, argc,
aExtraArgument, aReturn),
FORWARD_TO_OUTER(OpenInternal, (aUrl, aName, aOptions, aDialog,
aCalledNoScript, aDoJSFixups,
argv, argc, aExtraArgument, aReturn),
NS_ERROR_NOT_INITIALIZED);
NS_PRECONDITION(!aExtraArgument || (!argv && argc == 0),
"Can't pass in arguments both ways");
NS_PRECONDITION(!aCalledNoScript || (!argv && argc == 0),
"Can't pass JS args when called via the noscript methods");
NS_PRECONDITION(!aDoJSFixups || !aCalledNoScript,
"JS fixups should not be done when called noscript");
*aReturn = nsnull;
nsCOMPtr<nsIWebBrowserChrome> chrome;
GetWebBrowserChrome(getter_AddRefs(chrome));
if (!chrome) {
@ -5675,11 +5610,29 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
// -- see nsIWindowWatcher.idl
return NS_ERROR_NOT_AVAILABLE;
}
nsXPIDLCString url;
nsresult rv = NS_OK;
*aReturn = nsnull;
NS_ASSERTION(mDocShell, "Must have docshell here");
const PRBool checkForPopup =
!aDialog && !WindowExists(aName, !aCalledNoScript);
// These next two variables are only accessed when checkForPopup is true
PopupControlState abuseLevel;
OpenAllowValue allowReason;
if (checkForPopup) {
abuseLevel = CheckForAbusePoint();
allowReason = CheckOpenAllow(abuseLevel);
if (allowReason == allowNot) {
FireAbuseEvents(PR_TRUE, PR_FALSE, aUrl, aName, aOptions);
return aDoJSFixups ? NS_OK : NS_ERROR_FAILURE;
}
}
// Note: it's very important that this be an nsXPIDLCString, since we want
// .get() on it to return nsnull until we write stuff to it. The window
// watcher expects a null URL string if there is no URL to load.
nsXPIDLCString url;
nsresult rv = NS_OK;
if (!aUrl.IsEmpty()) {
// fix bug 35076
@ -5708,150 +5661,71 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
if (NS_FAILED(rv))
return rv;
// determine whether we must divert the open window to a new tab.
PRBool divertOpen = !WindowExists(aName);
// also check what the prefs prescribe?
// XXXbz this duplicates docshell code. Need to consolidate.
PRInt32 containerPref = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
nsCOMPtr<nsIURI> tabURI;
if (!aUrl.IsEmpty()) {
PRBool whoCares;
BuildURIfromBase(url.get(), getter_AddRefs(tabURI), &whoCares, 0);
}
if (divertOpen) { // no such named window
divertOpen = PR_FALSE; // more tests to pass:
if (!aExtraArgument) {
nsCOMPtr<nsIDOMChromeWindow> thisChrome =
do_QueryInterface(NS_STATIC_CAST(nsIDOMWindow *, this));
PRBool chromeTab = PR_FALSE;
if (tabURI)
tabURI->SchemeIs("chrome", &chromeTab);
if (!thisChrome && !chromeTab) {
containerPref =
nsContentUtils::GetIntPref("browser.link.open_newwindow",
nsIBrowserDOMWindow::OPEN_NEWWINDOW);
PRInt32 restrictionPref = nsContentUtils::GetIntPref(
"browser.link.open_newwindow.restriction");
/* The restriction pref is a power-user's fine-tuning pref. values:
0: no restrictions - divert everything
1: don't divert window.open at all
2: don't divert window.open with features */
if (containerPref == nsIBrowserDOMWindow::OPEN_NEWTAB ||
containerPref == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
divertOpen = restrictionPref != 1;
if (divertOpen && !aOptions.IsEmpty() && restrictionPref == 2)
divertOpen = PR_FALSE;
}
}
}
}
nsCOMPtr<nsIDOMWindow> domReturn;
// divert the window.open into a new tab or into this window, if required
nsCOMPtr<nsIWindowWatcher> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_TRUE(wwatch, rv);
if (divertOpen) {
if (containerPref == nsIBrowserDOMWindow::OPEN_NEWTAB ||
!aUrl.IsEmpty()) {
#ifdef DEBUG
printf("divert window.open to new tab\n");
#endif
// get nsIBrowserDOMWindow interface
NS_ConvertUTF16toUTF8 options(aOptions);
NS_ConvertUTF16toUTF8 name(aName);
nsCOMPtr<nsIBrowserDOMWindow> bwin;
const char *options_ptr = aOptions.IsEmpty() ? nsnull : options.get();
const char *name_ptr = aName.IsEmpty() ? nsnull : name.get();
nsCOMPtr<nsIDocShellTreeItem> docItem(do_QueryInterface(mDocShell));
if (docItem) {
nsCOMPtr<nsIDocShellTreeItem> rootItem;
docItem->GetRootTreeItem(getter_AddRefs(rootItem));
nsCOMPtr<nsIDOMWindow> rootWin(do_GetInterface(rootItem));
nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
if (chromeWin)
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
}
{
// Reset popup state while opening a window to prevent the
// current state from being active the whole time a modal
// dialog is open.
nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE);
// open new tab
if (bwin) {
// open the tab with the URL
// discard features (meaningless in this case)
bwin->OpenURI(tabURI, this,
containerPref, nsIBrowserDOMWindow::OPEN_NEW,
getter_AddRefs(domReturn));
nsCOMPtr<nsPIDOMWindow> domWin(do_GetInterface(domReturn));
if (domWin) {
domWin->SetOpenerWindow(this);
}
}
if (!aCalledNoScript) {
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
NS_ASSERTION(pwwatch,
"Unable to open windows from JS because window watcher "
"is broken");
NS_ENSURE_TRUE(pwwatch, NS_ERROR_UNEXPECTED);
PRUint32 extraArgc = argc >= 3 ? argc - 3 : 0;
rv = pwwatch->OpenWindowJS(this, url.get(), name_ptr, options_ptr,
aDialog, extraArgc, argv + 3,
getter_AddRefs(domReturn));
} else {
#ifdef DEBUG
printf("divert window.open to current window\n");
#endif
GetTop(getter_AddRefs(domReturn));
}
// Push a null JSContext here so that the window watcher won't screw us
// up. We do NOT want this case looking at the JS context on the stack
// when searching. Compare comments on
// nsIDOMWindowInternal::OpenWindow and nsIWindowWatcher::OpenWindow.
nsCOMPtr<nsIJSContextStack> stack =
do_GetService(sJSStackContractID);
if (domReturn && !aName.LowerCaseEqualsLiteral("_blank") &&
!aName.LowerCaseEqualsLiteral("_new"))
domReturn->SetName(aName);
}
if (stack) {
rv = stack->Push(nsnull);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = wwatch->OpenWindow(this, url.get(), name_ptr, options_ptr,
aExtraArgument, getter_AddRefs(domReturn));
// lacking specific instructions, or just as an error fallback,
// open a new window.
if (!domReturn) {
nsCOMPtr<nsIWindowWatcher> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
if (wwatch) {
NS_ConvertUTF16toUTF8 options(aOptions);
NS_ConvertUTF16toUTF8 name(aName);
const char *options_ptr = aOptions.IsEmpty() ? nsnull : options.get();
const char *name_ptr = aName.IsEmpty() ? nsnull : name.get();
{
// Reset popup state while opening a window to prevent the
// current state from being active the whole time a modal
// dialog is open.
nsAutoPopupStatePusher popupStatePusher(openAbused, PR_TRUE);
if (argc) {
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
if (pwwatch) {
PRUint32 extraArgc = argc >= 3 ? argc - 3 : 0;
rv = pwwatch->OpenWindowJS(this, url.get(), name_ptr, options_ptr,
aDialog, extraArgc, argv + 3,
getter_AddRefs(domReturn));
} else {
NS_ERROR("WindowWatcher service not a nsPIWindowWatcher!");
rv = NS_ERROR_UNEXPECTED;
}
} else {
rv = wwatch->OpenWindow(this, url.get(), name_ptr, options_ptr,
aExtraArgument, getter_AddRefs(domReturn));
}
if (stack) {
JSContext* cx;
stack->Pop(&cx);
NS_ASSERTION(!cx, "Unexpected JSContext popped!");
}
}
}
NS_ENSURE_SUCCESS(rv, rv);
// success!
if (domReturn) {
CallQueryInterface(domReturn, aReturn);
// Save the principal of the calling script
// We need it to decide whether to clear the scope in SetNewDocument
NS_ASSERTION(sSecMan, "No Security Manager Found!");
if (sSecMan) {
// Note that the opener script URL is not relevant for openDialog
// callers, since those already have chrome privileges. So we
// only want to do this wen aDoJSFixups is true.
if (aDoJSFixups && sSecMan) {
nsCOMPtr<nsIPrincipal> principal;
sSecMan->GetSubjectPrincipal(getter_AddRefs(principal));
if (principal) {
@ -5863,8 +5737,50 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
}
}
}
domReturn.swap(*aReturn);
}
if (NS_SUCCEEDED(rv)) {
if (aDoJSFixups) {
nsCOMPtr<nsIDOMChromeWindow> chrome_win(do_QueryInterface(*aReturn));
if (!chrome_win) {
// A new non-chrome window was created from a call to
// window.open() from JavaScript, make sure there's a document in
// the new window. We do this by simply asking the new window for
// its document, this will synchronously create an empty document
// if there is no document in the window.
// XXXbz should this just use EnsureInnerWindow()?
#ifdef DEBUG_jst
{
nsCOMPtr<nsPIDOMWindow> pidomwin(do_QueryInterface(*aReturn));
nsIDOMDocument *temp = pidomwin->GetExtantDocument();
NS_ASSERTION(temp, "No document in new window!!!");
}
#endif
nsCOMPtr<nsIDOMDocument> doc;
(*aReturn)->GetDocument(getter_AddRefs(doc));
}
}
if (checkForPopup) {
if (abuseLevel >= openControlled) {
nsGlobalWindow *opened = NS_STATIC_CAST(nsGlobalWindow *, *aReturn);
if (!opened->IsPopupSpamWindow()) {
opened->SetPopupSpamWindow(PR_TRUE);
++gOpenPopupSpamCount;
}
}
if (abuseLevel >= openAbused)
FireAbuseEvents(PR_FALSE, PR_TRUE, aUrl, aName, aOptions);
}
}
return rv;
}

Просмотреть файл

@ -321,11 +321,46 @@ protected:
}
// Window Control Functions
NS_IMETHOD OpenInternal(const nsAString& aUrl,
const nsAString& aName,
const nsAString& aOptions,
PRBool aDialog, jsval *argv, PRUint32 argc,
nsISupports *aExtraArgument, nsIDOMWindow **aReturn);
/**
* @param aURL the URL to load in the new window
* @param aName the name to use for the new window
* @param aOptions the window options to use for the new window
* @param aDialog true when called from variants of OpenDialog. If this is
* true, this method will skip popup blocking checks. The
* aDialog argument is passed on to the window watcher.
* @param aCalledNoScript true when called via the [noscript] open()
* and openDialog() methods. When this is true, we do
* NOT want to use the JS stack for things like caller
* determination.
* @param aDoJSFixups true when this is the content-accessible JS version of
* window opening. When true, popups do not cause us to
* throw, we save the caller's principal in the new window
* for later consumption, and we make sure that there is a
* document in the newly-opened window. Note that this
* last will only be done if the newly-opened window is
* non-chrome.
* @param argv The arguments to pass to the new window. The first
* three args, if present, will be aURL, aName, and aOptions. So
* this param only matters if there are more than 3 arguments.
* @param argc The number of arguments in argv.
* @param aExtraArgument Another way to pass arguments in. This is mutually
* exclusive with the argv/argc approach.
* @param aReturn [out] The window that was opened, if any.
*
* @note that the boolean args are const because the function shouldn't be
* messing with them. That also makes it easier for the compiler to sort out
* its build warning stuff.
*/
NS_HIDDEN_(nsresult) OpenInternal(const nsAString& aUrl,
const nsAString& aName,
const nsAString& aOptions,
PRBool aDialog,
PRBool aCalledNoScript,
PRBool aDoJSFixups,
jsval *argv, PRUint32 argc,
nsISupports *aExtraArgument,
nsIDOMWindow **aReturn);
static void CloseWindow(nsISupports* aWindow);
static void ClearWindowScope(nsISupports* aWindow);
@ -350,8 +385,7 @@ protected:
nsIURI **aBuiltURI,
PRBool *aFreeSecurityPass, JSContext **aCXused);
PopupControlState CheckForAbusePoint();
OpenAllowValue CheckOpenAllow(PopupControlState aAbuseLevel,
const nsAString &aName);
OpenAllowValue CheckOpenAllow(PopupControlState aAbuseLevel);
void FireAbuseEvents(PRBool aBlocked, PRBool aWindow,
const nsAString &aPopupURL,
const nsAString &aPopupWindowName,
@ -390,7 +424,10 @@ protected:
PRBool DispatchCustomEvent(const char *aEventName);
PRBool WindowExists(const nsAString& aName);
// If aLookForCallerOnJSStack is true, this method will look at the JS stack
// to determine who the caller is. If it's false, it'll use |this| as the
// caller.
PRBool WindowExists(const nsAString& aName, PRBool aLookForCallerOnJSStack);
already_AddRefed<nsIWidget> GetMainWidget();

Просмотреть файл

@ -78,6 +78,7 @@ SDK_LIBRARY = \
XPIDLSRCS = \
nsIWindowCreator2.idl \
nsIWindowProvider.idl \
$(NULL)
include $(srcdir)/objs.mk

Просмотреть файл

@ -0,0 +1,115 @@
/* -*- 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
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla.org code.
*
* The Initial Developer of the Original Code is Mozilla.com.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Boris Zbarsky <bzbarsky@mit.edu> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/**
* nsIWindowProvider is a callback interface used by Gecko when it needs to
* open a new window. This interface can be implemented by Gecko consumers who
* wish to provide a custom "new window" of their own (for example by returning
* a new tab, an existing window, etc) instead of just having a real new
* toplevel window open.
*
* @status UNDER_REVIEW ???
*/
#include "nsISupports.idl"
interface nsIDOMWindow;
interface nsIURI;
/**
* The nsIWindowProvider interface exists so that the window watcher's default
* behavior of opening a new window can be easly modified. When the window
* watcher needs to open a new window, it will first check with the
* nsIWindowProvider it gets from the parent window. If there is no provider
* or the provider does not provide a window, the window watcher will proceed
* to actually open a new window.
*/
[uuid(A4D32E30-9854-477A-83CB-FB7B8AE9273C)]
interface nsIWindowProvider : nsISupports
{
/**
* A method to request that this provider provide a window. The window
* returned need not to have the right name or parent set on it; setting
* those is the caller's responsibility. The provider can always return null
* to have the caller create a brand-new window.
*
* @param aParent Must not be null. This is the window that the caller wants
* to use as the parent for the new window. Generally,
* nsIWindowProvider implementors can expect to be somehow
* related to aParent; the relationship may depend on the
* nsIWindowProvider implementation.
* @param aChromeFlags The chrome flags the caller will use to create a new
* window if this provider returns null. See
* nsIWebBrowserChrome for the possible values of this
* field.
* @param aPositionSpecified Whether the attempt to create a window is trying
* to specify a position for the new window.
* @param aSizeSpecified Whether the attempt to create a window is trying to
* specify a size for the new window.
* @param aURI The URI to be loaded in the new window. The nsIWindowProvider
* implementation MUST NOT load this URI in the window it
* returns. This URI is provided solely to help the
* nsIWindowProvider implenentation make decisions; the caller
* will handle loading the URI in the window returned if
* provideWindow returns a window. Note that the URI may be null
* if the load cannot be represented by a single URI (e.g. if
* the load has extra load flags, POST data, etc).
* @param aName The name of the window being opened. Setting the name on the
* return value of provideWindow will be handled by the caller;
* aName is provided solely to help the nsIWindowProvider
* implementation make decisions.
* @param aFeatures The feature string for the window being opened. This may
* be empty. The nsIWindowProvider implementation is
* allowed to apply the feature string to the window it
* returns in any way it sees fit. See the nsIWindowWatcher
* interface for details on feature strings.
* @return A window the caller should use or null if the caller should just
* create a new window. The returned window may be newly opened by
* the nsIWindowProvider implementation or may be a window that
* already existed.
*
* @see nsIWindowWatcher for more information on aFeatures.
* @see nsIWebBrowserChrome for more information on aChromeFlags.
*/
nsIDOMWindow provideWindow(in nsIDOMWindow aParent,
in unsigned long aChromeFlags,
in boolean aPositionSpecified,
in boolean aSizeSpecified,
in nsIURI aURI,
in AString aName,
in AUTF8String aFeatures);
};

Просмотреть файл

@ -80,13 +80,22 @@ interface nsIWindowWatcher : nsISupports {
method will effectively act as if aParent were null.
@param aURL url to which to open the new window. Must already be
escaped, if applicable. can be null.
@param aName window name from JS window.open. can be null.
@param aName window name from JS window.open. can be null. If a window
with this name already exists, the openWindow call may just load
aUrl in it (if aUrl is not null) and return it.
@param aFeatures window features from JS window.open. can be null.
@param aArguments extra argument(s) to the new window, to be attached
as the |arguments| property. An nsISupportsArray will be
unwound into multiple arguments (but not recursively!).
can be null.
@return the new window
@note This method may examine the JS context stack for purposes of
determining the security context to use for the search for a given
window named aName.
@note This method should try to set the default charset for the new
window to the default charset of aParent. This is not guaranteed,
however.
*/
nsIDOMWindow openWindow(in nsIDOMWindow aParent, in string aUrl,
in string aName, in string aFeatures,

Просмотреть файл

@ -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
@ -79,13 +79,23 @@ interface nsPIWindowWatcher : nsISupports
method will effectively act as if aParent were null.
@param aURL url to which to open the new window. Must already be
escaped, if applicable. can be null.
@param aName window name from JS window.open. can be null.
@param aName window name from JS window.open. can be null. If a window
with this name already exists, the openWindow call may just load
aUrl in it (if aUrl is not null) and return it.
@param aFeatures window features from JS window.open. can be null.
@param aDialog use dialog defaults (see nsIDOMWindowInternal::openDialog)
@param argc count of argv arguments
@param argv extra JS arguments, if any
(see nsIDOMWindowInternal::openDialog)
@return the new window
@note This method may examine the JS context stack for purposes of
determining the security context to use for the search for a given
window named aName.
@note This method should try to set the default charset for the new
window to the default charset of the document in the calling window
(which is determined based on the JS stack and the value of
aParent). This is not guaranteed, however.
*/
nsIDOMWindow openWindowJS(in nsIDOMWindow aParent, in string aUrl,
in string aName, in string aFeatures, in boolean aDialog,
@ -115,9 +125,3 @@ interface nsPIWindowWatcher : nsISupports
in nsIDocShellTreeItem aOriginalRequestor);
};
%{C++
// {d535806e-afaf-47d1-8d89-783ad088c62a}
#define NS_PWINDOWWATCHER_IID \
{0xd535806e, 0xafaf, 0x47d1, {0x8d, 0x89, 0x78, 0x3a, 0xd0, 0x88, 0xc6, 0x2a}}
%}

Просмотреть файл

@ -81,6 +81,7 @@
#include "nsIMarkupDocumentViewer.h"
#include "nsIContentViewer.h"
#include "nsIDocumentViewer.h"
#include "nsIWindowProvider.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
@ -470,8 +471,8 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
rv = ConvertSupportsTojsvals(aParent, aArguments, &argc, &argv, &cx, &mark);
if (NS_SUCCEEDED(rv)) {
PRBool dialog = argc == 0 ? PR_FALSE : PR_TRUE;
rv = OpenWindowJS(aParent, aUrl, aName, aFeatures, dialog, argc, argv,
_retval);
rv = OpenWindowJSInternal(aParent, aUrl, aName, aFeatures, dialog, argc,
argv, PR_FALSE, _retval);
if (argv) {
js_FreeStack(cx, mark);
@ -481,6 +482,47 @@ nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
return rv;
}
struct SizeSpec {
SizeSpec() :
mLeftSpecified(PR_FALSE),
mTopSpecified(PR_FALSE),
mOuterWidthSpecified(PR_FALSE),
mOuterHeightSpecified(PR_FALSE),
mInnerWidthSpecified(PR_FALSE),
mInnerHeightSpecified(PR_FALSE),
mUseDefaultWidth(PR_FALSE),
mUseDefaultHeight(PR_FALSE)
{}
PRInt32 mLeft;
PRInt32 mTop;
PRInt32 mOuterWidth; // Total window width
PRInt32 mOuterHeight; // Total window height
PRInt32 mInnerWidth; // Content area width
PRInt32 mInnerHeight; // Content area height
PRPackedBool mLeftSpecified;
PRPackedBool mTopSpecified;
PRPackedBool mOuterWidthSpecified;
PRPackedBool mOuterHeightSpecified;
PRPackedBool mInnerWidthSpecified;
PRPackedBool mInnerHeightSpecified;
// If these booleans are true, don't look at the corresponding width values
// even if they're specified -- they'll be bogus
PRPackedBool mUseDefaultWidth;
PRPackedBool mUseDefaultHeight;
PRBool PositionSpecified() const {
return mLeftSpecified || mTopSpecified;
}
PRBool SizeSpecified() const {
return mOuterWidthSpecified || mOuterHeightSpecified ||
mInnerWidthSpecified || mInnerHeightSpecified;
}
};
NS_IMETHODIMP
nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
const char *aUrl,
@ -490,11 +532,27 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
PRUint32 argc,
jsval *argv,
nsIDOMWindow **_retval)
{
return OpenWindowJSInternal(aParent, aUrl, aName, aFeatures, aDialog, argc,
argv, PR_TRUE, _retval);
}
nsresult
nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
const char *aUrl,
const char *aName,
const char *aFeatures,
PRBool aDialog,
PRUint32 argc,
jsval *argv,
PRBool aCalledFromJS,
nsIDOMWindow **_retval)
{
nsresult rv = NS_OK;
PRBool nameSpecified,
featuresSpecified,
windowIsNew = PR_FALSE,
windowNeedsName = PR_FALSE,
windowIsModal = PR_FALSE,
uriToLoadIsChrome = PR_FALSE;
PRUint32 chromeFlags;
@ -509,8 +567,7 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
NS_ENSURE_ARG_POINTER(_retval);
*_retval = 0;
if (aParent)
GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
if (aUrl) {
rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
@ -522,7 +579,9 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
nameSpecified = PR_FALSE;
if (aName) {
CopyUTF8toUTF16(aName, name);
#ifdef DEBUG
CheckWindowName(name);
#endif
nameSpecified = PR_TRUE;
}
@ -534,39 +593,9 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
}
// try to find an extant window with the given name
if (nameSpecified) {
nsCOMPtr<nsIJSContextStack> stack =
do_GetService(sJSStackContractID);
JSContext *cx = nsnull;
if (stack) {
stack->Peek(&cx);
}
nsCOMPtr<nsIDocShellTreeItem> callerItem;
if (cx) {
nsCOMPtr<nsIWebNavigation> callerWebNav =
do_GetInterface(nsWWJSUtils::GetDynamicScriptGlobal(cx));
callerItem = do_QueryInterface(callerWebNav);
}
nsCOMPtr<nsIDocShellTreeItem> 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(), nsnull, callerItem,
getter_AddRefs(newDocShellItem));
}
nsCOMPtr<nsIDOMWindow> foundWindow;
SafeGetWindowByName(name, aParent, getter_AddRefs(foundWindow));
GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
// no extant window? make a new one.
@ -580,6 +609,9 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
aDialog, uriToLoadIsChrome,
!aParent || chromeParent);
SizeSpec sizeSpec;
CalcSizeSpec(features.get(), sizeSpec);
PRBool isCallerChrome = PR_FALSE;
nsCOMPtr<nsIScriptSecurityManager>
sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
@ -597,6 +629,39 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
callerContextGuard.Push(cx);
}
if (!newDocShellItem) {
// We're going to either open up a new window ourselves or ask a
// nsIWindowProvider for one. In either case, we'll want to set the right
// name on it.
windowNeedsName = PR_TRUE;
// Now check whether it's ok to ask a window provider for a window. Don't
// do it if we're opening a dialog or if our parent is a chrome window or
// if we're opening something that has dependent, modal, dialog, or chrome
// flags set.
nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
if (!aDialog && !chromeWin &&
!(chromeFlags & (nsIWebBrowserChrome::CHROME_DEPENDENT |
nsIWebBrowserChrome::CHROME_MODAL |
nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
if (provider) {
NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
nsCOMPtr<nsIDOMWindow> newWindow;
rv = provider->ProvideWindow(aParent, chromeFlags,
sizeSpec.PositionSpecified(),
sizeSpec.SizeSpecified(),
uriToLoad, name, features,
getter_AddRefs(newWindow));
if (NS_SUCCEEDED(rv)) {
GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
}
}
}
}
if (!newDocShellItem) {
windowIsNew = PR_TRUE;
@ -682,6 +747,9 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
if (!newDocShellItem)
return rv;
nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, _retval);
if (NS_FAILED(rv))
return rv;
@ -713,36 +781,58 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
}
}
/* allow an extant window to keep its name (important for cases like
_self where the given name is different (and invalid)). also _blank
is not a window name. */
if (windowIsNew)
newDocShellItem->SetName(nameSpecified && !name.LowerCaseEqualsLiteral("_blank") ?
/* allow a window that we found by name to keep its name (important for cases
like _self where the given name is different (and invalid)). Also, _blank
and _new are not window names. */
if (windowNeedsName)
newDocShellItem->SetName(nameSpecified &&
!name.LowerCaseEqualsLiteral("_blank") &&
!name.LowerCaseEqualsLiteral("_new") ?
name.get() : nsnull);
nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
// When a new window is opened through JavaScript, we want it to use the
// charset of its opener as a fallback in the event the document being loaded
// does not specify a charset. Failing to set this charset is not fatal, so we
// want to continue in the face of errors.
nsCOMPtr<nsPIDOMWindow> parentWin(do_QueryInterface(aParent));
if (parentWin) {
nsIDocShell *parentDocshell = parentWin->GetDocShell();
// parentDocshell may be null if the parent got closed in the meantime
if (parentDocshell) {
nsCOMPtr<nsIContentViewer> parentContentViewer;
parentDocshell->GetContentViewer(getter_AddRefs(parentContentViewer));
nsCOMPtr<nsIDocumentViewer> parentDocViewer(do_QueryInterface(parentContentViewer));
if (parentDocViewer) {
nsCOMPtr<nsIDocument> doc;
parentDocViewer->GetDocument(getter_AddRefs(doc));
nsCOMPtr<nsIContentViewer> newContentViewer;
newDocShell->GetContentViewer(getter_AddRefs(newContentViewer));
nsCOMPtr<nsIMarkupDocumentViewer> newMarkupDocViewer(do_QueryInterface(newContentViewer));
if (doc && newMarkupDocViewer) {
newMarkupDocViewer->SetDefaultCharacterSet(doc->GetDocumentCharacterSet());
// Inherit the right character set into the new window to use as a fallback
// in the event the document being loaded does not specify a charset. When
// aCalledFromJS is true, we want to use the character set of the document in
// the caller; otherwise we want to use the character set of aParent's
// docshell. Failing to set this charset is not fatal, so we want to continue
// in the face of errors.
nsCOMPtr<nsIContentViewer> newCV;
newDocShell->GetContentViewer(getter_AddRefs(newCV));
nsCOMPtr<nsIMarkupDocumentViewer> newMuCV = do_QueryInterface(newCV);
if (newMuCV) {
nsCOMPtr<nsIDocShellTreeItem> parentItem;
GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
if (aCalledFromJS) {
nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(parentItem);
nsCOMPtr<nsPIDOMWindow> callerWin = do_GetInterface(callerItem);
if (callerWin) {
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(callerWin->GetExtantDocument());
if (doc) {
newMuCV->SetDefaultCharacterSet(doc->GetDocumentCharacterSet());
}
}
}
else {
nsCOMPtr<nsIDocShell> parentDocshell = do_QueryInterface(parentItem);
// parentDocshell may be null if the parent got closed in the meantime
if (parentDocshell) {
nsCOMPtr<nsIContentViewer> parentCV;
parentDocshell->GetContentViewer(getter_AddRefs(parentCV));
nsCOMPtr<nsIMarkupDocumentViewer> parentMuCV =
do_QueryInterface(parentCV);
if (parentMuCV) {
nsCAutoString charset;
nsresult res = parentMuCV->GetDefaultCharacterSet(charset);
if (NS_SUCCEEDED(res)) {
newMuCV->SetDefaultCharacterSet(charset);
}
res = parentMuCV->GetPrevDocCharacterSet(charset);
if (NS_SUCCEEDED(res)) {
newMuCV->SetPrevDocCharacterSet(charset);
}
}
}
}
@ -787,8 +877,7 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
// get its document, if any
if (stack && NS_SUCCEEDED(stack->Peek(&ccx)) && ccx) {
nsIScriptGlobalObject *sgo =
nsWWJSUtils::GetStaticScriptGlobal(ccx, ::JS_GetGlobalObject(ccx));
nsIScriptGlobalObject *sgo = nsWWJSUtils::GetDynamicScriptGlobal(ccx);
nsCOMPtr<nsPIDOMWindow> w(do_QueryInterface(sgo));
if (w) {
@ -811,8 +900,7 @@ nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
}
if (windowIsNew)
SizeOpenedDocShellItem(newDocShellItem, aParent, features.get(),
chromeFlags);
SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
if (windowIsModal) {
nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
@ -1111,37 +1199,22 @@ nsWindowWatcher::GetWindowByName(const PRUnichar *aTargetName,
*aResult = nsnull;
nsCOMPtr<nsIWebNavigation> webNav;
nsCOMPtr<nsIDocShellTreeItem> treeItem;
// First, check if the TargetName exists in the aCurrentWindow hierarchy
webNav = do_GetInterface(aCurrentWindow);
if (webNav) {
nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem;
docShellTreeItem = do_QueryInterface(webNav);
if (docShellTreeItem) {
// Note: original requestor is null here, per idl comments
docShellTreeItem->FindItemWithName(aTargetName, nsnull, nsnull,
getter_AddRefs(treeItem));
}
nsCOMPtr<nsIDocShellTreeItem> startItem;
GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
if (startItem) {
// Note: original requestor is null here, per idl comments
startItem->FindItemWithName(aTargetName, nsnull, nsnull,
getter_AddRefs(treeItem));
}
// Next, see if the TargetName exists in any window hierarchy
if (!treeItem) {
else {
// Note: original requestor is null here, per idl comments
FindItemWithName(aTargetName, nsnull, nsnull, getter_AddRefs(treeItem));
}
if (treeItem) {
nsCOMPtr<nsIDOMWindow> domWindow;
domWindow = do_GetInterface(treeItem);
if (domWindow) {
*aResult = domWindow;
NS_ADDREF(*aResult);
}
}
nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem);
domWindow.swap(*aResult);
return NS_OK;
}
@ -1203,6 +1276,7 @@ nsWindowWatcher::URIfromURL(const char *aURL,
return NS_NewURI(aURI, aURL, baseURI);
}
#ifdef DEBUG
/* Check for an illegal name e.g. frame3.1
This just prints a warning message an continues; we open the window anyway,
(see bug 32898). */
@ -1218,15 +1292,14 @@ void nsWindowWatcher::CheckWindowName(nsString& aName)
// Don't use js_ReportError as this will cause the application
// to shut down (JS_ASSERT calls abort()) See bug 32898
nsAutoString warn;
nsCAutoString warn;
warn.AssignLiteral("Illegal character in window name ");
warn.Append(aName);
char *cp = ToNewCString(warn);
NS_WARNING(cp);
nsCRT::free(cp);
AppendUTF16toUTF8(aName, warn);
NS_WARNING(warn.get());
break;
}
}
#endif // DEBUG
#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \
prefBranch->GetBoolPref(feature, &forceEnable); \
@ -1513,6 +1586,65 @@ nsWindowWatcher::FindItemWithName(const PRUnichar* aName,
return rv;
}
already_AddRefed<nsIDocShellTreeItem>
nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
{
nsCOMPtr<nsIJSContextStack> stack =
do_GetService(sJSStackContractID);
JSContext *cx = nsnull;
if (stack) {
stack->Peek(&cx);
}
nsIDocShellTreeItem* callerItem = nsnull;
if (cx) {
nsCOMPtr<nsIWebNavigation> callerWebNav =
do_GetInterface(nsWWJSUtils::GetDynamicScriptGlobal(cx));
if (callerWebNav) {
CallQueryInterface(callerWebNav, &callerItem);
}
}
if (!callerItem) {
NS_IF_ADDREF(callerItem = aParentItem);
}
return callerItem;
}
nsresult
nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
nsIDOMWindow* aCurrentWindow,
nsIDOMWindow** aResult)
{
*aResult = nsnull;
nsCOMPtr<nsIDocShellTreeItem> startItem;
GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
const nsAFlatString& flatName = PromiseFlatString(aName);
nsCOMPtr<nsIDocShellTreeItem> foundItem;
if (startItem) {
startItem->FindItemWithName(flatName.get(), nsnull, callerItem,
getter_AddRefs(foundItem));
}
else {
FindItemWithName(flatName.get(), nsnull, callerItem,
getter_AddRefs(foundItem));
}
nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem);
foundWin.swap(*aResult);
return NS_OK;
}
/* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
This forces the creation of a script context, if one has not already
been created. Note it also sets the window's opener to the parent,
@ -1537,7 +1669,69 @@ nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
return rv;
}
/* Size and position the new window according to aFeatures. This method
void
nsWindowWatcher::CalcSizeSpec(const char* aFeatures, SizeSpec& aResult)
{
// Parse position spec, if any, from aFeatures
PRBool present;
PRInt32 temp;
present = PR_FALSE;
if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
aResult.mLeft = temp;
else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
aResult.mLeft = temp;
aResult.mLeftSpecified = present;
present = PR_FALSE;
if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
aResult.mTop = temp;
else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
aResult.mTop = temp;
aResult.mTopSpecified = present;
// Parse size spec, if any. Chrome size overrides content size.
if ((temp = WinHasOption(aFeatures, "outerWidth", PR_INT32_MIN, nsnull))) {
if (temp == PR_INT32_MIN) {
aResult.mUseDefaultWidth = PR_TRUE;
}
else {
aResult.mOuterWidth = temp;
}
aResult.mOuterWidthSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "width", PR_INT32_MIN, nsnull)) ||
(temp = WinHasOption(aFeatures, "innerWidth", PR_INT32_MIN,
nsnull))) {
if (temp == PR_INT32_MIN) {
aResult.mUseDefaultWidth = PR_TRUE;
} else {
aResult.mInnerWidth = temp;
}
aResult.mInnerWidthSpecified = PR_TRUE;
}
if ((temp = WinHasOption(aFeatures, "outerHeight", PR_INT32_MIN, nsnull))) {
if (temp == PR_INT32_MIN) {
aResult.mUseDefaultHeight = PR_TRUE;
}
else {
aResult.mOuterHeight = temp;
}
aResult.mOuterHeightSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "height", PR_INT32_MIN,
nsnull)) ||
(temp = WinHasOption(aFeatures, "innerHeight", PR_INT32_MIN,
nsnull))) {
if (temp == PR_INT32_MIN) {
aResult.mUseDefaultHeight = PR_TRUE;
} else {
aResult.mInnerHeight = temp;
}
aResult.mInnerHeightSpecified = PR_TRUE;
}
}
/* Size and position the new window according to aSizeSpec. This method
is assumed to be called after the window has already been given
a default position and size; thus its current position and size are
accurate defaults. The new window is made visible at method end.
@ -1545,8 +1739,7 @@ nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
void
nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
nsIDOMWindow *aParent,
const char *aFeatures,
PRUint32 aChromeFlags)
const SizeSpec & aSizeSpec)
{
// position and size of window
PRInt32 left = 0,
@ -1585,62 +1778,47 @@ nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
}
}
// Parse position spec, if any, from aFeatures
PRBool positionSpecified = PR_FALSE;
PRBool present;
PRInt32 temp;
present = PR_FALSE;
if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
left = temp;
else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
left = temp;
if (present)
positionSpecified = PR_TRUE;
present = PR_FALSE;
if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
top = temp;
else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
top = temp;
if (present)
positionSpecified = PR_TRUE;
PRBool sizeSpecified = PR_FALSE;
// Parse size spec, if any. Chrome size overrides content size.
if ((temp = WinHasOption(aFeatures, "outerWidth", width, nsnull))) {
width = temp;
sizeSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "width",
width - chromeWidth, nsnull))) {
width = temp;
sizeChromeWidth = PR_FALSE;
sizeSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "innerWidth",
width - chromeWidth, nsnull))) {
width = temp;
sizeChromeWidth = PR_FALSE;
sizeSpecified = PR_TRUE;
// Set up left/top
if (aSizeSpec.mLeftSpecified) {
left = aSizeSpec.mLeft;
}
if ((temp = WinHasOption(aFeatures, "outerHeight", height, nsnull))) {
height = temp;
sizeSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "height",
height - chromeHeight, nsnull))) {
height = temp;
sizeChromeHeight = PR_FALSE;
sizeSpecified = PR_TRUE;
} else if ((temp = WinHasOption(aFeatures, "innerHeight",
height - chromeHeight, nsnull))) {
height = temp;
sizeChromeHeight = PR_FALSE;
sizeSpecified = PR_TRUE;
if (aSizeSpec.mTopSpecified) {
top = aSizeSpec.mTop;
}
// Set up width
if (aSizeSpec.mOuterWidthSpecified) {
if (!aSizeSpec.mUseDefaultWidth) {
width = aSizeSpec.mOuterWidth;
} // Else specified to default; just use our existing width
}
else if (aSizeSpec.mInnerWidthSpecified) {
sizeChromeWidth = PR_FALSE;
if (aSizeSpec.mUseDefaultWidth) {
width = width - chromeWidth;
} else {
width = aSizeSpec.mInnerWidth;
}
}
// Set up height
if (aSizeSpec.mOuterHeightSpecified) {
if (!aSizeSpec.mUseDefaultHeight) {
height = aSizeSpec.mOuterHeight;
} // Else specified to default; just use our existing height
}
else if (aSizeSpec.mInnerHeightSpecified) {
sizeChromeHeight = PR_FALSE;
if (aSizeSpec.mUseDefaultHeight) {
height = height - chromeHeight;
} else {
height = aSizeSpec.mInnerHeight;
}
}
PRBool positionSpecified = aSizeSpec.PositionSpecified();
nsresult res;
PRBool enabled = PR_FALSE;
@ -1686,7 +1864,7 @@ nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
screen->GetAvailRect(&screenLeft, &screenTop,
&screenWidth, &screenHeight);
if (sizeSpecified) {
if (aSizeSpec.SizeSpecified()) {
/* Unlike position, force size out-of-bounds check only if
size actually was specified. Otherwise, intrinsically sized
windows are broken. */
@ -1717,7 +1895,7 @@ nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
if (positionSpecified)
treeOwnerAsWin->SetPosition(left, top);
if (sizeSpecified) {
if (aSizeSpec.SizeSpecified()) {
/* Prefer to trust the interfaces, which think in terms of pure
chrome or content sizes. If we have a mix, use the chrome size
adjusted by the chrome/content differences calculated earlier. */

Просмотреть файл

@ -59,6 +59,7 @@ struct JSContext;
struct JSObject;
struct nsWatcherWindowEntry;
struct PRLock;
struct SizeSpec;
class nsWindowWatcher :
public nsIWindowWatcher,
@ -84,12 +85,38 @@ private:
nsWatcherWindowEntry *FindWindowEntry(nsIDOMWindow *aWindow);
nsresult RemoveWindow(nsWatcherWindowEntry *inInfo);
// Get the caller tree item. Look on the JS stack, then fall back
// to the parent if there's nothing there.
already_AddRefed<nsIDocShellTreeItem>
GetCallerTreeItem(nsIDocShellTreeItem* aParentItem);
// Unlike GetWindowByName this will look for a caller on the JS
// stack, and then fall back on aCurrentWindow if it can't find one.
nsresult SafeGetWindowByName(const nsAString& aName,
nsIDOMWindow* aCurrentWindow,
nsIDOMWindow** aResult);
// Just like OpenWindowJS, but knows whether it got called via OpenWindowJS
// (which means called from script) or called via OpenWindow.
nsresult OpenWindowJSInternal(nsIDOMWindow *aParent,
const char *aUrl,
const char *aName,
const char *aFeatures,
PRBool aDialog,
PRUint32 argc,
jsval *argv,
PRBool aCalledFromJS,
nsIDOMWindow **_retval);
static JSContext *GetJSContextFromWindow(nsIDOMWindow *aWindow);
static JSContext *GetJSContextFromCallStack();
static nsresult URIfromURL(const char *aURL,
nsIDOMWindow *aParent,
nsIURI **aURI);
#ifdef DEBUG
static void CheckWindowName(nsString& aName);
#endif
static PRUint32 CalculateChromeFlags(const char *aFeatures,
PRBool aFeaturesSpecified,
PRBool aDialog,
@ -97,13 +124,14 @@ private:
PRBool aHasChromeParent);
static PRInt32 WinHasOption(const char *aOptions, const char *aName,
PRInt32 aDefault, PRBool *aPresenceFlag);
/* Compute the right SizeSpec based on aFeatures */
static void CalcSizeSpec(const char* aFeatures, SizeSpec& aResult);
static nsresult ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
nsIDOMWindow *aParent,
nsIDOMWindow **aOpenedWindow);
static void SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
nsIDOMWindow *aParent,
const char *aFeatures,
PRUint32 aChromeFlags);
const SizeSpec & aSizeSpec);
static nsresult AttachArguments(nsIDOMWindow *aWindow,
PRUint32 argc, jsval *argv);
static nsresult ConvertSupportsTojsvals(nsIDOMWindow *aWindow,

Просмотреть файл

@ -71,6 +71,7 @@ REQUIRES = xpcom \
xpconnect \
intl \
windowwatcher \
embed_base \
caps \
unicharutil \
uconv \

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIDOMElement.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMWindowInternal.h"
#include "nsIDOMChromeWindow.h"
#include "nsIBrowserDOMWindow.h"
#include "nsIDOMXULElement.h"
#include "nsIEmbeddingSiteWindow.h"
#include "nsIEmbeddingSiteWindow2.h"
@ -60,6 +62,9 @@
#include "nsIPrincipal.h"
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIWebNavigation.h"
#include "nsIDOMDocument.h"
#include "nsIScriptObjectPrincipal.h"
@ -116,6 +121,9 @@ NS_INTERFACE_MAP_BEGIN(nsContentTreeOwner)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
// XXXbz why not just implement those interfaces directly on this object?
// Should file a followup and fix.
NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIEmbeddingSiteWindow, mSiteWindow2)
NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIEmbeddingSiteWindow2, mSiteWindow2)
NS_INTERFACE_MAP_END
@ -682,6 +690,108 @@ NS_IMETHODIMP nsContentTreeOwner::SetTitle(const PRUnichar* aTitle)
return mXULWindow->SetTitle(title.get());
}
//*****************************************************************************
// nsContentTreeOwner: nsIWindowProvider
//*****************************************************************************
NS_IMETHODIMP
nsContentTreeOwner::ProvideWindow(nsIDOMWindow* aParent,
PRUint32 aChromeFlags,
PRBool aPositionSpecified,
PRBool aSizeSpecified,
nsIURI* aURI,
const nsAString& aName,
const nsACString& aFeatures,
nsIDOMWindow** aReturn)
{
NS_ENSURE_ARG_POINTER(aParent);
*aReturn = nsnull;
if (!mXULWindow) {
// Nothing to do here
return NS_OK;
}
#ifdef DEBUG
nsCOMPtr<nsIWebNavigation> parentNav = do_GetInterface(aParent);
nsCOMPtr<nsIDocShellTreeOwner> parentOwner = do_GetInterface(parentNav);
NS_ASSERTION(SameCOMIdentity(parentOwner,
NS_STATIC_CAST(nsIDocShellTreeOwner*, this)),
"Parent from wrong docshell tree?");
#endif
// First check what our prefs say
nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs) {
return NS_OK;
}
nsCOMPtr<nsIPrefBranch> branch;
prefs->GetBranch("browser.link.", getter_AddRefs(branch));
if (!branch) {
return NS_OK;
}
// Where should we open this?
PRInt32 containerPref;
if (NS_FAILED(branch->GetIntPref("open_newwindow", &containerPref))) {
return NS_OK;
}
if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
// Just open a window normally
return NS_OK;
}
/* Now check our restriction pref. The restriction pref is a power-user's
fine-tuning pref. values:
0: no restrictions - divert everything
1: don't divert window.open at all
2: don't divert window.open with features
*/
PRInt32 restrictionPref;
if (NS_FAILED(branch->GetIntPref("open_newwindow.restriction",
&restrictionPref)) ||
restrictionPref < 0 ||
restrictionPref > 2) {
restrictionPref = 2; // Sane default behavior
}
if (restrictionPref == 1) {
return NS_OK;
}
if (restrictionPref == 2 &&
// Only continue if there are no size/position features and no special
// chrome flags.
(aChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
aPositionSpecified || aSizeSpecified)) {
return NS_OK;
}
nsCOMPtr<nsIDOMWindowInternal> domWin;
mXULWindow->GetWindowDOMWindow(getter_AddRefs(domWin));
nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(domWin);
if (!chromeWin) {
// Really odd... but whatever
NS_WARNING("nsXULWindow's DOMWindow is not a chrome window");
return NS_OK;
}
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
if (!browserDOMWin) {
return NS_OK;
}
// Get a new rendering area from the browserDOMWin. To make this
// safe for cases when it'll try to return an existing window or
// something, get it with a null URI.
return browserDOMWin->OpenURI(nsnull, aParent, containerPref,
nsIBrowserDOMWindow::OPEN_NEW, aReturn);
}
//*****************************************************************************
// nsContentTreeOwner: Accessors
//*****************************************************************************

Просмотреть файл

@ -50,6 +50,7 @@
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIWebBrowserChrome2.h"
#include "nsIWindowProvider.h"
class nsXULWindow;
class nsSiteWindow2;
@ -57,7 +58,8 @@ class nsSiteWindow2;
class nsContentTreeOwner : public nsIDocShellTreeOwner,
public nsIBaseWindow,
public nsIInterfaceRequestor,
public nsIWebBrowserChrome2
public nsIWebBrowserChrome2,
public nsIWindowProvider
{
friend class nsXULWindow;
friend class nsSiteWindow2;
@ -70,6 +72,7 @@ public:
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIWEBBROWSERCHROME
NS_DECL_NSIWEBBROWSERCHROME2
NS_DECL_NSIWINDOWPROVIDER
protected:
nsContentTreeOwner(PRBool fPrimary);