From 688f1f95163a17e2286bb2e277093b0c41e17059 Mon Sep 17 00:00:00 2001 From: Mike Conley Date: Tue, 5 Jul 2016 12:00:07 -0400 Subject: [PATCH] Bug 1261842 - Factor out logic for creating windows for content processes from nsWindowWatcher::OpenWindowInternal. r=smaug MozReview-Commit-ID: 1dhGthT8bmu --HG-- extra : rebase_source : 34d321ebd3c27f414c63c72908c72d23aabb5396 extra : source : 0f8b603781817f58c4664a33429325bc316598e9 --- dom/base/nsGlobalWindow.cpp | 4 +- dom/ipc/ContentParent.cpp | 48 +- dom/workers/ServiceWorkerClients.cpp | 2 +- embedding/components/windowwatcher/moz.build | 2 + .../windowwatcher/nsPIWindowWatcher.idl | 36 +- .../windowwatcher/nsWindowWatcher.cpp | 496 ++++++++++++------ .../windowwatcher/nsWindowWatcher.h | 29 +- 7 files changed, 403 insertions(+), 214 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index f1baedcc32ee..1aae42d23afa 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -11833,7 +11833,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, // !aCalledNoScript. rv = pwwatch->OpenWindow2(AsOuter(), url.get(), name_ptr, options_ptr, /* aCalledFromScript = */ true, - aDialog, aNavigate, nullptr, argv, + aDialog, aNavigate, argv, 1.0f, 0, getter_AddRefs(domReturn)); } else { // Force a system caller here so that the window watcher won't screw us @@ -11853,7 +11853,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, rv = pwwatch->OpenWindow2(AsOuter(), url.get(), name_ptr, options_ptr, /* aCalledFromScript = */ false, - aDialog, aNavigate, nullptr, aExtraArgument, + aDialog, aNavigate, aExtraArgument, 1.0f, 0, getter_AddRefs(domReturn)); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 59e36b29e077..eb6d7a3a8475 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5317,13 +5317,6 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, return false; } - nsCOMPtr pwwatch = - do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult); - - if (NS_WARN_IF(NS_FAILED(*aResult))) { - return true; - } - TabParent* newTab = TabParent::GetFrom(aNewTab); MOZ_ASSERT(newTab); @@ -5419,41 +5412,22 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab, const char* features = aFeatures.IsVoid() ? nullptr : aFeatures.get(); - *aResult = pwwatch->OpenWindow2(parent, nullptr, - aName.IsVoid() ? - nullptr : - NS_ConvertUTF16toUTF8(aName).get(), - features, aCalledFromJS, - false, false, thisTabParent, nullptr, - aFullZoom, 1, getter_AddRefs(window)); + nsCOMPtr pwwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult); - if (NS_WARN_IF(!window)) { - return true; - } - - *aResult = NS_ERROR_FAILURE; - auto* pwindow = nsPIDOMWindowOuter::From(window); - nsCOMPtr windowDocShell = pwindow->GetDocShell(); - if (NS_WARN_IF(!windowDocShell)) { - return true; - } - - nsCOMPtr treeOwner; - windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); - - nsCOMPtr xulWin = do_GetInterface(treeOwner); - if (NS_WARN_IF(!xulWin)) { - return true; - } - - nsCOMPtr xulBrowserWin; - xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin)); - if (NS_WARN_IF(!xulBrowserWin)) { + if (NS_WARN_IF(NS_FAILED(*aResult))) { return true; } nsCOMPtr newRemoteTab; - *aResult = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab)); + if (!thisTabParent) { + // Because we weren't passed an opener tab, the content process has asked us + // to open a new window that is unrelated to a pre-existing tab. + *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab)); + } else { + *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, features, aCalledFromJS, + aFullZoom, getter_AddRefs(newRemoteTab)); + } if (NS_WARN_IF(NS_FAILED(*aResult))) { return true; diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp index df2fef16ec86..55c3339462c0 100644 --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -606,7 +606,7 @@ private: spec.get(), nullptr, nullptr, - false, false, true, nullptr, nullptr, 1.0f, 0, + false, false, true, nullptr, 1.0f, 0, getter_AddRefs(newWindow)); nsCOMPtr pwindow = nsPIDOMWindowOuter::From(newWindow); pwindow.forget(aWindow); diff --git a/embedding/components/windowwatcher/moz.build b/embedding/components/windowwatcher/moz.build index 1daf9dfb6682..31aa718c979b 100644 --- a/embedding/components/windowwatcher/moz.build +++ b/embedding/components/windowwatcher/moz.build @@ -45,3 +45,5 @@ LOCAL_INCLUDES += [ if CONFIG['GNU_CXX']: CXXFLAGS += ['-Wno-error=shadow'] + +include('/ipc/chromium/chromium-config.mozbuild') diff --git a/embedding/components/windowwatcher/nsPIWindowWatcher.idl b/embedding/components/windowwatcher/nsPIWindowWatcher.idl index 97ae4ffd383f..5370cf4e496e 100644 --- a/embedding/components/windowwatcher/nsPIWindowWatcher.idl +++ b/embedding/components/windowwatcher/nsPIWindowWatcher.idl @@ -54,9 +54,6 @@ interface nsPIWindowWatcher : nsISupports @param aDialog use dialog defaults (see nsIDOMWindow::openDialog) @param aNavigate true if we should navigate the new window to the specified URL. - @param aOpeningTab the nsITabParent that is opening the new window. The - nsITabParent is a remote tab belonging to aParent. Can - be nullptr if this window is not being opened from a tab. @param aArgs Window argument @param aOpenerFullZoom the full zoom multiplier for the opener window. this can be null in the single process case where @@ -77,10 +74,41 @@ interface nsPIWindowWatcher : nsISupports in boolean aCalledFromScript, in boolean aDialog, in boolean aNavigate, - in nsITabParent aOpeningTab, in nsISupports aArgs, [optional] in float aOpenerFullZoom); + /** + * Opens a new window using the most recent non-private browser + * window as its parent. + * + * @return the nsITabParent of the initial browser for the newly opened + * window. + */ + nsITabParent openWindowWithoutParent(); + + /** + * Opens a new window so that the window that aOpeningTab belongs to + * is set as the parent window. The newly opened window will also + * inherit load context information from aOpeningTab. + * + * @param aOpeningTab + * The nsITabParent that is requesting the new window be opened. + * @param aFeatures + * Window features if called with window.open or similar. + * @param aCalledFromJS + * True if called via window.open or similar. + * @param aOpenerFullZoom + * The current zoom multiplier for the opener tab. This is then + * applied to the newly opened window. + * + * @return the nsITabParent of the initial browser for the newly opened + * window. + */ + nsITabParent openWindowWithTabParent(in nsITabParent aOpeningTab, + in string aFeatures, + in boolean aCalledFromJS, + in float aOpenerFullZoom); + /** * Find a named docshell tree item amongst all windows registered * with the window watcher. This may be a subframe in some window, diff --git a/embedding/components/windowwatcher/nsWindowWatcher.cpp b/embedding/components/windowwatcher/nsWindowWatcher.cpp index 3ef41aa29fc3..47a16ddbb1e8 100644 --- a/embedding/components/windowwatcher/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp @@ -64,6 +64,9 @@ #include "mozilla/Preferences.h" #include "mozilla/dom/DOMStorage.h" #include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/TabParent.h" +#include "nsIXULWindow.h" +#include "nsIXULBrowserWindow.h" #ifdef USEWEAKREFS #include "nsIWeakReference.h" @@ -368,7 +371,7 @@ nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent, return OpenWindowInternal(aParent, aUrl, aName, aFeatures, /* calledFromJS = */ false, dialog, - /* navigate = */ true, nullptr, argv, + /* navigate = */ true, argv, /* openerFullZoom = */ nullptr, aResult); } @@ -425,7 +428,6 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent, bool aCalledFromScript, bool aDialog, bool aNavigate, - nsITabParent* aOpeningTab, nsISupports* aArguments, float aOpenerFullZoom, uint8_t aOptionalArgc, @@ -448,7 +450,7 @@ nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent, return OpenWindowInternal(aParent, aUrl, aName, aFeatures, aCalledFromScript, dialog, - aNavigate, aOpeningTab, argv, + aNavigate, argv, aOptionalArgc >= 1 ? &aOpenerFullZoom : nullptr, aResult); } @@ -486,6 +488,154 @@ CheckUserContextCompatibility(nsIDocShell* aDocShell) return principalUserContextId == userContextId; } +NS_IMETHODIMP +nsWindowWatcher::OpenWindowWithoutParent(nsITabParent** aResult) +{ + return OpenWindowWithTabParent(nullptr, "", true, 1.0f, aResult); +} + +NS_IMETHODIMP +nsWindowWatcher::OpenWindowWithTabParent(nsITabParent* aOpeningTabParent, + const char* aFeatures, + bool aCalledFromJS, + float aOpenerFullZoom, + nsITabParent** aResult) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(mWindowCreator); + + if (!nsContentUtils::IsSafeToRunScript()) { + nsContentUtils::WarnScriptWasIgnored(nullptr); + return NS_ERROR_FAILURE; + } + + if (NS_WARN_IF(!mWindowCreator)) { + return NS_ERROR_UNEXPECTED; + } + + bool isPrivateBrowsingWindow = + Preferences::GetBool("browser.privatebrowsing.autostart"); + + nsCOMPtr parentWindowOuter; + if (aOpeningTabParent) { + // We need to examine the window that aOpeningTabParent belongs to in + // order to inform us of what kind of window we're going to open. + TabParent* openingTab = TabParent::GetFrom(aOpeningTabParent); + parentWindowOuter = openingTab->GetParentWindowOuter(); + + // Propagate the privacy status of the parent window, if + // available, to the child. + if (!isPrivateBrowsingWindow) { + nsCOMPtr parentContext = openingTab->GetLoadContext(); + if (parentContext) { + isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing(); + } + } + } + + if (!parentWindowOuter) { + // We couldn't find a browser window for the opener, so either we + // never were passed aOpeningTabParent, the window is closed, + // or it's in the process of closing. Either way, we'll use + // the most recently opened browser window instead. + parentWindowOuter = nsContentUtils::GetMostRecentNonPBWindow(); + } + + if (NS_WARN_IF(!parentWindowOuter)) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr parentTreeOwner; + GetWindowTreeOwner(parentWindowOuter, getter_AddRefs(parentTreeOwner)); + if (NS_WARN_IF(!parentTreeOwner)) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr windowCreator2(do_QueryInterface(mWindowCreator)); + if (NS_WARN_IF(!windowCreator2)) { + return NS_ERROR_UNEXPECTED; + } + + uint32_t contextFlags = 0; + if (parentWindowOuter->IsLoadingOrRunningTimeout()) { + contextFlags |= + nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT; + } + + // B2G multi-screen support. mozDisplayId is returned from the + // "display-changed" event, it is also platform-dependent. +#ifdef MOZ_WIDGET_GONK + int retval = WinHasOption(features, "mozDisplayId", 0, nullptr); + windowCreator2->SetScreenId(retval); +#endif + + nsAutoCString features(aFeatures); + uint32_t chromeFlags = CalculateChromeFlagsForChild(features); + + // A content process has asked for a new window, which implies + // that the new window will need to be remote. + chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + + bool cancel = false; + nsCOMPtr parentChrome(do_GetInterface(parentTreeOwner)); + nsCOMPtr newWindowChrome; + + nsresult rv = + windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags, contextFlags, + aOpeningTabParent, &cancel, + getter_AddRefs(newWindowChrome)); + + if (NS_SUCCEEDED(rv) && cancel) { + newWindowChrome = nullptr; // just in case + return NS_ERROR_ABORT; + } + + if (NS_WARN_IF(!newWindowChrome)) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr chromeTreeItem = do_GetInterface(newWindowChrome); + if (NS_WARN_IF(!chromeTreeItem)) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr chromeTreeOwner; + chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner)); + if (NS_WARN_IF(!chromeTreeOwner)) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr chromeContext = do_QueryInterface(chromeTreeItem); + if (NS_WARN_IF(!chromeContext)) { + return NS_ERROR_UNEXPECTED; + } + + chromeContext->SetPrivateBrowsing(isPrivateBrowsingWindow); + + // Tabs opened from a content process can only open new windows + // that will also run with out-of-process tabs. + chromeContext->SetRemoteTabs(true); + + if (PL_strcasestr(features.get(), "width=") || + PL_strcasestr(features.get(), "height=")) { + chromeTreeOwner->SetPersistence(false, false, false); + } + + SizeSpec sizeSpec; + CalcSizeSpec(features, sizeSpec); + SizeOpenedDocShellItem(chromeTreeItem, parentWindowOuter, false, sizeSpec, + &aOpenerFullZoom); + + nsCOMPtr newTabParent; + chromeTreeOwner->GetPrimaryTabParent(getter_AddRefs(newTabParent)); + if (NS_WARN_IF(!newTabParent)) { + return NS_ERROR_UNEXPECTED; + } + + newTabParent.forget(aResult); + return NS_OK; +} + nsresult nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, const char* aUrl, @@ -494,7 +644,6 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, bool aCalledFromJS, bool aDialog, bool aNavigate, - nsITabParent* aOpeningTab, nsIArray* aArgv, float* aOpenerFullZoom, mozIDOMWindowProxy** aResult) @@ -507,10 +656,6 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, bool uriToLoadIsChrome = false; bool windowIsModalContentDialog = false; - // Opening tabs are only ever passed to OpenWindowInternal if we're opening - // a window from a remote tab. - bool openedFromRemoteTab = !!aOpeningTab; - uint32_t chromeFlags; nsAutoString name; // string version of aName nsAutoCString features; // string version of aFeatures @@ -521,10 +666,6 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, nsCOMPtr parent = aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr; - // When the opener is a remote tab, the url passed from the child process - // isn't actually used. This code needs some serious refactoring. - MOZ_ASSERT_IF(openedFromRemoteTab, !aUrl); - MOZ_ASSERT_IF(openedFromRemoteTab, XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aResult); *aResult = 0; @@ -538,7 +679,7 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, // We expect TabParent to have provided us the absolute URI of the window // we're to open, so there's no need to call URIfromURL (or more importantly, // to check for a chrome URI, which cannot be opened from a remote tab). - if (aUrl && !openedFromRemoteTab) { + if (aUrl) { rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad)); if (NS_FAILED(rv)) { return rv; @@ -554,26 +695,16 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, name.SetIsVoid(true); } - bool featuresSpecified = false; if (aFeatures) { features.Assign(aFeatures); - featuresSpecified = true; features.StripWhitespace(); } else { features.SetIsVoid(true); } - // We only want to check for existing named windows if: - // a) We're the child process - // b) We're the parent process, and aOpeningTab wasn't passed - // in. - // This is because when using child processes, the parent process shouldn't - // know or care about names - unless we're opening named windows from chrome. - if (!aOpeningTab) { - // try to find an extant window with the given name - nsCOMPtr foundWindow = SafeGetWindowByName(name, aParent); - GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem)); - } + // try to find an extant window with the given name + nsCOMPtr foundWindow = SafeGetWindowByName(name, aParent); + GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem)); // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI. // The state of the window can change before this call and if we are blocked @@ -598,23 +729,29 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, if (aParent) { // Check if the parent document has chrome privileges. nsIDocument* doc = parentWindow->GetDoc(); - hasChromeParent = - doc && nsContentUtils::IsChromeDoc(doc) && !openedFromRemoteTab; + hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc); } - // Make sure we call CalculateChromeFlags() *before* we push the - // callee context onto the context stack so that - // CalculateChromeFlags() sees the actual caller when doing its - // security checks. - chromeFlags = CalculateChromeFlags(aParent, features, featuresSpecified, - aDialog, uriToLoadIsChrome, - hasChromeParent, aCalledFromJS, - openedFromRemoteTab); + bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode(); - // If we are opening a window from a remote browser, the resulting window - // should also be remote. - MOZ_ASSERT_IF(openedFromRemoteTab, - chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); + // Make sure we calculate the chromeFlags *before* we push the + // callee context onto the context stack so that + // the calculation sees the actual caller when doing its + // security checks. + if (isCallerChrome && XRE_IsParentProcess()) { + chromeFlags = CalculateChromeFlagsForParent(aParent, features, + aDialog, uriToLoadIsChrome, + hasChromeParent, aCalledFromJS); + } else { + chromeFlags = CalculateChromeFlagsForChild(features); + + // Until ShowModalDialog is removed, it's still possible for content to + // request dialogs, but only in single-process mode. + if (aDialog) { + MOZ_ASSERT(XRE_IsParentProcess()); + chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; + } + } // If we're not called through our JS version of the API, and we got // our internal modal option, treat the window we're opening as a @@ -638,8 +775,6 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, nsCOMPtr sm( do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); - bool isCallerChrome = - nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !openedFromRemoteTab; // XXXbz Why is an AutoJSAPI good enough here? Wouldn't AutoEntryScript (so // we affect the entry global) make more sense? Or do we just want to affect @@ -837,7 +972,7 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, bool cancel = false; rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags, - contextFlags, aOpeningTab, + contextFlags, nullptr, &cancel, getter_AddRefs(newChrome)); if (NS_SUCCEEDED(rv) && cancel) { newChrome = 0; // just in case @@ -849,6 +984,14 @@ nsWindowWatcher::OpenWindowInternal(mozIDOMWindowProxy* aParent, } if (newChrome) { + nsCOMPtr xulWin = do_GetInterface(newChrome); + if (xulWin) { + nsCOMPtr xulBrowserWin; + xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin)); + if (xulBrowserWin) { + xulBrowserWin->ForceInitialBrowserNonRemote(); + } + } /* It might be a chrome nsXULWindow, in which case it won't have an nsIDOMWindow (primary content shell). But in that case, it'll be able to hand over an nsIDocShellTreeItem directly. */ @@ -1540,110 +1683,41 @@ nsWindowWatcher::URIfromURL(const char* aURL, return NS_NewURI(aURI, aURL, baseURI); } -#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \ - prefBranch->GetBoolPref(feature, &forceEnable); \ - if (forceEnable && !(aDialog && !openedFromContentScript) && \ - !(!openedFromContentScript && aHasChromeParent) && !aChromeURL) { \ - chromeFlags |= flag; \ - } else { \ - chromeFlags |= \ - WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \ +#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \ + prefBranch->GetBoolPref(feature, &forceEnable); \ + if (forceEnable && !aDialog && !aHasChromeParent && !aChromeURL) { \ + chromeFlags |= flag; \ + } else { \ + chromeFlags |= \ + WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \ } -/** - * Calculate the chrome bitmask from a string list of features. - * @param aParent the opener window - * @param aFeatures a string containing a list of named chrome features - * @param aNullFeatures true if aFeatures was a null pointer (which fact - * is lost by its conversion to a string in the caller) - * @param aDialog affects the assumptions made about unnamed features - * @return the chrome bitmask - */ // static uint32_t -nsWindowWatcher::CalculateChromeFlags(mozIDOMWindowProxy* aParent, - const nsACString& aFeatures, - bool aFeaturesSpecified, - bool aDialog, - bool aChromeURL, - bool aHasChromeParent, - bool aCalledFromJS, - bool aOpenedFromRemoteTab) +nsWindowWatcher::CalculateChromeFlagsHelper(uint32_t aInitialFlags, + const nsACString& aFeatures, + bool& presenceFlag, + bool aDialog, + bool aHasChromeParent, + bool aChromeURL) { - const bool inContentProcess = XRE_IsContentProcess(); - uint32_t chromeFlags = 0; - - // The features string is made void by OpenWindowInternal - // if nullptr was originally passed as the features string. - if (aFeatures.IsVoid()) { - chromeFlags = nsIWebBrowserChrome::CHROME_ALL; - if (aDialog) { - chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | - nsIWebBrowserChrome::CHROME_OPENAS_CHROME; - } - - if (inContentProcess) { - return chromeFlags; - } - } else { - chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS; - } - - bool openedFromContentScript = - aOpenedFromRemoteTab ? aCalledFromJS - : !nsContentUtils::LegacyIsCallerChromeOrNativeCode(); - - /* This function has become complicated since browser windows and - dialogs diverged. The difference is, browser windows assume all - chrome not explicitly mentioned is off, if the features string - is not null. Exceptions are some OS border chrome new with Mozilla. - Dialogs interpret a (mostly) empty features string to mean - "OS's choice," and also support an "all" flag explicitly disallowed - in the standards-compliant window.(normal)open. */ - - bool presenceFlag = false; - if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) { - chromeFlags = nsIWebBrowserChrome::CHROME_ALL; - } - - /* Next, allow explicitly named options to override the initial settings */ - - if (!inContentProcess && !openedFromContentScript) { - // Determine whether the window is a private browsing window - chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ? - nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0; - chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ? - nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0; - } - - if (!inContentProcess) { - // Determine whether the window should have remote tabs. - bool remote = BrowserTabsRemoteAutostart(); - - if (!openedFromContentScript) { - if (remote) { - remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag); - } else { - remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag); - } - } - - if (remote) { - chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; - } - } + uint32_t chromeFlags = aInitialFlags; nsresult rv; - nsCOMPtr prefBranch; nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, true); + + NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT); rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch)); - NS_ENSURE_SUCCESS(rv, true); + NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT); + + // NS_CALCULATE_CHROME_FLAG_FOR requires aFeatures, forceEnable, aDialog + // aHasChromeParent, aChromeURL, presenceFlag and chromeFlags to be in + // scope. bool forceEnable = false; NS_CALCULATE_CHROME_FLAG_FOR("titlebar", @@ -1670,6 +1744,130 @@ nsWindowWatcher::CalculateChromeFlags(mozIDOMWindowProxy* aParent, chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS; } + return chromeFlags; +} + +// static +uint32_t +nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags, + bool aChromeURL) +{ + aChromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; + aChromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; + aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; + aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED; + aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP; + /* Untrusted script is allowed to pose modal windows with a chrome + scheme. This check could stand to be better. But it effectively + prevents untrusted script from opening modal windows in general + while still allowing alerts and the like. */ + if (!aChromeURL) { + aChromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME); + } + + if (!(aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) { + aChromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT; + } + + return aChromeFlags; +} + +/** + * Calculate the chrome bitmask from a string list of features requested + * from a child process. Feature strings that are restricted to the parent + * process are ignored here. + * @param aFeatures a string containing a list of named features + * @return the chrome bitmask + */ +// static +uint32_t +nsWindowWatcher::CalculateChromeFlagsForChild(const nsACString& aFeatures) +{ + if (aFeatures.IsVoid()) { + return nsIWebBrowserChrome::CHROME_ALL; + } + + bool presenceFlag = false; + uint32_t chromeFlags = CalculateChromeFlagsHelper( + nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, presenceFlag); + + return EnsureFlagsSafeForContent(chromeFlags); +} + +/** + * Calculate the chrome bitmask from a string list of features for a new + * privileged window. + * @param aParent the opener window + * @param aFeatures a string containing a list of named chrome features + * @param aDialog affects the assumptions made about unnamed features + * @param aChromeURL true if the window is being sent to a chrome:// URL + * @param aHasChromeParent true if the parent window is privileged + * @param aCalledFromJS true if the window open request came from script. + * @return the chrome bitmask + */ +// static +uint32_t +nsWindowWatcher::CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent, + const nsACString& aFeatures, + bool aDialog, + bool aChromeURL, + bool aHasChromeParent, + bool aCalledFromJS) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode()); + + uint32_t chromeFlags = 0; + + // The features string is made void by OpenWindowInternal + // if nullptr was originally passed as the features string. + if (aFeatures.IsVoid()) { + chromeFlags = nsIWebBrowserChrome::CHROME_ALL; + if (aDialog) { + chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME; + } + } else { + chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS; + } + + /* This function has become complicated since browser windows and + dialogs diverged. The difference is, browser windows assume all + chrome not explicitly mentioned is off, if the features string + is not null. Exceptions are some OS border chrome new with Mozilla. + Dialogs interpret a (mostly) empty features string to mean + "OS's choice," and also support an "all" flag explicitly disallowed + in the standards-compliant window.(normal)open. */ + + bool presenceFlag = false; + if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) { + chromeFlags = nsIWebBrowserChrome::CHROME_ALL; + } + + /* Next, allow explicitly named options to override the initial settings */ + chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, presenceFlag, + aDialog, aHasChromeParent, aChromeURL); + + // Determine whether the window is a private browsing window + chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ? + nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0; + chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ? + nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0; + + // Determine whether the window should have remote tabs. + bool remote = BrowserTabsRemoteAutostart(); + + if (remote) { + remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag); + } else { + remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag); + } + + if (remote) { + chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; + } + chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; @@ -1723,16 +1921,11 @@ nsWindowWatcher::CalculateChromeFlags(mozIDOMWindowProxy* aParent, does not provide any affordance for dialog windows. This does not interfere with dialog windows created through openDialog. */ bool disableDialogFeature = false; - nsCOMPtr branch = do_QueryInterface(prefs); + nsCOMPtr branch = do_GetService(NS_PREFSERVICE_CONTRACTID); + branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature); - if (openedFromContentScript) { - // If the caller context is content, we do not support the - // dialog feature. See bug 1095236. - disableDialogFeature = true; - } - if (!disableDialogFeature) { chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ? nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; @@ -1754,27 +1947,8 @@ nsWindowWatcher::CalculateChromeFlags(mozIDOMWindowProxy* aParent, */ // Check security state for use in determing window dimensions - if (openedFromContentScript || !aHasChromeParent) { - // If priv check fails (or if we're called from chrome, but the - // parent is not a chrome window), set all elements to minimum - // reqs., else leave them alone. - chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; - chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; - chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; - chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED; - chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP; - /* Untrusted script is allowed to pose modal windows with a chrome - scheme. This check could stand to be better. But it effectively - prevents untrusted script from opening modal windows in general - while still allowing alerts and the like. */ - if (!aChromeURL) - chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL | - nsIWebBrowserChrome::CHROME_OPENAS_CHROME); - } - - if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) { - // Remove the dependent flag if we're not opening as chrome - chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT; + if (!aHasChromeParent) { + chromeFlags = EnsureFlagsSafeForContent(chromeFlags, aChromeURL); } // Disable CHROME_OPENAS_DIALOG if the window is inside