Bug 391834, rearrange some popup dialog code to be simpler,r=smaug,patch mostly by gavin

This commit is contained in:
Neil Deakin 2012-08-13 15:03:59 -04:00
parent 668a8e43d7
commit 783b418a43
2 changed files with 93 additions and 86 deletions

View File

@ -684,7 +684,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mCleanedUp(false), mCleanedUp(false),
mCallCleanUpAfterModalDialogCloses(false), mCallCleanUpAfterModalDialogCloses(false),
mDialogAbuseCount(0), mDialogAbuseCount(0),
mDialogDisabled(false) mStopAbuseDialogs(false)
{ {
nsLayoutStatics::AddRef(); nsLayoutStatics::AddRef();
@ -2551,67 +2551,64 @@ nsGlobalWindow::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
} }
bool bool
nsGlobalWindow::DialogOpenAttempted() nsGlobalWindow::DialogsAreBlocked(bool *aBeingAbused)
{ {
*aBeingAbused = false;
nsGlobalWindow *topWindow = GetScriptableTop(); nsGlobalWindow *topWindow = GetScriptableTop();
if (!topWindow) { if (!topWindow) {
NS_ERROR("DialogOpenAttempted() called without a top window?"); NS_ERROR("DialogsAreBlocked() called without a top window?");
return true;
return false;
} }
topWindow = topWindow->GetCurrentInnerWindowInternal(); topWindow = topWindow->GetCurrentInnerWindowInternal();
if (!topWindow || if (!topWindow) {
topWindow->mLastDialogQuitTime.IsNull() || return true;
}
*aBeingAbused = topWindow->DialogsAreBeingAbused();
return topWindow->mStopAbuseDialogs && *aBeingAbused;
}
bool
nsGlobalWindow::DialogsAreBeingAbused()
{
NS_ASSERTION(GetScriptableTop() &&
GetScriptableTop()->GetCurrentInnerWindowInternal() == this,
"DialogsAreBeingAbused called with invalid window");
if (mLastDialogQuitTime.IsNull() ||
nsContentUtils::CallerHasUniversalXPConnect()) { nsContentUtils::CallerHasUniversalXPConnect()) {
return false; return false;
} }
TimeDuration dialogInterval(TimeStamp::Now() - mLastDialogQuitTime);
if (dialogInterval.ToSeconds() <
Preferences::GetInt("dom.successive_dialog_time_limit",
DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT)) {
mDialogAbuseCount++;
TimeDuration dialogDuration(TimeStamp::Now() - return GetPopupControlState() > openAllowed ||
topWindow->mLastDialogQuitTime); mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
if (dialogDuration.ToSeconds() <
Preferences::GetInt("dom.successive_dialog_time_limit",
SUCCESSIVE_DIALOG_TIME_LIMIT)) {
topWindow->mDialogAbuseCount++;
return (topWindow->GetPopupControlState() > openAllowed ||
topWindow->mDialogAbuseCount > MAX_DIALOG_COUNT);
} }
topWindow->mDialogAbuseCount = 0; // Reset the abuse counter
mDialogAbuseCount = 0;
return false; return false;
} }
bool bool
nsGlobalWindow::AreDialogsBlocked() nsGlobalWindow::ConfirmDialogIfNeeded()
{ {
nsGlobalWindow *topWindow = GetScriptableTop(); FORWARD_TO_OUTER(ConfirmDialogIfNeeded, (), false);
if (!topWindow) {
NS_ASSERTION(!mDocShell, "AreDialogsBlocked() called without a top window?");
return true;
}
topWindow = topWindow->GetCurrentInnerWindowInternal();
return !topWindow ||
(topWindow->mDialogDisabled &&
(topWindow->GetPopupControlState() > openAllowed ||
topWindow->mDialogAbuseCount >= MAX_DIALOG_COUNT));
}
bool
nsGlobalWindow::ConfirmDialogAllowed()
{
FORWARD_TO_OUTER(ConfirmDialogAllowed, (), false);
NS_ENSURE_TRUE(mDocShell, false); NS_ENSURE_TRUE(mDocShell, false);
nsCOMPtr<nsIPromptService> promptSvc = nsCOMPtr<nsIPromptService> promptSvc =
do_GetService("@mozilla.org/embedcomp/prompt-service;1"); do_GetService("@mozilla.org/embedcomp/prompt-service;1");
if (!DialogOpenAttempted() || !promptSvc) { if (!promptSvc) {
return true; return true;
} }
@ -2641,14 +2638,13 @@ nsGlobalWindow::PreventFurtherDialogs()
nsGlobalWindow *topWindow = GetScriptableTop(); nsGlobalWindow *topWindow = GetScriptableTop();
if (!topWindow) { if (!topWindow) {
NS_ERROR("PreventFurtherDialogs() called without a top window?"); NS_ERROR("PreventFurtherDialogs() called without a top window?");
return; return;
} }
topWindow = topWindow->GetCurrentInnerWindowInternal(); topWindow = topWindow->GetCurrentInnerWindowInternal();
if (topWindow) {
if (topWindow) topWindow->mStopAbuseDialogs = true;
topWindow->mDialogDisabled = true; }
} }
nsresult nsresult
@ -4792,12 +4788,10 @@ nsGlobalWindow::Alert(const nsAString& aString)
{ {
FORWARD_TO_OUTER(Alert, (aString), NS_ERROR_NOT_INITIALIZED); FORWARD_TO_OUTER(Alert, (aString), NS_ERROR_NOT_INITIALIZED);
if (AreDialogsBlocked()) bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
// We have to capture this now so as not to get confused with the
// popup state we push next
bool shouldEnableDisableDialog = DialogOpenAttempted();
// Reset popup state while opening a modal dialog, and firing events // Reset popup state while opening a modal dialog, and firing events
// about the dialog, to prevent the current state from being active // about the dialog, to prevent the current state from being active
@ -4844,7 +4838,7 @@ nsGlobalWindow::Alert(const nsAString& aString)
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
GetCurrentInnerWindowInternal()->mDoc : GetCurrentInnerWindowInternal()->mDoc :
nullptr); nullptr);
if (shouldEnableDisableDialog) { if (needToPromptForAbuse) {
bool disallowDialog = false; bool disallowDialog = false;
nsXPIDLString label; nsXPIDLString label;
nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
@ -4866,12 +4860,10 @@ nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
{ {
FORWARD_TO_OUTER(Confirm, (aString, aReturn), NS_ERROR_NOT_INITIALIZED); FORWARD_TO_OUTER(Confirm, (aString, aReturn), NS_ERROR_NOT_INITIALIZED);
if (AreDialogsBlocked()) bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
// We have to capture this now so as not to get confused with the popup state
// we push next
bool shouldEnableDisableDialog = DialogOpenAttempted();
// Reset popup state while opening a modal dialog, and firing events // Reset popup state while opening a modal dialog, and firing events
// about the dialog, to prevent the current state from being active // about the dialog, to prevent the current state from being active
@ -4913,7 +4905,7 @@ nsGlobalWindow::Confirm(const nsAString& aString, bool* aReturn)
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ? nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
GetCurrentInnerWindowInternal()->mDoc : GetCurrentInnerWindowInternal()->mDoc :
nullptr); nullptr);
if (shouldEnableDisableDialog) { if (needToPromptForAbuse) {
bool disallowDialog = false; bool disallowDialog = false;
nsXPIDLString label; nsXPIDLString label;
nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
@ -4939,12 +4931,10 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
SetDOMStringToNull(aReturn); SetDOMStringToNull(aReturn);
if (AreDialogsBlocked()) bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
// We have to capture this now so as not to get confused with the popup state
// we push next
bool shouldEnableDisableDialog = DialogOpenAttempted();
// Reset popup state while opening a modal dialog, and firing events // Reset popup state while opening a modal dialog, and firing events
// about the dialog, to prevent the current state from being active // about the dialog, to prevent the current state from being active
@ -4987,7 +4977,7 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
bool disallowDialog = false; bool disallowDialog = false;
nsXPIDLString label; nsXPIDLString label;
if (shouldEnableDisableDialog) { if (needToPromptForAbuse) {
nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES, nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
"ScriptDialogLabel", label); "ScriptDialogLabel", label);
} }
@ -5258,8 +5248,14 @@ nsGlobalWindow::Print()
if (Preferences::GetBool("dom.disable_window_print", false)) if (Preferences::GetBool("dom.disable_window_print", false))
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
if (AreDialogsBlocked() || !ConfirmDialogAllowed()) bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
if (needToPromptForAbuse && !ConfirmDialogIfNeeded()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint; nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint), if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
@ -7190,8 +7186,14 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
// pending reflows. // pending reflows.
EnsureReflowFlushAndPaint(); EnsureReflowFlushAndPaint();
if (AreDialogsBlocked() || !ConfirmDialogAllowed()) bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
}
if (needToPromptForAbuse && !ConfirmDialogIfNeeded()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIDOMWindow> dlgWin; nsCOMPtr<nsIDOMWindow> dlgWin;
nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1")); nsAutoString options(NS_LITERAL_STRING("-moz-internal-modal=1,status=1"));

View File

@ -74,11 +74,11 @@
// Amount of time allowed between alert/prompt/confirm before enabling // Amount of time allowed between alert/prompt/confirm before enabling
// the stop dialog checkbox. // the stop dialog checkbox.
#define SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
// During click or mousedown events (and others, see nsDOMEvent) we allow modal // Maximum number of successive dialogs before we prompt users to disable
// dialogs up to this limit, even if they were disabled. // dialogs for this window.
#define MAX_DIALOG_COUNT 10 #define MAX_SUCCESSIVE_DIALOG_COUNT 5
// Idle fuzz time upper limit // Idle fuzz time upper limit
#define MAX_IDLE_FUZZ_TIME_MS 90000 #define MAX_IDLE_FUZZ_TIME_MS 90000
@ -438,19 +438,19 @@ public:
return nullptr; return nullptr;
} }
// Call this when a modal dialog is about to be opened. Returns // Returns true if dialogs need to be prevented from appearings for this
// true if we've reached the state in this top level window where we // window. beingAbused returns whether dialogs are being abused.
// ask the user if further dialogs should be blocked. bool DialogsAreBlocked(bool *aBeingAbused);
bool DialogOpenAttempted();
// Returns true if dialogs have already been blocked for this // Returns true if we've reached the state in this top level window where we
// window. // ask the user if further dialogs should be blocked. This method must only
bool AreDialogsBlocked(); // be called on the scriptable top inner window.
bool DialogsAreBeingAbused();
// Ask the user if further dialogs should be blocked. This is used // Ask the user if further dialogs should be blocked, if dialogs are currently
// in the cases where we have no modifiable UI to show, in that case // being abused. This is used in the cases where we have no modifiable UI to
// we show a separate dialog when asking this question. // show, in that case we show a separate dialog to ask this question.
bool ConfirmDialogAllowed(); bool ConfirmDialogIfNeeded();
// Prevent further dialogs in this (top level) window // Prevent further dialogs in this (top level) window
void PreventFurtherDialogs(); void PreventFurtherDialogs();
@ -1042,17 +1042,22 @@ protected:
nsCOMPtr<nsIIDBFactory> mIndexedDB; nsCOMPtr<nsIIDBFactory> mIndexedDB;
// In the case of a "trusted" dialog (@see PopupControlState), we // This counts the number of windows that have been opened in rapid succession
// set this counter to ensure a max of MAX_DIALOG_LIMIT // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
// to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
// have elapsed without any other dialogs.
PRUint32 mDialogAbuseCount; PRUint32 mDialogAbuseCount;
// This holds the time when the last modal dialog was shown, if two // This holds the time when the last modal dialog was shown. If more than
// dialogs are shown within CONCURRENT_DIALOG_TIME_LIMIT the // MAX_DIALOG_LIMIT dialogs are shown within the time span defined by
// checkbox is shown. In the case of ShowModalDialog another Confirm // dom.successive_dialog_time_limit, we show a checkbox or confirmation prompt
// dialog will be shown, the result of the checkbox/confirm dialog // to allow disabling of further dialogs from this window.
// will be stored in mDialogDisabled variable.
TimeStamp mLastDialogQuitTime; TimeStamp mLastDialogQuitTime;
bool mDialogDisabled;
// This is set to true once the user has opted-in to preventing further
// dialogs for this window. Subsequent dialogs may still open if
// mDialogAbuseCount gets reset.
bool mStopAbuseDialogs;
nsRefPtr<nsDOMMozURLProperty> mURLProperty; nsRefPtr<nsDOMMozURLProperty> mURLProperty;