Bug 1507375 - Restrict the controllability of UI parts visibility with features parameter of window.open. r=smaug

Make the features parameter of window.open just a condition for whether to open
a popup or a new tab.
Also remove dom.disable_window_open_feature.* prefs.

Differential Revision: https://phabricator.services.mozilla.com/D65926

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tooru Fujisawa 2020-03-19 09:13:12 +00:00
Родитель f7f372d8a6
Коммит 51880be4b3
20 изменённых файлов: 700 добавлений и 285 удалений

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

@ -61,25 +61,34 @@ add_task(async function titlebar_buttons_visibility() {
const BUTTONS_MAY_VISIBLE = true;
const BUTTONS_NEVER_VISIBLE = false;
const drawInTitlebarValues = {
true: BUTTONS_MAY_VISIBLE,
false: BUTTONS_NEVER_VISIBLE,
};
const windowFeaturesValues = {
"width=300,height=100": BUTTONS_NEVER_VISIBLE,
toolbar: BUTTONS_MAY_VISIBLE,
menubar: BUTTONS_NEVER_VISIBLE,
"menubar,toolbar": BUTTONS_MAY_VISIBLE,
};
// Always open a new window.
// With default behavior, it opens a new tab, that doesn't affect button
// visibility at all.
Services.prefs.setIntPref("browser.link.open_newwindow", 2);
const drawInTitlebarValues = [
[true, BUTTONS_MAY_VISIBLE],
[false, BUTTONS_NEVER_VISIBLE],
];
const windowFeaturesValues = [
// Opens a popup
["width=300,height=100", BUTTONS_NEVER_VISIBLE],
["toolbar", BUTTONS_NEVER_VISIBLE],
["menubar", BUTTONS_NEVER_VISIBLE],
["menubar,toolbar", BUTTONS_NEVER_VISIBLE],
// Opens a new window
["", BUTTONS_MAY_VISIBLE],
];
const menuBarShownValues = [true, false];
for (const drawInTitlebar of Object.keys(drawInTitlebarValues)) {
Services.prefs.setBoolPref(
"browser.tabs.drawInTitlebar",
drawInTitlebar == "true"
);
for (const [drawInTitlebar, drawInTitlebarButtons] of drawInTitlebarValues) {
Services.prefs.setBoolPref("browser.tabs.drawInTitlebar", drawInTitlebar);
for (const windowFeatures of Object.keys(windowFeaturesValues)) {
for (const [
windowFeatures,
windowFeaturesButtons,
] of windowFeaturesValues) {
for (const menuBarShown of menuBarShownValues) {
CustomizableUI.setToolbarVisibility("toolbar-menubar", menuBarShown);
@ -109,8 +118,8 @@ add_task(async function titlebar_buttons_visibility() {
const params = `drawInTitlebar=${drawInTitlebar}, windowFeatures=${windowFeatures}, menuBarShown=${menuBarShown}`;
if (
drawInTitlebarValues[drawInTitlebar] == BUTTONS_MAY_VISIBLE &&
windowFeaturesValues[windowFeatures] == BUTTONS_MAY_VISIBLE
drawInTitlebarButtons == BUTTONS_MAY_VISIBLE &&
windowFeaturesButtons == BUTTONS_MAY_VISIBLE
) {
ok(
buttonsInMenubarShown || buttonsInTabbarShown,
@ -137,4 +146,5 @@ add_task(async function titlebar_buttons_visibility() {
CustomizableUI.setToolbarVisibility("toolbar-menubar", false);
Services.prefs.clearUserPref("browser.tabs.drawInTitlebar");
Services.prefs.clearUserPref("browser.link.open_newwindow");
});

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

@ -74,8 +74,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=369370
is(img.width, 800, "image width");
is(img.height, 600, "image height");
is(kidDoc.body.scrollLeft, 400, "Checking scrollLeft");
is(kidDoc.body.scrollTop, 300, "Checking scrollTop");
is(kidDoc.body.scrollLeft,
kidDoc.body.scrollLeftMax, "Checking scrollLeft");
is(kidDoc.body.scrollTop,
kidDoc.body.scrollTopMax, "Checking scrollTop");
// ========== test 4 ==========
// Click there again to zoom out
@ -106,7 +108,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=369370
// Give the image document time to respond
SimpleTest.executeSoon(function() {
is(img.height, 600, "image height");
is(img.getBoundingClientRect().top, 25, "Image is vertically centered");
var bodyHeight = kidDoc.body.scrollHeight;
var imgRect = img.getBoundingClientRect();
is(imgRect.top, bodyHeight - imgRect.bottom, "Image is vertically centered");
test7();
});
}, {once: true});
@ -139,7 +143,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=369370
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, function() {
kidWin = window.open("bug369370-popup.png", "bug369370", "width=400,height=300,scrollbars=no");
kidWin = window.open("bug369370-popup.png", "bug369370", "width=400,height=300");
// will init onload
ok(kidWin, "opened child window");
kidWin.onload = childLoaded;

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

@ -878,10 +878,10 @@ BrowserChild::GetInterface(const nsIID& aIID, void** aSink) {
NS_IMETHODIMP
BrowserChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
bool aCalledFromJS, bool aPositionSpecified,
bool aSizeSpecified, nsIURI* aURI,
const nsAString& aName, const nsACString& aFeatures,
bool aForceNoOpener, bool aForceNoReferrer,
bool aCalledFromJS, bool aWidthSpecified,
nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener,
bool aForceNoReferrer,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn) {
*aReturn = nullptr;
@ -899,7 +899,7 @@ BrowserChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
if (!iframeMoz) {
int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
nsPIDOMWindowOuter::From(aParent), aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified);
aWidthSpecified);
// If it turns out we're opening in the current browser, just hand over the
// current browser's docshell.
@ -921,10 +921,10 @@ BrowserChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
// open window call was canceled. It's important that we pass this error
// code back to our caller.
ContentChild* cc = ContentChild::GetSingleton();
return cc->ProvideWindowCommon(
this, aParent, iframeMoz, aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified, aURI, aName, aFeatures, aForceNoOpener, aForceNoReferrer,
aLoadState, aWindowIsNew, aReturn);
return cc->ProvideWindowCommon(this, aParent, iframeMoz, aChromeFlags,
aCalledFromJS, aWidthSpecified, aURI, aName,
aFeatures, aForceNoOpener, aForceNoReferrer,
aLoadState, aWindowIsNew, aReturn);
}
void BrowserChild::DestroyWindow() {

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

@ -775,16 +775,16 @@ void ContentChild::SetProcessName(const nsAString& aName) {
NS_IMETHODIMP
ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
bool aCalledFromJS, bool aPositionSpecified,
bool aSizeSpecified, nsIURI* aURI,
const nsAString& aName, const nsACString& aFeatures,
bool aForceNoOpener, bool aForceNoReferrer,
bool aCalledFromJS, bool aWidthSpecified,
nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener,
bool aForceNoReferrer,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn) {
return ProvideWindowCommon(
nullptr, aParent, false, aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified, aURI, aName, aFeatures, aForceNoOpener, aForceNoReferrer,
aLoadState, aWindowIsNew, aReturn);
return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
aCalledFromJS, aWidthSpecified, aURI, aName,
aFeatures, aForceNoOpener, aForceNoReferrer,
aLoadState, aWindowIsNew, aReturn);
}
static nsresult GetCreateWindowParams(mozIDOMWindowProxy* aParent,
@ -863,11 +863,10 @@ static nsresult GetCreateWindowParams(mozIDOMWindowProxy* aParent,
nsresult ContentChild::ProvideWindowCommon(
BrowserChild* aTabOpener, mozIDOMWindowProxy* aParent, bool aIframeMoz,
uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified,
bool aSizeSpecified, nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn) {
uint32_t aChromeFlags, bool aCalledFromJS, bool aWidthSpecified,
nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures,
bool aForceNoOpener, bool aForceNoReferrer, nsDocShellLoadState* aLoadState,
bool* aWindowIsNew, BrowsingContext** aReturn) {
*aReturn = nullptr;
nsAutoPtr<IPCTabContext> ipcContext;
@ -944,9 +943,8 @@ nsresult ContentChild::ProvideWindowCommon(
MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(name));
Unused << SendCreateWindowInDifferentProcess(
aTabOpener, aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified, uriToLoad, features, fullZoom, name,
triggeringPrincipal, csp, referrerInfo);
aTabOpener, aChromeFlags, aCalledFromJS, aWidthSpecified, uriToLoad,
features, fullZoom, name, triggeringPrincipal, csp, referrerInfo);
// We return NS_ERROR_ABORT, so that the caller knows that we've abandoned
// the window open as far as it is concerned.
@ -1199,9 +1197,9 @@ nsresult ContentChild::ProvideWindowCommon(
}
SendCreateWindow(aTabOpener, newChild, aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified, uriToLoad, features,
fullZoom, Principal(triggeringPrincipal), csp,
referrerInfo, std::move(resolve), std::move(reject));
aWidthSpecified, uriToLoad, features, fullZoom,
Principal(triggeringPrincipal), csp, referrerInfo,
std::move(resolve), std::move(reject));
}
// =======================

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

@ -112,8 +112,8 @@ class ContentChild final
nsresult ProvideWindowCommon(BrowserChild* aTabOpener,
mozIDOMWindowProxy* aParent, bool aIframeMoz,
uint32_t aChromeFlags, bool aCalledFromJS,
bool aPositionSpecified, bool aSizeSpecified,
nsIURI* aURI, const nsAString& aName,
bool aWidthSpecified, nsIURI* aURI,
const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener,
bool aForceNoReferrer,
nsDocShellLoadState* aLoadState,

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

@ -4619,13 +4619,13 @@ bool ContentParent::DeallocPWebBrowserPersistDocumentParent(
mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
PBrowserParent* aThisTab, bool aSetOpener, const uint32_t& aChromeFlags,
const bool& aCalledFromJS, const bool& aPositionSpecified,
const bool& aSizeSpecified, nsIURI* aURIToLoad, const nsCString& aFeatures,
const float& aFullZoom, uint64_t aNextRemoteTabId, const nsString& aName,
nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
bool* aWindowIsNew, int32_t& aOpenLocation,
nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
bool aLoadURI, nsIContentSecurityPolicy* aCsp)
const bool& aCalledFromJS, const bool& aWidthSpecified, nsIURI* aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom,
uint64_t aNextRemoteTabId, const nsString& aName, nsresult& aResult,
nsCOMPtr<nsIRemoteTab>& aNewRemoteTab, bool* aWindowIsNew,
int32_t& aOpenLocation, nsIPrincipal* aTriggeringPrincipal,
nsIReferrerInfo* aReferrerInfo, bool aLoadURI,
nsIContentSecurityPolicy* aCsp)
{
// The content process should never be in charge of computing whether or
@ -4699,8 +4699,7 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
}
aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
outerWin, aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified);
outerWin, aChromeFlags, aCalledFromJS, aWidthSpecified);
MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
@ -4841,11 +4840,10 @@ mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
PBrowserParent* aThisTab, PBrowserParent* aNewTab,
const uint32_t& aChromeFlags, const bool& aCalledFromJS,
const bool& aPositionSpecified, const bool& aSizeSpecified,
const Maybe<URIParams>& aURIToLoad, const nsCString& aFeatures,
const float& aFullZoom, const IPC::Principal& aTriggeringPrincipal,
nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo,
CreateWindowResolver&& aResolve) {
const bool& aWidthSpecified, const Maybe<URIParams>& aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom,
const IPC::Principal& aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
nsIReferrerInfo* aReferrerInfo, CreateWindowResolver&& aResolve) {
nsresult rv = NS_OK;
CreatedWindowInfo cwi;
@ -4884,9 +4882,9 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
aThisTab, /* aSetOpener = */ true, aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified, uriToLoad, aFeatures, aFullZoom,
nextRemoteTabId, VoidString(), rv, newRemoteTab, &cwi.windowOpened(),
openLocation, aTriggeringPrincipal, aReferrerInfo,
aWidthSpecified, uriToLoad, aFeatures, aFullZoom, nextRemoteTabId,
VoidString(), rv, newRemoteTab, &cwi.windowOpened(), openLocation,
aTriggeringPrincipal, aReferrerInfo,
/* aLoadUri = */ false, aCsp);
if (!ipcResult) {
return ipcResult;
@ -4918,9 +4916,9 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess(
PBrowserParent* aThisTab, const uint32_t& aChromeFlags,
const bool& aCalledFromJS, const bool& aPositionSpecified,
const bool& aSizeSpecified, const Maybe<URIParams>& aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom, const nsString& aName,
const bool& aCalledFromJS, const bool& aWidthSpecified,
const Maybe<URIParams>& aURIToLoad, const nsCString& aFeatures,
const float& aFullZoom, const nsString& aName,
nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
nsIReferrerInfo* aReferrerInfo) {
MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(aName));
@ -4958,7 +4956,7 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess(
nsresult rv;
mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
aThisTab, /* aSetOpener = */ false, aChromeFlags, aCalledFromJS,
aPositionSpecified, aSizeSpecified, uriToLoad, aFeatures, aFullZoom,
aWidthSpecified, uriToLoad, aFeatures, aFullZoom,
/* aNextRemoteTabId = */ 0, aName, rv, newRemoteTab, &windowIsNew,
openLocation, aTriggeringPrincipal, aReferrerInfo,
/* aLoadUri = */ true, aCsp);

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

@ -521,17 +521,17 @@ class ContentParent final
mozilla::ipc::IPCResult RecvCreateWindow(
PBrowserParent* aThisBrowserParent, PBrowserParent* aNewTab,
const uint32_t& aChromeFlags, const bool& aCalledFromJS,
const bool& aPositionSpecified, const bool& aSizeSpecified,
const Maybe<URIParams>& aURIToLoad, const nsCString& aFeatures,
const float& aFullZoom, const IPC::Principal& aTriggeringPrincipal,
const bool& aWidthSpecified, const Maybe<URIParams>& aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom,
const IPC::Principal& aTriggeringPrincipal,
nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo,
CreateWindowResolver&& aResolve);
mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess(
PBrowserParent* aThisTab, const uint32_t& aChromeFlags,
const bool& aCalledFromJS, const bool& aPositionSpecified,
const bool& aSizeSpecified, const Maybe<URIParams>& aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom, const nsString& aName,
const bool& aCalledFromJS, const bool& aWidthSpecified,
const Maybe<URIParams>& aURIToLoad, const nsCString& aFeatures,
const float& aFullZoom, const nsString& aName,
nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
nsIReferrerInfo* aReferrerInfo);
@ -727,9 +727,8 @@ class ContentParent final
// compatibility with GeckoView.
mozilla::ipc::IPCResult CommonCreateWindow(
PBrowserParent* aThisTab, bool aSetOpener, const uint32_t& aChromeFlags,
const bool& aCalledFromJS, const bool& aPositionSpecified,
const bool& aSizeSpecified, nsIURI* aURIToLoad,
const nsCString& aFeatures, const float& aFullZoom,
const bool& aCalledFromJS, const bool& aWidthSpecified,
nsIURI* aURIToLoad, const nsCString& aFeatures, const float& aFullZoom,
uint64_t aNextRemoteTabId, const nsString& aName, nsresult& aResult,
nsCOMPtr<nsIRemoteTab>& aNewRemoteTab, bool* aWindowIsNew,
int32_t& aOpenLocation, nsIPrincipal* aTriggeringPrincipal,

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

@ -1354,8 +1354,7 @@ parent:
PBrowser aNewTab,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
bool aWidthSpecified,
URIParams? aURIToLoad,
nsCString aFeatures,
float aFullZoom,
@ -1368,8 +1367,7 @@ parent:
PBrowser aThisTab,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified,
bool aWidthSpecified,
URIParams? aURIToLoad,
nsCString aFeatures,
float aFullZoom,

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

@ -28,43 +28,106 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=642338
*/
// Popup is opened if one of the following condtitions is met:
// * both location and toolbar are false
// * menubar is false
// * resizable is false
// * scrollbars is false
// * status is false
// * width is specified
var featuresList = [
{
type: 'non-popup',
target: 'all-bars-toolbar',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=yes',
},
{
type: 'non-popup',
target: 'all-bars-location',
features: 'location=yes,menubar=yes,status=yes,scrollbars=yes',
},
{
type: 'popup',
target: 'no-menubar',
features: 'toolbar=yes,menubar=no,status=yes,scrollbars=yes',
},
{
type: 'popup',
target: 'no-toolbar',
features: 'toolbar=no,menubar=yes,status=yes,scrollbars=yes',
},
{
type: 'popup',
target: 'no-status',
features: 'toolbar=yes,menubar=yes,status=no,scrollbars=yes',
},
{
type: 'popup',
target: 'no-scrollbars',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=no',
},
{
type: 'popup',
target: 'no-bars',
features: 'toolbar=no,menubar=no,status=no,scrollbars=no',
},
{
type: 'popup',
target: 'no-resizable',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=yes'
+ ',resizable=no',
},
{
type: 'popup',
target: 'width-sized',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=yes'
+ ',width=500',
},
{
type: 'non-popup',
target: 'height-sized',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=yes'
+ ',height=500',
},
{
type: 'popup',
target: 'sized',
features: 'toolbar=yes,menubar=yes,status=yes,scrollbars=yes'
+ ',width=500,height=500',
},
];
var numWindows = 0;
/* Called when our popup loads. */
function testWindow(w)
{
// If dom.disable_window_open_feature.X is true, then we can't disable
// feature X when we call window.open from content. So to check that our popup
// has the right bars shown, we need to check that it obeys first the pref
// and then the arguments to window.open, if applicable.
function checkFeature(feature, prefname) {
if (prefname === undefined)
prefname = feature;
if (SpecialPowers.getBoolPref('dom.disable_window_open_feature.' + prefname)) {
is(w[feature].visible, true, feature + ' should always be true.');
}
else {
// w.location.search == '?true' if we expect the bars to be on, and
// '?false' otherwise. By default, no bars are enabled, so '?default'
// can be handled the same way as '?false'.
var enabled = w.location.search == '?true';
is(w[feature].visible, enabled, feature + ' should follow window.open settings.');
function checkFeature(feature) {
if (w.location.search.startsWith('?popup')) {
is(w[feature].visible, false, `${feature} should be hidden for popup (${w.name}).`);
} else {
is(w[feature].visible, true, `${feature} should be visible for non-popup (${w.name}).`);
}
}
// If popup is opened, the following properties become false:
// * menubar.visible
// * toolbar.visible
// * personalbar.visible
// * status.visible
checkFeature('menubar');
checkFeature('toolbar');
checkFeature('personalbar');
checkFeature('statusbar', 'status');
checkFeature('locationbar', 'location');
// The following aren't affected by feature.
is(w.scrollbars.visible, true, `scrollbars should always be visible (${w.name}).`);
is(w.statusbar.visible, true, `statusbar should always be visible (${w.name}).`);
is(w.locationbar.visible, true, `locationbar should always be visible (${w.name}).`);
w.close();
numWindows++;
if (numWindows == 3) {
if (numWindows == featuresList.length) {
// We're done!
SimpleTest.finish();
}
@ -72,24 +135,10 @@ function testWindow(w)
}
SimpleTest.waitForExplicitFinish();
// These will call back into testWindow when they open.
var allBarsWindow =
window.open('file_window_bar.html?true', 'all-bars',
'menubar=yes,toolbar=yes,location=yes,' +
'personalbar=yes,status=yes,scrollbars=yes',
true);
var noBarsWindow =
window.open('file_window_bar.html?false', 'no-bars',
'menubar=no,toolbar=no,location=no,' +
'personalbar=no,status=no,scrollbars=no',
false);
var defaultWindow =
window.open('file_window_bar.html?default', 'default-bars',
'width=500,height=500', false);
for (var item of featuresList) {
// This will call back into testWindow when they open.
window.open(`file_window_bar.html?${item.type}`, item.target, item.features);
}
</script>
</pre>

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

@ -15,7 +15,7 @@ SpecialPowers.pushPrefEnv(
{"set": [["layers.single-tile.enabled", false]]},
function() {
window.open("transformed_scrolling_repaints_3_window.html", "transformed_scrolling_repaints_3",
"chrome,width=300,height=400,scrollbars=no");
"chrome,width=350,height=450");
}
);
</script>

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

@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html>
<html style="overflow: hidden;">
<head>
<title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them</title>
<script src="/tests/SimpleTest/paint_listener.js"></script>

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

@ -49,11 +49,8 @@ interface nsIWindowProvider : nsISupports
* 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 aWidthSpecified Whether the attempt to create a window is trying
* to specify the width for the new window.
*
* @param aURI The URI to be loaded in the new window (may be NULL). The
* nsIWindowProvider implementation must not load this URI into the
@ -101,8 +98,7 @@ interface nsIWindowProvider : nsISupports
BrowsingContext provideWindow(in mozIDOMWindowProxy aParent,
in unsigned long aChromeFlags,
in boolean aCalledFromJS,
in boolean aPositionSpecified,
in boolean aSizeSpecified,
in boolean aWidthSpecified,
in nsIURI aURI,
in AString aName,
in AUTF8String aFeatures,

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

@ -343,9 +343,14 @@ struct SizeSpec {
bool PositionSpecified() const { return mLeftSpecified || mTopSpecified; }
bool SizeSpecified() const {
return mOuterWidthSpecified || mOuterHeightSpecified ||
mInnerWidthSpecified || mInnerHeightSpecified;
bool SizeSpecified() const { return WidthSpecified() || HeightSpecified(); }
bool WidthSpecified() const {
return mOuterWidthSpecified || mInnerWidthSpecified;
}
bool HeightSpecified() const {
return mOuterHeightSpecified || mInnerHeightSpecified;
}
};
@ -517,7 +522,10 @@ nsWindowWatcher::OpenWindowWithRemoteTab(
return NS_ERROR_UNEXPECTED;
}
uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures);
SizeSpec sizeSpec;
CalcSizeSpec(aFeatures, sizeSpec);
uint32_t chromeFlags = CalculateChromeFlagsForChild(aFeatures, sizeSpec);
// A content process has asked for a new window, which implies
// that the new window will need to be remote.
@ -564,8 +572,6 @@ nsWindowWatcher::OpenWindowWithRemoteTab(
MaybeDisablePersistence(aFeatures, chromeTreeOwner);
SizeSpec sizeSpec;
CalcSizeSpec(aFeatures, sizeSpec);
SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
Some(aOpenerFullZoom));
@ -685,16 +691,19 @@ nsresult nsWindowWatcher::OpenWindowInternal(
bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
SizeSpec sizeSpec;
CalcSizeSpec(features, sizeSpec);
// 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,
chromeFlags = CalculateChromeFlagsForParent(aParent, features, sizeSpec,
aDialog, uriToLoadIsChrome,
hasChromeParent, aCalledFromJS);
} else {
chromeFlags = CalculateChromeFlagsForChild(features);
chromeFlags = CalculateChromeFlagsForChild(features, sizeSpec);
if (aDialog) {
MOZ_ASSERT(XRE_IsParentProcess());
@ -716,9 +725,6 @@ nsresult nsWindowWatcher::OpenWindowInternal(
}
}
SizeSpec sizeSpec;
CalcSizeSpec(features, sizeSpec);
// 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
// GetSubjectPrincipal()?
@ -803,9 +809,9 @@ nsresult nsWindowWatcher::OpenWindowInternal(
if (provider) {
rv = provider->ProvideWindow(
aParent, chromeFlags, aCalledFromJS, sizeSpec.PositionSpecified(),
sizeSpec.SizeSpecified(), uriToLoad, name, features, aForceNoOpener,
aForceNoReferrer, aLoadState, &windowIsNew, getter_AddRefs(newBC));
aParent, chromeFlags, aCalledFromJS, sizeSpec.WidthSpecified(),
uriToLoad, name, features, aForceNoOpener, aForceNoReferrer,
aLoadState, &windowIsNew, getter_AddRefs(newBC));
if (NS_SUCCEEDED(rv) && newBC) {
nsCOMPtr<nsIDocShell> newDocShell = newBC->GetDocShell();
@ -1646,37 +1652,17 @@ nsresult 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 && !aHasChromeParent && !aChromeURL) { \
chromeFlags |= flag; \
} else { \
chromeFlags |= \
WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \
}
#define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \
chromeFlags |= \
WinHasOption(aFeatures, (feature), 0, &presenceFlag) ? (flag) : 0;
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsHelper(
uint32_t aInitialFlags, const nsACString& aFeatures, bool& presenceFlag,
bool aDialog, bool aHasChromeParent, bool aChromeURL) {
uint32_t aInitialFlags, const nsACString& aFeatures,
const SizeSpec& aSizeSpec, bool& presenceFlag, bool aHasChromeParent) {
uint32_t chromeFlags = aInitialFlags;
nsresult rv;
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefs =
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nsIWebBrowserChrome::CHROME_DEFAULT);
rv = prefs->GetBranch("dom.disable_window_open_feature.",
getter_AddRefs(prefBranch));
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 requires aFeatures, presenceFlag, and
// chromeFlags to be in scope.
NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
nsIWebBrowserChrome::CHROME_TITLEBAR);
@ -1702,7 +1688,33 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsHelper(
}
presenceFlag = presenceFlag || scrollbarsPresent;
return chromeFlags;
if (aHasChromeParent) {
return chromeFlags;
}
// Web content isn't allowed to control UI visibility separately, but only
// whether to open a popup or not.
//
// The above code is still necessary to calculate `presenceFlag`.
// (`ShouldOpenPopup` early returns and doesn't check all feature)
if (ShouldOpenPopup(aFeatures, aSizeSpec)) {
// Flags for opening a popup, that doesn't have the following:
// * nsIWebBrowserChrome::CHROME_TOOLBAR
// * nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR
// * nsIWebBrowserChrome::CHROME_MENUBAR
return aInitialFlags | nsIWebBrowserChrome::CHROME_TITLEBAR |
nsIWebBrowserChrome::CHROME_WINDOW_CLOSE |
nsIWebBrowserChrome::CHROME_LOCATIONBAR |
nsIWebBrowserChrome::CHROME_STATUSBAR |
nsIWebBrowserChrome::CHROME_WINDOW_RESIZE |
nsIWebBrowserChrome::CHROME_WINDOW_MIN |
nsIWebBrowserChrome::CHROME_SCROLLBARS;
}
// Otherwise open the current/new tab in the current/new window
// (depends on browser.link.open_newwindow).
return aInitialFlags | nsIWebBrowserChrome::CHROME_ALL;
}
// static
@ -1729,23 +1741,68 @@ uint32_t nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags,
return aChromeFlags;
}
// static
bool nsWindowWatcher::ShouldOpenPopup(const nsACString& aFeatures,
const SizeSpec& aSizeSpec) {
if (aFeatures.IsVoid()) {
return false;
}
// Follow Google Chrome's behavior that opens a popup depending on
// the following features.
bool unused;
if (!WinHasOption(aFeatures, "location", 0, &unused) &&
!WinHasOption(aFeatures, "toolbar", 0, &unused)) {
return true;
}
if (!WinHasOption(aFeatures, "menubar", 0, &unused)) {
return true;
}
// `resizable` defaults to true.
// Should open popup only when explicitly specified to 0.
bool resizablePresent = false;
if (!WinHasOption(aFeatures, "resizable", 0, &resizablePresent) &&
resizablePresent) {
return true;
}
if (!WinHasOption(aFeatures, "scrollbars", 0, &unused)) {
return true;
}
if (!WinHasOption(aFeatures, "status", 0, &unused)) {
return true;
}
// Follow Safari's behavior that opens a popup when width is specified.
if (aSizeSpec.WidthSpecified()) {
return true;
}
return false;
}
/**
* 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.
* from a child process. The feature string can only control whether to open a
* new tab or a new popup.
* @param aFeatures a string containing a list of named features
* @param aSizeSpec the result of CalcSizeSpec
* @return the chrome bitmask
*/
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsForChild(
const nsACString& aFeatures) {
const nsACString& aFeatures, const SizeSpec& aSizeSpec) {
if (aFeatures.IsVoid()) {
return nsIWebBrowserChrome::CHROME_ALL;
}
bool presenceFlag = false;
uint32_t chromeFlags = CalculateChromeFlagsHelper(
nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, presenceFlag);
uint32_t chromeFlags =
CalculateChromeFlagsHelper(nsIWebBrowserChrome::CHROME_WINDOW_BORDERS,
aFeatures, aSizeSpec, presenceFlag);
return EnsureFlagsSafeForContent(chromeFlags);
}
@ -1763,8 +1820,9 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForChild(
*/
// static
uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
mozIDOMWindowProxy* aParent, const nsACString& aFeatures, bool aDialog,
bool aChromeURL, bool aHasChromeParent, bool aCalledFromJS) {
mozIDOMWindowProxy* aParent, const nsACString& aFeatures,
const SizeSpec& aSizeSpec, bool aDialog, bool aChromeURL,
bool aHasChromeParent, bool aCalledFromJS) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
@ -1796,9 +1854,8 @@ uint32_t nsWindowWatcher::CalculateChromeFlagsForParent(
}
/* Next, allow explicitly named options to override the initial settings */
chromeFlags =
CalculateChromeFlagsHelper(chromeFlags, aFeatures, presenceFlag, aDialog,
aHasChromeParent, aChromeURL);
chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, aSizeSpec,
presenceFlag, aHasChromeParent);
// Determine whether the window is a private browsing window
chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag)
@ -2402,8 +2459,7 @@ void nsWindowWatcher::GetWindowTreeOwner(nsPIDOMWindowOuter* aWindow,
int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified) {
bool aWidthSpecified) {
bool isFullScreen = aParent->GetFullScreen();
// Where should we open this?
@ -2453,7 +2509,7 @@ int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
}
if (restrictionPref == 2) {
// Only continue if there are no size/position features and no special
// Only continue if there are no width feature and no special
// chrome flags - with the exception of the remoteness and private flags,
// which might have been automatically flipped by Gecko.
int32_t uiChromeFlags = aChromeFlags;
@ -2462,8 +2518,7 @@ int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
aPositionSpecified || aSizeSpecified) {
if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL || aWidthSpecified) {
return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
}
}

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

@ -53,8 +53,7 @@ class nsWindowWatcher : public nsIWindowWatcher,
static int32_t GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
uint32_t aChromeFlags,
bool aCalledFromJS,
bool aPositionSpecified,
bool aSizeSpecified);
bool aWidthSpecified);
// Will first look for a caller on the JS stack, and then fall back on
// aCurrentContext if it can't find one.
@ -86,10 +85,15 @@ class nsWindowWatcher : public nsIWindowWatcher,
static nsresult URIfromURL(const char* aURL, mozIDOMWindowProxy* aParent,
nsIURI** aURI);
static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr);
static bool ShouldOpenPopup(const nsACString& aFeatures,
const SizeSpec& aSizeSpec);
static uint32_t CalculateChromeFlagsForChild(const nsACString& aFeaturesStr,
const SizeSpec& aSizeSpec);
static uint32_t CalculateChromeFlagsForParent(mozIDOMWindowProxy* aParent,
const nsACString& aFeaturesStr,
const SizeSpec& aSizeSpec,
bool aDialog, bool aChromeURL,
bool aHasChromeParent,
bool aCalledFromJS);
@ -121,10 +125,9 @@ class nsWindowWatcher : public nsIWindowWatcher,
static uint32_t CalculateChromeFlagsHelper(uint32_t aInitialFlags,
const nsACString& aFeatures,
const SizeSpec& aSizeSpec,
bool& presenceFlag,
bool aDialog = false,
bool aHasChromeParent = false,
bool aChromeURL = false);
bool aHasChromeParent = false);
static uint32_t EnsureFlagsSafeForContent(uint32_t aChromeFlags,
bool aChromeURL = false);

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

@ -1,5 +1,7 @@
[DEFAULT]
tags = openwindow
support-files =
head.js
[browser_new_content_window_chromeflags.js]
[browser_new_remote_window_flags.js]
@ -7,3 +9,6 @@ run-if = e10s
[browser_new_content_window_from_chrome_principal.js]
[browser_new_sized_window.js]
skip-if = os == 'win' # Bug 1276802 - Opening windows from content on Windows might not get the size right
[browser_popup_condition.js]
skip-if = debug # Opening many windows takes long on debug build
[browser_non_popup_from_popup.js]

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

@ -3,49 +3,12 @@
* being opened from content.
*/
// The following features set chrome flags on new windows and are
// supported by web content. The schema for each property on this
// object is as follows:
// The following are not allowed from web content.
//
// <feature string>: {
// flag: <associated nsIWebBrowserChrome flag>,
// defaults_to: <what this feature defaults to normally>
// }
const ALLOWED = {
toolbar: {
flag: Ci.nsIWebBrowserChrome.CHROME_TOOLBAR,
defaults_to: true,
},
personalbar: {
flag: Ci.nsIWebBrowserChrome.CHROME_PERSONAL_TOOLBAR,
defaults_to: true,
},
menubar: {
flag: Ci.nsIWebBrowserChrome.CHROME_MENUBAR,
defaults_to: true,
},
scrollbars: {
flag: Ci.nsIWebBrowserChrome.CHROME_SCROLLBARS,
defaults_to: false,
},
minimizable: {
flag: Ci.nsIWebBrowserChrome.CHROME_WINDOW_MIN,
defaults_to: true,
},
};
// Construct a features string that flips all ALLOWED features
// to not be their defaults.
const ALLOWED_STRING = Object.keys(ALLOWED)
.map(feature => {
let toValue = ALLOWED[feature].defaults_to ? "no" : "yes";
return `${feature}=${toValue}`;
})
.join(",");
// The following are not allowed from web content, at least
// in the default case (since some are disabled by default
// via the dom.disable_window_open_feature pref branch).
const DISALLOWED = {
location: {
flag: Ci.nsIWebBrowserChrome.CHROME_LOCATIONBAR,
@ -140,21 +103,6 @@ registerCleanupFunction(() => {
Services.prefs.clearUserPref("browser.link.open_newwindow");
});
/**
* Given some nsIDOMWindow for a window running in the parent
* process, return the nsIWebBrowserChrome chrome flags for
* the associated XUL window.
*
* @param win (nsIDOMWindow)
* Some window in the parent process.
* @returns int
*/
function getParentChromeFlags(win) {
return win.docShell.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIAppWindow).chromeFlags;
}
/**
* Given some nsIDOMWindow for a window running in the parent process,
* asynchronously return the nsIWebBrowserChrome chrome flags for the
@ -184,34 +132,13 @@ function getContentChromeFlags(win) {
}
/**
* For some chromeFlags, ensures that flags that are in the
* ALLOWED group were modified, and that flags in the DISALLOWED
* For some chromeFlags, ensures that flags in the DISALLOWED
* group were not modified.
*
* @param chromeFlags (int)
* Some chromeFlags to check.
*/
function assertContentFlags(chromeFlags) {
for (let feature in ALLOWED) {
let flag = ALLOWED[feature].flag;
if (ALLOWED[feature].defaults_to) {
// The feature is supposed to default to true, so we should
// have been able to flip it off.
Assert.ok(
!(chromeFlags & flag),
`Expected feature ${feature} to be disabled`
);
} else {
// The feature is supposed to default to false, so we should
// have been able to flip it on.
Assert.ok(
chromeFlags & flag,
`Expected feature ${feature} to be enabled`
);
}
}
function assertContentFlags(chromeFlags, isPopup) {
for (let feature in DISALLOWED) {
let flag = DISALLOWED[feature].flag;
Assert.ok(flag, "Expected flag to be a non-zeroish value");
@ -235,11 +162,10 @@ function assertContentFlags(chromeFlags) {
/**
* Opens a window from content using window.open with the
* features computed from ALLOWED and DISALLOWED. The computed
* feature string attempts to flip every feature away from their
* default.
* features computed from DISALLOWED. The computed feature string attempts to
* flip every feature away from their default.
*/
add_task(async function test_new_remote_window_flags() {
add_task(async function test_disallowed_flags() {
// Construct a features string that flips all DISALLOWED features
// to not be their defaults.
const DISALLOWED_STRING = Object.keys(DISALLOWED)
@ -249,7 +175,7 @@ add_task(async function test_new_remote_window_flags() {
})
.join(",");
const FEATURES = [ALLOWED_STRING, DISALLOWED_STRING].join(",");
const FEATURES = [DISALLOWED_STRING].join(",");
const SCRIPT_PAGE = `data:text/html,<script>window.open("about:blank", "_blank", "${FEATURES}");</script>`;
const SCRIPT_PAGE_FOR_CHROME_ALL = `data:text/html,<script>window.open("about:blank", "_blank", "all");</script>`;

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

@ -0,0 +1,53 @@
"use strict";
// Opening non-popup from a popup should open a new tab in the most recent
// non-popup window.
add_task(async function test_non_popup_from_popup() {
const BLANK_PAGE = "data:text/html,";
// A page opened in a new tab.
const OPEN_PAGE = "data:text/plain,hello";
// A page opened in a new popup.
// This opens a new non-popup page with OPEN_PAGE,
// tha should be opened in a new tab in most recent window.
const NON_POPUP_OPENER = btoa(
`data:text/html,<script>window.open('${OPEN_PAGE}', '', '')</script>`
);
// A page opened in a new tab.
// This opens a popup with NON_POPUP_OPENER.
const POPUP_OPENER = `data:text/html,<script>window.open(atob("${NON_POPUP_OPENER}"), "", "width=500");</script>`;
await SpecialPowers.pushPrefEnv({
set: [["browser.link.open_newwindow", 3]],
});
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE,
},
async function(browser) {
// Wait for a popup opened by POPUP_OPENER.
const newPopupPromise = BrowserTestUtils.waitForNewWindow();
// Wait for a new tab opened by NON_POPUP_OPENER.
const newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, OPEN_PAGE);
// Open a new tab that opens a popup with NON_POPUP_OPENER.
await BrowserTestUtils.loadURI(gBrowser, POPUP_OPENER);
let win = await newPopupPromise;
Assert.ok(true, "popup is opened");
let tab = await newTabPromise;
Assert.ok(true, "new tab is opened in the recent window");
BrowserTestUtils.removeTab(tab);
await BrowserTestUtils.closeWindow(win);
}
);
await SpecialPowers.popPrefEnv();
});

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

@ -0,0 +1,308 @@
"use strict";
requestLongerTimeout(5);
/**
* Opens windows from content using window.open with several features patterns.
*/
add_task(async function test_popup_conditions() {
const PATTERNS = [
{ features: "", popup: false },
// If features isn't empty, the following should be true to open tab/window:
// * location or toolbar (defaults to false)
// * menubar (defaults to false)
// * resizable (defaults to true)
// * scrollbars (defaults to false)
// * status (defaults to false)
// and also the following shouldn't be specified:
// * left or screenX
// * top or screenY
// * width or innerWidth
// * outerWidth
// * height or innerHeight
// * outerHeight
{ features: "location,menubar,resizable,scrollbars,status", popup: false },
{ features: "toolbar,menubar,resizable,scrollbars,status", popup: false },
{
features: "location,menubar,toolbar,resizable,scrollbars,status",
popup: false,
},
// resizable defaults to true.
{ features: "location,menubar,scrollbars,status", popup: false },
// The following testcases use "location,menubar,scrollbars,status"
// as the base non-popup case, and test the boundary between popup
// vs non-popup.
// If either location or toolbar is true, not popup.
{
features: "location=0,toolbar,menubar,resizable,scrollbars,status",
popup: false,
},
{
features: "location,toolbar=0,menubar,resizable,scrollbars,status",
popup: false,
},
{
features: "location,toolbar,menubar,resizable,scrollbars,status",
popup: false,
},
// If both location and toolbar are false, popup.
{
features: "location=0,toolbar=0,menubar,resizable,scrollbars,status",
popup: true,
},
{ features: "menubar,scrollbars,status", popup: true },
// If menubar is false, popup.
{ features: "location,resizable,scrollbars,status", popup: true },
{ features: "location,menubar=0,resizable,scrollbars,status", popup: true },
// If resizable is true, not popup.
{
features: "location,menubar,resizable=yes,scrollbars,status",
popup: false,
},
// If resizable is false, popup.
{ features: "location,menubar,resizable=0,scrollbars,status", popup: true },
// If scrollbars is false, popup.
{ features: "location,menubar,resizable,status", popup: true },
{ features: "location,menubar,resizable,scrollbars=0,status", popup: true },
// If status is false, popup.
{ features: "location,menubar,resizable,scrollbars", popup: true },
{ features: "location,menubar,resizable,scrollbars,status=0", popup: true },
// If either width or innerWidth is specified, popup.
{ features: "location,menubar,scrollbars,status,width=100", popup: true },
{
features: "location,menubar,scrollbars,status,innerWidth=100",
popup: true,
},
// If outerWidth is specified, popup.
{
features: "location,menubar,scrollbars,status,outerWidth=100",
popup: true,
},
// left or screenX alone doesn't make it a popup.
{
features: "location,menubar,scrollbars,status,left=100",
popup: false,
},
{
features: "location,menubar,scrollbars,status,screenX=100",
popup: false,
},
// top or screenY alone doesn't make it a popup.
{
features: "location,menubar,scrollbars,status,top=100",
popup: false,
},
{
features: "location,menubar,scrollbars,status,screenY=100",
popup: false,
},
// height or innerHeight or outerHeight alone doesn't make it a popup.
{
features: "location,menubar,scrollbars,status,height=100",
popup: false,
},
{
features: "location,menubar,scrollbars,status,innerHeight=100",
popup: false,
},
{
features: "location,menubar,scrollbars,status,outerHeight=100",
popup: false,
},
// Most feature defaults to false if the feature is not empty.
// Specifying only some of them opens a popup.
{ features: "location", popup: true },
{ features: "toolbar", popup: true },
{ features: "location,toolbar", popup: true },
{ features: "menubar", popup: true },
{ features: "location,toolbar,menubar", popup: true },
{ features: "resizable", popup: true },
{ features: "scrollbars", popup: true },
{ features: "status", popup: true },
{ features: "left=500", popup: true },
{ features: "top=500", popup: true },
{ features: "left=500,top=500", popup: true },
{ features: "width=500", popup: true },
{ features: "height=500", popup: true },
{ features: "width=500, height=500", popup: true },
// Specifying unknown feature makes the feature not empty.
{ features: "someunknownfeature", popup: true },
// personalbar have no effect.
{ features: "personalbar=0", popup: true },
{ features: "personalbar=yes", popup: true },
{
features: "location,menubar,resizable,scrollbars,status,personalbar=0",
popup: false,
},
{
features: "location,menubar,resizable,scrollbars,status,personalbar=yes",
popup: false,
},
// noopener is removed before testing if feature is empty.
{ features: "noopener", popup: false },
// noreferrer is removed before testing if feature is empty.
{ features: "noreferrer", popup: false },
];
const WINDOW_FLAGS = {
CHROME_WINDOW_BORDERS: true,
CHROME_WINDOW_CLOSE: true,
CHROME_WINDOW_RESIZE: true,
CHROME_LOCATIONBAR: true,
CHROME_STATUSBAR: true,
CHROME_SCROLLBARS: true,
CHROME_TITLEBAR: true,
CHROME_MENUBAR: true,
CHROME_TOOLBAR: true,
CHROME_PERSONAL_TOOLBAR: true,
};
const POPUP_FLAGS = {
CHROME_WINDOW_BORDERS: true,
CHROME_WINDOW_CLOSE: true,
CHROME_WINDOW_RESIZE: true,
CHROME_LOCATIONBAR: true,
CHROME_STATUSBAR: true,
CHROME_SCROLLBARS: true,
CHROME_TITLEBAR: true,
CHROME_MENUBAR: false,
CHROME_TOOLBAR: false,
CHROME_PERSONAL_TOOLBAR: false,
};
async function test_patterns({ nonPopup }) {
for (const { features, popup } of PATTERNS) {
const BLANK_PAGE = "data:text/html,";
const OPEN_PAGE = "data:text/plain,hello";
const SCRIPT_PAGE = `data:text/html,<script>window.open("${OPEN_PAGE}", "", "${features}");</script>`;
async function testNewWindow(flags) {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE,
},
async function(browser) {
const newWinPromise = BrowserTestUtils.waitForNewWindow();
await BrowserTestUtils.loadURI(gBrowser, SCRIPT_PAGE);
const win = await newWinPromise;
const parentChromeFlags = getParentChromeFlags(win);
for (const [name, visible] of Object.entries(flags)) {
if (visible) {
Assert.equal(
!!(parentChromeFlags & Ci.nsIWebBrowserChrome[name]),
true,
`${name} should be present for features "${features}"`
);
} else {
Assert.equal(
!!(parentChromeFlags & Ci.nsIWebBrowserChrome[name]),
false,
`${name} should not be present for features "${features}"`
);
}
}
await BrowserTestUtils.closeWindow(win);
}
);
}
async function testNewTab() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE,
},
async function(browser) {
const newTabPromise = BrowserTestUtils.waitForNewTab(
gBrowser,
OPEN_PAGE
);
await BrowserTestUtils.loadURI(gBrowser, SCRIPT_PAGE);
let tab = await newTabPromise;
BrowserTestUtils.removeTab(tab);
}
);
}
async function testCurrentTab() {
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE,
},
async function(browser) {
const pagePromise = BrowserTestUtils.browserLoaded(
browser,
false,
OPEN_PAGE
);
await BrowserTestUtils.loadURI(gBrowser, SCRIPT_PAGE);
await pagePromise;
}
);
}
if (!popup) {
if (nonPopup == "window") {
await testNewWindow(WINDOW_FLAGS);
} else if (nonPopup == "tab") {
await testNewTab();
} else {
// current tab
await testCurrentTab();
}
} else {
await testNewWindow(POPUP_FLAGS);
}
}
}
// Non-popup is opened in a new tab (default behavior).
await SpecialPowers.pushPrefEnv({
set: [["browser.link.open_newwindow", 3]],
});
await test_patterns({ nonPopup: "tab" });
await SpecialPowers.popPrefEnv();
// Non-popup is opened in a current tab.
await SpecialPowers.pushPrefEnv({
set: [["browser.link.open_newwindow", 1]],
});
await test_patterns({ nonPopup: "current" });
await SpecialPowers.popPrefEnv();
// Non-popup is opened in a new window.
await SpecialPowers.pushPrefEnv({
set: [["browser.link.open_newwindow", 2]],
});
await test_patterns({ nonPopup: "window" });
await SpecialPowers.popPrefEnv();
});

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

@ -0,0 +1,14 @@
/**
* Given some nsIDOMWindow for a window running in the parent
* process, return the nsIWebBrowserChrome chrome flags for
* the associated XUL window.
*
* @param win (nsIDOMWindow)
* Some window in the parent process.
* @returns int
*/
function getParentChromeFlags(win) {
return win.docShell.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIAppWindow).chromeFlags;
}

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

@ -620,9 +620,9 @@ NS_IMETHODIMP nsContentTreeOwner::SetTitle(const nsAString& aTitle) {
NS_IMETHODIMP
nsContentTreeOwner::ProvideWindow(
mozIDOMWindowProxy* aParent, uint32_t aChromeFlags, bool aCalledFromJS,
bool aPositionSpecified, bool aSizeSpecified, nsIURI* aURI,
const nsAString& aName, const nsACString& aFeatures, bool aForceNoOpener,
bool aForceNoReferrer, nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
bool aWidthSpecified, nsIURI* aURI, const nsAString& aName,
const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer,
nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
BrowsingContext** aReturn) {
NS_ENSURE_ARG_POINTER(aParent);
@ -683,8 +683,7 @@ nsContentTreeOwner::ProvideWindow(
}
int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
parentWin, aChromeFlags, aCalledFromJS, aPositionSpecified,
aSizeSpecified);
parentWin, aChromeFlags, aCalledFromJS, aWidthSpecified);
if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB &&
openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {