mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 391834, don't allow prompts in beforeunload, unload and pagehide events,r=smaug,patch mostly by gavin
This commit is contained in:
parent
afbbe47319
commit
1a89394d0d
@ -1616,6 +1616,11 @@
|
||||
evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0);
|
||||
aTab.dispatchEvent(evt);
|
||||
|
||||
// Prevent this tab from showing further dialogs, since we're closing it
|
||||
var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.preventFurtherDialogs();
|
||||
|
||||
// Remove the tab's filter and progress listener.
|
||||
const filter = this.mTabFilters[aTab._tPos];
|
||||
#ifdef MOZ_E10S_COMPAT
|
||||
|
@ -199,6 +199,7 @@ _BROWSER_FILES = \
|
||||
browser_tabfocus.js \
|
||||
browser_tabs_isActive.js \
|
||||
browser_tabs_owner.js \
|
||||
browser_unloaddialogs.js \
|
||||
browser_urlbarAutoFillTrimURLs.js \
|
||||
browser_urlbarCopying.js \
|
||||
browser_urlbarEnter.js \
|
||||
|
134
browser/base/content/test/browser_unloaddialogs.js
Normal file
134
browser/base/content/test/browser_unloaddialogs.js
Normal file
@ -0,0 +1,134 @@
|
||||
function notify(event)
|
||||
{
|
||||
if (event.target.location == "about:blank")
|
||||
return;
|
||||
|
||||
var eventname = event.type;
|
||||
if (eventname == "pagehide")
|
||||
details.pagehides++;
|
||||
else if (eventname == "beforeunload")
|
||||
details.beforeunloads++;
|
||||
else if (eventname == "unload")
|
||||
details.unloads++;
|
||||
}
|
||||
|
||||
var details;
|
||||
|
||||
var gUseFrame = false;
|
||||
|
||||
const windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
|
||||
|
||||
const TEST_BASE_URL = "data:text/html,<script>" +
|
||||
"function note(event) { try { alert(event.type); } catch(ex) { return; } throw 'alert appeared'; }" +
|
||||
"</script>" +
|
||||
"<body onpagehide='note(event)' onbeforeunload='alert(event.type);' onunload='note(event)'>";
|
||||
|
||||
const TEST_URL = TEST_BASE_URL + "Test</body>";
|
||||
const TEST_FRAME_URL = TEST_BASE_URL + "Frames</body>";
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
windowMediator.addListener(promptListener);
|
||||
runTest();
|
||||
}
|
||||
|
||||
function runTest()
|
||||
{
|
||||
details = {
|
||||
testNumber : 0,
|
||||
beforeunloads : 0,
|
||||
pagehides : 0,
|
||||
unloads : 0,
|
||||
prompts : 0
|
||||
};
|
||||
|
||||
var tab = gBrowser.addTab(TEST_URL);
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.linkedBrowser.addEventListener("pageshow", shown, true);
|
||||
|
||||
tab.linkedBrowser.addEventListener("pagehide", notify, true);
|
||||
tab.linkedBrowser.addEventListener("beforeunload", notify, true);
|
||||
tab.linkedBrowser.addEventListener("unload", notify, true);
|
||||
}
|
||||
|
||||
function shown(event)
|
||||
{
|
||||
if (details.testNumber == 0) {
|
||||
var browser;
|
||||
var iframe;
|
||||
if (gUseFrame) {
|
||||
iframe = event.target.createElement("iframe");
|
||||
iframe.src = TEST_FRAME_URL;
|
||||
event.target.documentElement.appendChild(iframe);
|
||||
browser = iframe.contentWindow;
|
||||
}
|
||||
else {
|
||||
browser = gBrowser.selectedTab.linkedBrowser;
|
||||
details.testNumber = 1; // Move onto to the next step immediately
|
||||
}
|
||||
}
|
||||
|
||||
if (details.testNumber == 1) {
|
||||
// Test going to another page
|
||||
executeSoon(function () {
|
||||
const urlToLoad = "data:text/html,<body>Another Page</body>";
|
||||
if (gUseFrame) {
|
||||
event.target.location = urlToLoad;
|
||||
}
|
||||
else {
|
||||
gBrowser.selectedBrowser.loadURI(urlToLoad);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (details.testNumber == 2) {
|
||||
is(details.pagehides, 1, "pagehides after next page")
|
||||
is(details.beforeunloads, 1, "beforeunloads after next page")
|
||||
is(details.unloads, 1, "unloads after next page")
|
||||
is(details.prompts, 1, "prompts after next page")
|
||||
|
||||
executeSoon(function () gUseFrame ? gBrowser.goBack() : event.target.defaultView.back());
|
||||
}
|
||||
else if (details.testNumber == 3) {
|
||||
is(details.pagehides, 2, "pagehides after back")
|
||||
is(details.beforeunloads, 2, "beforeunloads after back")
|
||||
// No cache, so frame is unloaded
|
||||
is(details.unloads, gUseFrame ? 2 : 1, "unloads after back")
|
||||
is(details.prompts, 1, "prompts after back")
|
||||
|
||||
// Test closing the tab
|
||||
gBrowser.selectedBrowser.removeEventListener("pageshow", shown, true);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// When the frame is present, there is are two beforeunload and prompts,
|
||||
// one for the frame and the other for the parent.
|
||||
is(details.pagehides, 3, "pagehides after close")
|
||||
is(details.beforeunloads, gUseFrame ? 4 : 3, "beforeunloads after close")
|
||||
is(details.unloads, gUseFrame ? 3 : 2, "unloads after close")
|
||||
is(details.prompts, gUseFrame ? 3 : 2, "prompts after close")
|
||||
|
||||
// Now run the test again using a child frame.
|
||||
if (gUseFrame) {
|
||||
windowMediator.removeListener(promptListener);
|
||||
finish();
|
||||
}
|
||||
else {
|
||||
gUseFrame = true;
|
||||
runTest();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
details.testNumber++;
|
||||
}
|
||||
|
||||
var promptListener = {
|
||||
onWindowTitleChange: function () {},
|
||||
onOpenWindow: function (win) {
|
||||
details.prompts++;
|
||||
let domWin = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
|
||||
executeSoon(function () { domWin.close() });
|
||||
},
|
||||
onCloseWindow: function () {},
|
||||
};
|
@ -62,9 +62,6 @@ function testLeavePage() {
|
||||
let dialogsAccepted = 0;
|
||||
|
||||
whenDialogOpened(function onDialogOpened(dialog) {
|
||||
if (++dialogsAccepted < 3)
|
||||
whenDialogOpened(onDialogOpened);
|
||||
|
||||
// Leave page
|
||||
dialog.acceptDialog();
|
||||
});
|
||||
|
@ -27,7 +27,7 @@ class nsDOMNavigationTiming;
|
||||
[ptr] native nsIViewPtr(nsIView);
|
||||
[ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
|
||||
|
||||
[scriptable, builtinclass, uuid(26b2380b-4a1a-46cd-b7d8-7600e41c1688)]
|
||||
[scriptable, builtinclass, uuid(b9d92b8b-5623-4079-ae11-36bb341f322e)]
|
||||
interface nsIContentViewer : nsISupports
|
||||
{
|
||||
|
||||
@ -134,7 +134,7 @@ interface nsIContentViewer : nsISupports
|
||||
*/
|
||||
void clearHistoryEntry();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Change the layout to view the document with page layout (like print preview), but
|
||||
* dynamic and editable (like Galley layout).
|
||||
*/
|
||||
@ -146,7 +146,7 @@ interface nsIContentViewer : nsISupports
|
||||
*/
|
||||
readonly attribute nsISHEntry historyEntry;
|
||||
|
||||
/*
|
||||
/**
|
||||
* Indicates when we're in a state where content shouldn't be allowed to
|
||||
* trigger a tab-modal prompt (as opposed to a window-modal prompt) because
|
||||
* we're part way through some operation (eg beforeunload) that shouldn't be
|
||||
@ -155,6 +155,11 @@ interface nsIContentViewer : nsISupports
|
||||
*/
|
||||
readonly attribute boolean isTabModalPromptAllowed;
|
||||
|
||||
/**
|
||||
* Returns whether this content viewer is in a hidden state.
|
||||
*/
|
||||
readonly attribute boolean isHidden;
|
||||
|
||||
[noscript] readonly attribute nsIPresShellPtr presShell;
|
||||
[noscript] readonly attribute nsPresContextPtr presContext;
|
||||
// aDocument must not be null.
|
||||
|
@ -2287,6 +2287,22 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::PreventFurtherDialogs()
|
||||
{
|
||||
// Permanently disable further dialogs for this window.
|
||||
|
||||
if (!IsUniversalXPConnectCapable()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
|
||||
static_cast<nsGlobalWindow*>(window.get())->PreventFurtherDialogs(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetFileOrBlob(const nsAString& aName, const jsval& aBlobParts,
|
||||
const jsval& aParameters, JSContext* aCx,
|
||||
|
@ -684,7 +684,8 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
||||
mCleanedUp(false),
|
||||
mCallCleanUpAfterModalDialogCloses(false),
|
||||
mDialogAbuseCount(0),
|
||||
mStopAbuseDialogs(false)
|
||||
mStopAbuseDialogs(false),
|
||||
mDialogsPermanentlyDisabled(false)
|
||||
{
|
||||
nsLayoutStatics::AddRef();
|
||||
|
||||
@ -2566,6 +2567,22 @@ nsGlobalWindow::DialogsAreBlocked(bool *aBeingAbused)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (topWindow->mDialogsPermanentlyDisabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Dialogs are blocked if the content viewer is hidden
|
||||
if (mDocShell) {
|
||||
nsCOMPtr<nsIContentViewer> cv;
|
||||
mDocShell->GetContentViewer(getter_AddRefs(cv));
|
||||
|
||||
bool isHidden;
|
||||
cv->GetIsHidden(&isHidden);
|
||||
if (isHidden) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*aBeingAbused = topWindow->DialogsAreBeingAbused();
|
||||
|
||||
return topWindow->mStopAbuseDialogs && *aBeingAbused;
|
||||
@ -2625,7 +2642,7 @@ nsGlobalWindow::ConfirmDialogIfNeeded()
|
||||
"ScriptDialogPreventTitle", title);
|
||||
promptSvc->Confirm(this, title.get(), label.get(), &disableDialog);
|
||||
if (disableDialog) {
|
||||
PreventFurtherDialogs();
|
||||
PreventFurtherDialogs(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2633,7 +2650,7 @@ nsGlobalWindow::ConfirmDialogIfNeeded()
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::PreventFurtherDialogs()
|
||||
nsGlobalWindow::PreventFurtherDialogs(bool aPermanent)
|
||||
{
|
||||
nsGlobalWindow *topWindow = GetScriptableTop();
|
||||
if (!topWindow) {
|
||||
@ -2644,6 +2661,9 @@ nsGlobalWindow::PreventFurtherDialogs()
|
||||
topWindow = topWindow->GetCurrentInnerWindowInternal();
|
||||
if (topWindow) {
|
||||
topWindow->mStopAbuseDialogs = true;
|
||||
if (aPermanent) {
|
||||
topWindow->mDialogsPermanentlyDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4847,7 +4867,7 @@ nsGlobalWindow::Alert(const nsAString& aString)
|
||||
rv = prompt->AlertCheck(title.get(), final.get(), label.get(),
|
||||
&disallowDialog);
|
||||
if (disallowDialog)
|
||||
PreventFurtherDialogs();
|
||||
PreventFurtherDialogs(false);
|
||||
} else {
|
||||
rv = prompt->Alert(title.get(), final.get());
|
||||
}
|
||||
@ -4914,7 +4934,7 @@ nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
|
||||
rv = prompt->ConfirmCheck(title.get(), final.get(), label.get(),
|
||||
&disallowDialog, aReturn);
|
||||
if (disallowDialog)
|
||||
PreventFurtherDialogs();
|
||||
PreventFurtherDialogs(false);
|
||||
} else {
|
||||
rv = prompt->Confirm(title.get(), final.get(), aReturn);
|
||||
}
|
||||
@ -4990,7 +5010,7 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
|
||||
&inoutValue, label.get(), &disallowDialog, &ok);
|
||||
|
||||
if (disallowDialog) {
|
||||
PreventFurtherDialogs();
|
||||
PreventFurtherDialogs(false);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -453,7 +453,7 @@ public:
|
||||
bool ConfirmDialogIfNeeded();
|
||||
|
||||
// Prevent further dialogs in this (top level) window
|
||||
void PreventFurtherDialogs();
|
||||
void PreventFurtherDialogs(bool aPermanent);
|
||||
|
||||
virtual void SetHasAudioAvailableEventListeners();
|
||||
|
||||
@ -1059,6 +1059,11 @@ protected:
|
||||
// mDialogAbuseCount gets reset.
|
||||
bool mStopAbuseDialogs;
|
||||
|
||||
// This flag gets set when dialogs should be permanently disabled for this
|
||||
// window (e.g. when we are closing the tab and therefore are guaranteed to be
|
||||
// destroying this window).
|
||||
bool mDialogsPermanentlyDisabled;
|
||||
|
||||
nsRefPtr<nsDOMMozURLProperty> mURLProperty;
|
||||
|
||||
nsTHashtable<nsPtrHashKey<nsDOMEventTargetHelper> > mEventTargetObjects;
|
||||
|
@ -39,7 +39,7 @@ interface nsIFile;
|
||||
interface nsIDOMTouch;
|
||||
interface nsIDOMClientRect;
|
||||
|
||||
[scriptable, uuid(97548ee0-9def-421a-ab2a-c6c98efa9a3c)]
|
||||
[scriptable, uuid(5fc61d7b-a303-4f34-adfe-b7828675ba45)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -1183,4 +1183,10 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* The caller of this method must have UniversalXPConnect privileges.
|
||||
*/
|
||||
void setScrollPositionClampingScrollPortSize(in float aWidth, in float aHeight);
|
||||
|
||||
/**
|
||||
* Prevent this window (and any child windows) from displaying any further
|
||||
* dialogs (e.g. window.alert()).
|
||||
*/
|
||||
void preventFurtherDialogs();
|
||||
};
|
||||
|
@ -4310,6 +4310,13 @@ DocumentViewerImpl::GetIsTabModalPromptAllowed(bool *aAllowed)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocumentViewerImpl::GetIsHidden(bool *aHidden)
|
||||
{
|
||||
*aHidden = mHidden;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DocumentViewerImpl::DestroyPresShell()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user