mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1116385. r=Mossop
This commit is contained in:
parent
4b72a507bb
commit
ad9d2a6598
@ -10,6 +10,9 @@ const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "EnableDelayHelper",
|
||||
"resource://gre/modules/SharedPromptUtils.jsm");
|
||||
|
||||
|
||||
this.CommonDialog = function CommonDialog(args, ui) {
|
||||
@ -161,13 +164,11 @@ CommonDialog.prototype = {
|
||||
this.setDefaultFocus(true);
|
||||
|
||||
if (this.args.enableDelay) {
|
||||
this.setButtonsEnabledState(false);
|
||||
// Use a longer, pref-controlled delay when the dialog is first opened.
|
||||
let delayTime = Services.prefs.getIntPref("security.dialog_enable_delay");
|
||||
this.startOnFocusDelay(delayTime);
|
||||
let self = this;
|
||||
this.ui.focusTarget.addEventListener("blur", function(e) { self.onBlur(e); }, false);
|
||||
this.ui.focusTarget.addEventListener("focus", function(e) { self.onFocus(e); }, false);
|
||||
this.delayHelper = new EnableDelayHelper({
|
||||
disableDialog: () => this.setButtonsEnabledState(false),
|
||||
enableDialog: () => this.setButtonsEnabledState(true),
|
||||
focusTarget: this.ui.focusTarget
|
||||
});
|
||||
}
|
||||
|
||||
// Play a sound (unless we're tab-modal -- don't want those to feel like OS prompts).
|
||||
@ -230,44 +231,6 @@ CommonDialog.prototype = {
|
||||
this.ui.button3.disabled = !enabled;
|
||||
},
|
||||
|
||||
onBlur : function (aEvent) {
|
||||
if (aEvent.target != this.ui.focusTarget)
|
||||
return;
|
||||
this.setButtonsEnabledState(false);
|
||||
|
||||
// If we blur while waiting to enable the buttons, just cancel the
|
||||
// timer to ensure the delay doesn't fire while not focused.
|
||||
if (this.focusTimer) {
|
||||
this.focusTimer.cancel();
|
||||
this.focusTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
onFocus : function (aEvent) {
|
||||
if (aEvent.target != this.ui.focusTarget)
|
||||
return;
|
||||
this.startOnFocusDelay();
|
||||
},
|
||||
|
||||
startOnFocusDelay : function(delayTime) {
|
||||
// Shouldn't already have a timer, but just in case...
|
||||
if (this.focusTimer)
|
||||
return;
|
||||
// If no delay specified, use 250ms. (This is the normal case for when
|
||||
// after the dialog has been opened and focus shifts.)
|
||||
if (!delayTime)
|
||||
delayTime = 250;
|
||||
let self = this;
|
||||
this.focusTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.focusTimer.initWithCallback(function() { self.onFocusTimeout(); },
|
||||
delayTime, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
onFocusTimeout : function() {
|
||||
this.focusTimer = null;
|
||||
this.setButtonsEnabledState(true);
|
||||
},
|
||||
|
||||
setDefaultFocus : function(isInitialLoad) {
|
||||
let b = (this.args.defaultButtonNum || 0);
|
||||
let button = this.ui["button" + b];
|
||||
|
@ -1,10 +1,12 @@
|
||||
this.EXPORTED_SYMBOLS = [ "PromptUtils" ];
|
||||
this.EXPORTED_SYMBOLS = [ "PromptUtils", "EnableDelayHelper" ];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.PromptUtils = {
|
||||
// Fire a dialog open/close event. Used by tabbrowser to focus the
|
||||
// tab which is triggering a prompt.
|
||||
@ -40,3 +42,110 @@ this.PromptUtils = {
|
||||
obj[propName] = propBag.getProperty(propName);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This helper handles the enabling/disabling of dialogs that might
|
||||
* be subject to fast-clicking attacks. It handles the initial delayed
|
||||
* enabling of the dialog, as well as disabling it on blur and reapplying
|
||||
* the delay when the dialog regains focus.
|
||||
*
|
||||
* @param enableDialog A custom function to be called when the dialog
|
||||
* is to be enabled.
|
||||
* @param diableDialog A custom function to be called when the dialog
|
||||
* is to be disabled.
|
||||
* @param focusTarget The window used to watch focus/blur events.
|
||||
*/
|
||||
this.EnableDelayHelper = function({enableDialog, disableDialog, focusTarget}) {
|
||||
this.enableDialog = makeSafe(enableDialog);
|
||||
this.disableDialog = makeSafe(disableDialog);
|
||||
this.focusTarget = focusTarget;
|
||||
|
||||
this.disableDialog();;
|
||||
|
||||
this.focusTarget.addEventListener("blur", this, false);
|
||||
this.focusTarget.addEventListener("focus", this, false);
|
||||
this.focusTarget.document.addEventListener("unload", this, false);
|
||||
|
||||
this.startOnFocusDelay();
|
||||
};
|
||||
|
||||
this.EnableDelayHelper.prototype = {
|
||||
get delayTime() {
|
||||
return Services.prefs.getIntPref("security.dialog_enable_delay");
|
||||
},
|
||||
|
||||
handleEvent : function(event) {
|
||||
if (event.target != this.focusTarget &&
|
||||
event.target != this.focusTarget.document)
|
||||
return;
|
||||
|
||||
switch (event.type) {
|
||||
case "blur":
|
||||
this.onBlur();
|
||||
break;
|
||||
|
||||
case "focus":
|
||||
this.onFocus();
|
||||
break;
|
||||
|
||||
case "unload":
|
||||
this.onUnload();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onBlur : function () {
|
||||
this.disableDialog();
|
||||
// If we blur while waiting to enable the buttons, just cancel the
|
||||
// timer to ensure the delay doesn't fire while not focused.
|
||||
if (this._focusTimer) {
|
||||
this._focusTimer.cancel();
|
||||
this._focusTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
onFocus : function () {
|
||||
this.startOnFocusDelay();
|
||||
},
|
||||
|
||||
onUnload: function() {
|
||||
this.focusTarget.removeEventListener("blur", this, false);
|
||||
this.focusTarget.removeEventListener("focus", this, false);
|
||||
this.focusTarget.document.removeEventListener("unload", this, false);
|
||||
|
||||
if (this._focusTimer) {
|
||||
this._focusTimer.cancel();
|
||||
this._focusTimer = null;
|
||||
}
|
||||
|
||||
this.focusTarget = this.enableDialog = this.disableDialog = null;
|
||||
},
|
||||
|
||||
startOnFocusDelay : function() {
|
||||
if (this._focusTimer)
|
||||
return;
|
||||
|
||||
this._focusTimer = Cc["@mozilla.org/timer;1"]
|
||||
.createInstance(Ci.nsITimer);
|
||||
this._focusTimer.initWithCallback(
|
||||
() => { this.onFocusTimeout(); },
|
||||
this.delayTime,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT
|
||||
);
|
||||
},
|
||||
|
||||
onFocusTimeout : function() {
|
||||
this._focusTimer = null;
|
||||
this.enableDialog();
|
||||
},
|
||||
};
|
||||
|
||||
function makeSafe(fn) {
|
||||
return function () {
|
||||
// The dialog could be gone by now (if the user closed it),
|
||||
// which makes it likely that the given fn might throw.
|
||||
try {
|
||||
fn();
|
||||
} catch (e) { }
|
||||
};
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
<dialog id="unknownContentType"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="dialog.initDialog();" onunload="if (dialog) dialog.onCancel();"
|
||||
onblur="if (dialog) dialog.onBlur(event);" onfocus="dialog.onFocus(event);"
|
||||
#ifdef XP_WIN
|
||||
style="width: 36em;"
|
||||
#else
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
const {utils: Cu, interfaces: Ci, classes: Cc, results: Cr} = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "EnableDelayHelper",
|
||||
"resource://gre/modules/SharedPromptUtils.jsm");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//// Helper Functions
|
||||
@ -539,25 +542,21 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
|
||||
this.mDialog.setTimeout("dialog.postShowCallback()", 0);
|
||||
|
||||
let acceptDelay = Services.prefs.getIntPref("security.dialog_enable_delay");
|
||||
this.mDialog.document.documentElement.getButton("accept").disabled = true;
|
||||
this._showTimer = Components.classes["@mozilla.org/timer;1"]
|
||||
.createInstance(nsITimer);
|
||||
this._showTimer.initWithCallback(this, acceptDelay, nsITimer.TYPE_ONE_SHOT);
|
||||
this.delayHelper = new EnableDelayHelper({
|
||||
disableDialog: () => {
|
||||
this.mDialog.document.documentElement.getButton("accept").disabled = true;
|
||||
},
|
||||
enableDialog: () => {
|
||||
this.mDialog.document.documentElement.getButton("accept").disabled = false;
|
||||
},
|
||||
focusTarget: this.mDialog
|
||||
});
|
||||
},
|
||||
|
||||
notify: function (aTimer) {
|
||||
if (aTimer == this._showTimer) {
|
||||
if (!this.mDialog) {
|
||||
this.reallyShow();
|
||||
} else {
|
||||
// The user may have already canceled the dialog.
|
||||
try {
|
||||
if (!this._blurred) {
|
||||
this.mDialog.document.documentElement.getButton("accept").disabled = false;
|
||||
}
|
||||
} catch (ex) {}
|
||||
this._delayExpired = true;
|
||||
}
|
||||
// The timer won't release us, so we have to release it.
|
||||
this._showTimer = null;
|
||||
@ -641,21 +640,6 @@ nsUnknownContentTypeDialog.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_blurred: false,
|
||||
_delayExpired: false,
|
||||
onBlur: function(aEvent) {
|
||||
this._blurred = true;
|
||||
this.mDialog.document.documentElement.getButton("accept").disabled = true;
|
||||
},
|
||||
|
||||
onFocus: function(aEvent) {
|
||||
this._blurred = false;
|
||||
if (this._delayExpired) {
|
||||
var script = "document.documentElement.getButton('accept').disabled = false";
|
||||
this.mDialog.setTimeout(script, 250);
|
||||
}
|
||||
},
|
||||
|
||||
// Returns true if opening the default application makes sense.
|
||||
openWithDefaultOK: function() {
|
||||
// The checking is different on Windows...
|
||||
|
@ -36,4 +36,5 @@ skip-if = os != 'win' && toolkit != 'cocoa'
|
||||
# disabled for very frequent orange--bug 630567
|
||||
skip-if = os != 'win' || true
|
||||
[test_ui_stays_open_on_alert_clickback.xul]
|
||||
[test_unknownContentType_delayedbutton.xul]
|
||||
[test_unknownContentType_dialog_layout.xul]
|
||||
|
@ -0,0 +1,117 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<!--
|
||||
* The unknownContentType popup can have two different layouts depending on
|
||||
* whether a helper application can be selected or not.
|
||||
* This tests that both layouts have correct collapsed elements.
|
||||
-->
|
||||
|
||||
<window title="Unknown Content Type Dialog Test"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="doTest()">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript"
|
||||
src="utils.js"/>
|
||||
|
||||
<script type="application/javascript"><![CDATA[
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
Components.utils.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
const UCT_URI = "chrome://mozapps/content/downloads/unknownContentType.xul";
|
||||
const LOAD_URI = "http://mochi.test:8888/chrome/toolkit/mozapps/downloads/tests/chrome/unknownContentType_dialog_layout_data.txt";
|
||||
|
||||
const DIALOG_DELAY = Services.prefs.getIntPref("security.dialog_enable_delay") + 200;
|
||||
|
||||
let UCTObserver = {
|
||||
opened: Promise.defer(),
|
||||
closed: Promise.defer(),
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
|
||||
|
||||
switch (aTopic) {
|
||||
case "domwindowopened":
|
||||
win.addEventListener("load", function onLoad(event) {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
|
||||
// Let the dialog initialize
|
||||
SimpleTest.executeSoon(function() {
|
||||
UCTObserver.opened.resolve(win);
|
||||
});
|
||||
}, false);
|
||||
break;
|
||||
|
||||
case "domwindowclosed":
|
||||
if (win.location == UCT_URI) {
|
||||
this.closed.resolve();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Services.ww.registerNotification(UCTObserver);
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
SimpleTest.requestFlakyTimeout("This test is testing a timing-based feature, so it really needs to wait a certain amount of time to verify that the feature worked.");
|
||||
|
||||
function waitDelay(delay) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.setTimeout(resolve, delay);
|
||||
});
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
Task.spawn(function test_aboutCrashed() {
|
||||
let frame = document.getElementById("testframe");
|
||||
frame.setAttribute("src", LOAD_URI);
|
||||
|
||||
let uctWindow = yield UCTObserver.opened.promise;
|
||||
let ok = uctWindow.document.documentElement.getButton("accept");
|
||||
|
||||
SimpleTest.is(ok.disabled, true, "button started disabled");
|
||||
|
||||
yield waitDelay(DIALOG_DELAY);
|
||||
|
||||
SimpleTest.is(ok.disabled, false, "button was enabled");
|
||||
|
||||
focusOutOfDialog = SimpleTest.promiseFocus(window);
|
||||
window.focus();
|
||||
yield focusOutOfDialog;
|
||||
|
||||
SimpleTest.is(ok.disabled, true, "button was disabled");
|
||||
|
||||
focusOnDialog = SimpleTest.promiseFocus(uctWindow);
|
||||
uctWindow.focus();
|
||||
yield focusOnDialog;
|
||||
|
||||
SimpleTest.is(ok.disabled, true, "button remained disabled");
|
||||
|
||||
yield waitDelay(DIALOG_DELAY);
|
||||
SimpleTest.is(ok.disabled, false, "button re-enabled after delay");
|
||||
|
||||
uctWindow.document.documentElement.cancelDialog();
|
||||
yield UCTObserver.closed.promise;
|
||||
|
||||
Services.ww.unregisterNotification(UCTObserver);
|
||||
uctWindow = null;
|
||||
UCTObserver = null;
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
]]></script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display:none;"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
<iframe xmlns="http://www.w3.org/1999/xhtml"
|
||||
id="testframe">
|
||||
</iframe>
|
||||
</window>
|
Loading…
Reference in New Issue
Block a user