Bug 1267720 - Factor out logic for creating windows for content processes from OpenWindowInternal
MozReview-Commit-ID: 1dhGthT8bmu
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5415,23 +5415,16 @@ ContentParent::RecvCreateWindow(PBrowser
if (aThisTab) {
thisTabParent = TabParent::GetFrom(aThisTab);
}
if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowserOrApp())) {
return false;
}
- nsCOMPtr<nsPIWindowWatcher> pwwatch =
- do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
-
- if (NS_WARN_IF(NS_FAILED(*aResult))) {
- return true;
- }
-
TabParent* newTab = TabParent::GetFrom(aNewTab);
MOZ_ASSERT(newTab);
// Content has requested that we open this new content window, so
// we must have an opener.
newTab->SetHasContentOpener(true);
nsCOMPtr<nsIContent> frame;
@@ -5517,47 +5510,32 @@ ContentParent::RecvCreateWindow(PBrowser
return true;
}
nsCOMPtr<mozIDOMWindowProxy> window;
TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
const char* features = aFeatures.IsVoid() ? nullptr : aFeatures.get();
- *aResult = pwwatch->OpenWindow2(parent, nullptr, name, features, aCalledFromJS,
- false, false, thisTabParent, nullptr,
- aFullZoom, 1, getter_AddRefs(window));
-
- if (NS_WARN_IF(!window)) {
- return true;
- }
-
- *aResult = NS_ERROR_FAILURE;
- auto* pwindow = nsPIDOMWindowOuter::From(window);
- nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell();
- if (NS_WARN_IF(!windowDocShell)) {
- return true;
- }
-
- nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
- windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
-
- nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner);
- if (NS_WARN_IF(!xulWin)) {
- return true;
- }
-
- nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
- xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
- if (NS_WARN_IF(!xulBrowserWin)) {
+ nsCOMPtr<nsPIWindowWatcher> pwwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
+
+ if (NS_WARN_IF(NS_FAILED(*aResult))) {
return true;
}
nsCOMPtr<nsITabParent> 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->OpenWindowForTablessContent(getter_AddRefs(newRemoteTab));
+ } else {
+ *aResult = pwwatch->OpenWindowForTabContent(thisTabParent, features, aCalledFromJS,
+ aFullZoom, getter_AddRefs(newRemoteTab));
+ }
if (NS_WARN_IF(NS_FAILED(*aResult))) {
return true;
}
MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
newTab->SwapFrameScriptsFrom(*aFrameScripts);
--- a/embedding/components/windowwatcher/moz.build
+++ b/embedding/components/windowwatcher/moz.build
@@ -37,8 +37,10 @@ if CONFIG['MOZ_XUL']:
]
FINAL_LIBRARY = 'xul'
# For nsJSUtils
LOCAL_INCLUDES += [
'/docshell/base',
'/dom/base',
]
+
+include('/ipc/chromium/chromium-config.mozbuild')
--- a/embedding/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/embedding/components/windowwatcher/nsPIWindowWatcher.idl
@@ -77,16 +77,47 @@ interface nsPIWindowWatcher : nsISupport
in boolean aCalledFromScript,
in boolean aDialog,
in boolean aNavigate,
in nsITabParent aOpeningTab,
in nsISupports aArgs,
[optional] in float aOpenerFullZoom);
/**
+ * Used by the Service Worker Client.openWindow API. This allows notification
+ * handlers to open new top-level windows.
+ *
+ * @return the nsITabParent of the initial browser for the newly opened
+ * window.
+ */
+ nsITabParent openWindowForTablessContent();
+
+ /**
+ * This should only be called if a content process has requested that
+ * a new window be opened.
+ *
+ * @param aOpeningTab
+ * The nsITabParent that is requesting the new window be opened.
+ * @param aFeatures
+ * Window features if called with window.open.
+ * @param aCalledFromJS
+ * True if called via window.open.
+ * @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 openWindowForTabContent(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,
* for example.
*
* @param aName the name of the window. Must not be null.
* @param aRequestor the tree item immediately making the request.
* We should make sure to not recurse down into its findItemWithName
* method.
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -59,16 +59,17 @@
#include "nsPresContext.h"
#include "nsContentUtils.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsSandboxFlags.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/DOMStorage.h"
#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabParent.h"
#ifdef USEWEAKREFS
#include "nsIWeakReference.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
@@ -478,16 +479,253 @@ CheckUserContextCompatibility(nsIDocShel
uint32_t principalUserContextId;
nsresult rv = subjectPrincipal->GetUserContextId(&principalUserContextId);
NS_ENSURE_SUCCESS(rv, false);
return principalUserContextId == userContextId;
}
+
+NS_IMETHODIMP
+nsWindowWatcher::OpenWindowForTablessContent(nsITabParent** aResult)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(mWindowCreator);
+
+ if (!nsContentUtils::IsSafeToRunScript()) {
+ nsContentUtils::WarnScriptWasIgnored(nullptr);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter =
+ nsContentUtils::GetMostRecentNonPBWindow();
+ if (NS_WARN_IF(!parentWindowOuter)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
+ GetWindowTreeOwner(parentWindowOuter, getter_AddRefs(parentTreeOwner));
+ if (NS_WARN_IF(!parentTreeOwner)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIWindowCreator2> 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;
+ }
+
+ uint32_t chromeFlags =
+ CalculateChromeFlagsForContent(EmptyCString(), true);
+
+ chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+
+ bool cancel = false;
+ nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
+ nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
+
+ nsresult rv =
+ windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags, contextFlags,
+ nullptr, &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<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
+ if (NS_WARN_IF(!chromeTreeItem)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
+ if (NS_WARN_IF(!chromeContext)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIDocShellTreeOwner> chromeTreeOwner;
+ chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner));
+ if (NS_WARN_IF(!chromeTreeOwner)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ bool isPrivateBrowsingWindow =
+ Preferences::GetBool("browser.privatebrowsing.autostart");
+
+ 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);
+
+ SizeSpec sizeSpec;
+ CalcSizeSpec(EmptyCString().get(), sizeSpec);
+ float fullZoom = 1.0f;
+ SizeOpenedDocShellItem(chromeTreeItem, parentWindowOuter, false, sizeSpec,
+ &fullZoom);
+
+ nsCOMPtr<nsITabParent> newTabParent;
+ chromeTreeOwner->GetPrimaryTabParent(getter_AddRefs(newTabParent));
+ if (NS_WARN_IF(!newTabParent)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ newTabParent.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowWatcher::OpenWindowForTabContent(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(!aOpeningTabParent)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (NS_WARN_IF(!mWindowCreator)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // 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);
+
+ nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter =
+ openingTab->GetParentWindowOuter();
+
+ if (!parentWindowOuter) {
+ // We couldn't find a browser window for the opener, so either
+ // it's been 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<nsIDocShellTreeOwner> parentTreeOwner;
+ GetWindowTreeOwner(parentWindowOuter, getter_AddRefs(parentTreeOwner));
+
+ nsCOMPtr<nsIWindowCreator2> 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.get(), "mozDisplayId", 0, nullptr);
+ windowCreator2->SetScreenId(retval);
+#endif
+
+ nsAutoCString features(aFeatures);
+ uint32_t chromeFlags =
+ CalculateChromeFlagsForContent(features, aCalledFromJS);
+ // TODO: Always make remote yo
+ chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+
+ bool cancel = false;
+ nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
+ nsCOMPtr<nsIWebBrowserChrome> 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<nsIDocShellTreeItem> chromeTreeItem = do_GetInterface(newWindowChrome);
+ if (NS_WARN_IF(!chromeTreeItem)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIDocShellTreeOwner> chromeTreeOwner;
+ chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner));
+ if (NS_WARN_IF(!chromeTreeOwner)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (PL_strcasestr(features.get(), "width=") ||
+ PL_strcasestr(features.get(), "height=")) {
+ chromeTreeOwner->SetPersistence(false, false, false);
+ }
+
+ nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
+ if (NS_WARN_IF(!chromeContext)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ bool isPrivateBrowsingWindow =
+ Preferences::GetBool("browser.privatebrowsing.autostart");
+
+ // Otherwise, propagate the privacy status of the parent window, if
+ // available, to the child.
+ if (!isPrivateBrowsingWindow) {
+ nsCOMPtr<nsIDocShellTreeItem> parentItem;
+ GetWindowTreeItem(parentWindowOuter, getter_AddRefs(parentItem));
+ nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
+ if (parentContext) {
+ isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
+ }
+ }
+ 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);
+
+ SizeSpec sizeSpec;
+ CalcSizeSpec(features.get(), sizeSpec);
+ SizeOpenedDocShellItem(chromeTreeItem, parentWindowOuter, false, sizeSpec,
+ &aOpenerFullZoom);
+
+ nsCOMPtr<nsITabParent> 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,
const char* aName,
const char* aFeatures,
bool aCalledFromJS,
bool aDialog,
bool aNavigate,
@@ -827,19 +1065,18 @@ nsWindowWatcher::OpenWindowInternal(mozI
#ifdef MOZ_WIDGET_GONK
int retval = WinHasOption(features.get(), "mozDisplayId", 0, nullptr);
windowCreator2->SetScreenId(retval);
#endif
bool cancel = false;
rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
- contextFlags, uriToLoad,
- aOpeningTab, &cancel,
- getter_AddRefs(newChrome));
+ contextFlags, aOpeningTab,
+ &cancel, getter_AddRefs(newChrome));
if (NS_SUCCEEDED(rv) && cancel) {
newChrome = 0; // just in case
rv = NS_ERROR_ABORT;
}
} else {
rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
getter_AddRefs(newChrome));
}
@@ -1541,16 +1778,86 @@ nsWindowWatcher::URIfromURL(const char*
if (forceEnable && !(aDialog && !openedFromContentScript) && \
!(!openedFromContentScript && aHasChromeParent) && !aChromeURL) { \
chromeFlags |= flag; \
} else { \
chromeFlags |= \
WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \
}
+#define NS_CALCULATE_CHROME_FLAG_FOR_CONTENT(feature, flag) \
+ prefBranch->GetBoolPref(feature, &forceEnable); \
+ if (forceEnable) { \
+ chromeFlags |= flag; \
+ } else { \
+ chromeFlags |= \
+ WinHasOption(aFeatures.BeginReading(), feature, 0, &presenceFlag) ? flag : 0; \
+ }
+
+
+// static
+uint32_t
+nsWindowWatcher::CalculateChromeFlagsForContent(const nsACString& aFeatures,
+ bool aCalledFromJS)
+{
+ if (aFeatures.IsEmpty()) {
+ return nsIWebBrowserChrome::CHROME_ALL;
+ }
+
+ uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ nsCOMPtr<nsIPrefService> prefs =
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ // TODO: Figure out error handling here...
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ rv = prefs->GetBranch("dom.disable_window_open_feature.",
+ getter_AddRefs(prefBranch));
+
+ NS_ENSURE_SUCCESS(rv, 0);
+
+ bool forceEnable = false;
+ bool presenceFlag = false;
+
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("titlebar",
+ nsIWebBrowserChrome::CHROME_TITLEBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("close",
+ nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("toolbar",
+ nsIWebBrowserChrome::CHROME_TOOLBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("location",
+ nsIWebBrowserChrome::CHROME_LOCATIONBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("personalbar",
+ nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("status",
+ nsIWebBrowserChrome::CHROME_STATUSBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("menubar",
+ nsIWebBrowserChrome::CHROME_MENUBAR);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("scrollbars",
+ nsIWebBrowserChrome::CHROME_SCROLLBARS);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("resizable",
+ nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
+ NS_CALCULATE_CHROME_FLAG_FOR_CONTENT("minimizable",
+ nsIWebBrowserChrome::CHROME_WINDOW_MIN);
+
+ chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
+ chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
+
+ chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
+ chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
+ chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
+ chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
+ nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
+ chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
+
+ return chromeFlags;
+}
+
/**
* 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
--- a/embedding/components/windowwatcher/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/nsWindowWatcher.h
@@ -86,16 +86,19 @@ protected:
nsIArray* aArgv,
float* aOpenerFullZoom,
mozIDOMWindowProxy** aResult);
static nsresult URIfromURL(const char* aURL,
mozIDOMWindowProxy* aParent,
nsIURI** aURI);
+ static uint32_t CalculateChromeFlagsForContent(const nsACString& aFeaturesStr,
+ bool aCalledFromJS);
+
static uint32_t CalculateChromeFlags(mozIDOMWindowProxy* aParent,
const char* aFeatures,
bool aFeaturesSpecified,
bool aDialog,
bool aChromeURL,
bool aHasChromeParent,
bool aCalledFromJS,
bool aOpenedFromRemoteTab);