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),
mCallCleanUpAfterModalDialogCloses(false),
mDialogAbuseCount(0),
mDialogDisabled(false)
mStopAbuseDialogs(false)
{
nsLayoutStatics::AddRef();
@ -2551,67 +2551,64 @@ nsGlobalWindow::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
}
bool
nsGlobalWindow::DialogOpenAttempted()
nsGlobalWindow::DialogsAreBlocked(bool *aBeingAbused)
{
*aBeingAbused = false;
nsGlobalWindow *topWindow = GetScriptableTop();
if (!topWindow) {
NS_ERROR("DialogOpenAttempted() called without a top window?");
return false;
NS_ERROR("DialogsAreBlocked() called without a top window?");
return true;
}
topWindow = topWindow->GetCurrentInnerWindowInternal();
if (!topWindow ||
topWindow->mLastDialogQuitTime.IsNull() ||
if (!topWindow) {
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()) {
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() -
topWindow->mLastDialogQuitTime);
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);
return GetPopupControlState() > openAllowed ||
mDialogAbuseCount > MAX_SUCCESSIVE_DIALOG_COUNT;
}
topWindow->mDialogAbuseCount = 0;
// Reset the abuse counter
mDialogAbuseCount = 0;
return false;
}
bool
nsGlobalWindow::AreDialogsBlocked()
nsGlobalWindow::ConfirmDialogIfNeeded()
{
nsGlobalWindow *topWindow = GetScriptableTop();
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);
FORWARD_TO_OUTER(ConfirmDialogIfNeeded, (), false);
NS_ENSURE_TRUE(mDocShell, false);
nsCOMPtr<nsIPromptService> promptSvc =
do_GetService("@mozilla.org/embedcomp/prompt-service;1");
if (!DialogOpenAttempted() || !promptSvc) {
if (!promptSvc) {
return true;
}
@ -2641,14 +2638,13 @@ nsGlobalWindow::PreventFurtherDialogs()
nsGlobalWindow *topWindow = GetScriptableTop();
if (!topWindow) {
NS_ERROR("PreventFurtherDialogs() called without a top window?");
return;
}
topWindow = topWindow->GetCurrentInnerWindowInternal();
if (topWindow)
topWindow->mDialogDisabled = true;
if (topWindow) {
topWindow->mStopAbuseDialogs = true;
}
}
nsresult
@ -4792,12 +4788,10 @@ nsGlobalWindow::Alert(const nsAString& aString)
{
FORWARD_TO_OUTER(Alert, (aString), NS_ERROR_NOT_INITIALIZED);
if (AreDialogsBlocked())
bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
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
// about the dialog, to prevent the current state from being active
@ -4844,7 +4838,7 @@ nsGlobalWindow::Alert(const nsAString& aString)
nsAutoSyncOperation sync(GetCurrentInnerWindowInternal() ?
GetCurrentInnerWindowInternal()->mDoc :
nullptr);
if (shouldEnableDisableDialog) {
if (needToPromptForAbuse) {
bool disallowDialog = false;
nsXPIDLString label;
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);
if (AreDialogsBlocked())
bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
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
// 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() ?
GetCurrentInnerWindowInternal()->mDoc :
nullptr);
if (shouldEnableDisableDialog) {
if (needToPromptForAbuse) {
bool disallowDialog = false;
nsXPIDLString label;
nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
@ -4939,12 +4931,10 @@ nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
SetDOMStringToNull(aReturn);
if (AreDialogsBlocked())
bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
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
// 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;
nsXPIDLString label;
if (shouldEnableDisableDialog) {
if (needToPromptForAbuse) {
nsContentUtils::GetLocalizedString(nsContentUtils::eCOMMON_DIALOG_PROPERTIES,
"ScriptDialogLabel", label);
}
@ -5258,8 +5248,14 @@ nsGlobalWindow::Print()
if (Preferences::GetBool("dom.disable_window_print", false))
return NS_ERROR_NOT_AVAILABLE;
if (AreDialogsBlocked() || !ConfirmDialogAllowed())
bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (needToPromptForAbuse && !ConfirmDialogIfNeeded()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint;
if (NS_SUCCEEDED(GetInterface(NS_GET_IID(nsIWebBrowserPrint),
@ -7190,8 +7186,14 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs,
// pending reflows.
EnsureReflowFlushAndPaint();
if (AreDialogsBlocked() || !ConfirmDialogAllowed())
bool needToPromptForAbuse;
if (DialogsAreBlocked(&needToPromptForAbuse)) {
return NS_ERROR_NOT_AVAILABLE;
}
if (needToPromptForAbuse && !ConfirmDialogIfNeeded()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIDOMWindow> dlgWin;
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
// 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
// dialogs up to this limit, even if they were disabled.
#define MAX_DIALOG_COUNT 10
// Maximum number of successive dialogs before we prompt users to disable
// dialogs for this window.
#define MAX_SUCCESSIVE_DIALOG_COUNT 5
// Idle fuzz time upper limit
#define MAX_IDLE_FUZZ_TIME_MS 90000
@ -438,19 +438,19 @@ public:
return nullptr;
}
// Call this when a modal dialog is about to be opened. Returns
// true if we've reached the state in this top level window where we
// ask the user if further dialogs should be blocked.
bool DialogOpenAttempted();
// Returns true if dialogs need to be prevented from appearings for this
// window. beingAbused returns whether dialogs are being abused.
bool DialogsAreBlocked(bool *aBeingAbused);
// Returns true if dialogs have already been blocked for this
// window.
bool AreDialogsBlocked();
// Returns true if we've reached the state in this top level window where we
// ask the user if further dialogs should be blocked. This method must only
// be called on the scriptable top inner window.
bool DialogsAreBeingAbused();
// Ask the user if further dialogs should be blocked. This is used
// in the cases where we have no modifiable UI to show, in that case
// we show a separate dialog when asking this question.
bool ConfirmDialogAllowed();
// Ask the user if further dialogs should be blocked, if dialogs are currently
// being abused. This is used in the cases where we have no modifiable UI to
// show, in that case we show a separate dialog to ask this question.
bool ConfirmDialogIfNeeded();
// Prevent further dialogs in this (top level) window
void PreventFurtherDialogs();
@ -1042,17 +1042,22 @@ protected:
nsCOMPtr<nsIIDBFactory> mIndexedDB;
// In the case of a "trusted" dialog (@see PopupControlState), we
// set this counter to ensure a max of MAX_DIALOG_LIMIT
// This counts the number of windows that have been opened in rapid succession
// (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;
// This holds the time when the last modal dialog was shown, if two
// dialogs are shown within CONCURRENT_DIALOG_TIME_LIMIT the
// checkbox is shown. In the case of ShowModalDialog another Confirm
// dialog will be shown, the result of the checkbox/confirm dialog
// will be stored in mDialogDisabled variable.
// This holds the time when the last modal dialog was shown. If more than
// MAX_DIALOG_LIMIT dialogs are shown within the time span defined by
// dom.successive_dialog_time_limit, we show a checkbox or confirmation prompt
// to allow disabling of further dialogs from this window.
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;