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;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
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) {
|
this.CommonDialog = function CommonDialog(args, ui) {
|
||||||
@ -161,13 +164,11 @@ CommonDialog.prototype = {
|
|||||||
this.setDefaultFocus(true);
|
this.setDefaultFocus(true);
|
||||||
|
|
||||||
if (this.args.enableDelay) {
|
if (this.args.enableDelay) {
|
||||||
this.setButtonsEnabledState(false);
|
this.delayHelper = new EnableDelayHelper({
|
||||||
// Use a longer, pref-controlled delay when the dialog is first opened.
|
disableDialog: () => this.setButtonsEnabledState(false),
|
||||||
let delayTime = Services.prefs.getIntPref("security.dialog_enable_delay");
|
enableDialog: () => this.setButtonsEnabledState(true),
|
||||||
this.startOnFocusDelay(delayTime);
|
focusTarget: this.ui.focusTarget
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play a sound (unless we're tab-modal -- don't want those to feel like OS prompts).
|
// 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;
|
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) {
|
setDefaultFocus : function(isInitialLoad) {
|
||||||
let b = (this.args.defaultButtonNum || 0);
|
let b = (this.args.defaultButtonNum || 0);
|
||||||
let button = this.ui["button" + b];
|
let button = this.ui["button" + b];
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
this.EXPORTED_SYMBOLS = [ "PromptUtils" ];
|
this.EXPORTED_SYMBOLS = [ "PromptUtils", "EnableDelayHelper" ];
|
||||||
|
|
||||||
const Cc = Components.classes;
|
const Cc = Components.classes;
|
||||||
const Ci = Components.interfaces;
|
const Ci = Components.interfaces;
|
||||||
const Cr = Components.results;
|
const Cr = Components.results;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
this.PromptUtils = {
|
this.PromptUtils = {
|
||||||
// Fire a dialog open/close event. Used by tabbrowser to focus the
|
// Fire a dialog open/close event. Used by tabbrowser to focus the
|
||||||
// tab which is triggering a prompt.
|
// tab which is triggering a prompt.
|
||||||
@ -40,3 +42,110 @@ this.PromptUtils = {
|
|||||||
obj[propName] = propBag.getProperty(propName);
|
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"
|
<dialog id="unknownContentType"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
onload="dialog.initDialog();" onunload="if (dialog) dialog.onCancel();"
|
onload="dialog.initDialog();" onunload="if (dialog) dialog.onCancel();"
|
||||||
onblur="if (dialog) dialog.onBlur(event);" onfocus="dialog.onFocus(event);"
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
style="width: 36em;"
|
style="width: 36em;"
|
||||||
#else
|
#else
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
const {utils: Cu, interfaces: Ci, classes: Cc, results: Cr} = Components;
|
const {utils: Cu, interfaces: Ci, classes: Cc, results: Cr} = Components;
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
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
|
//// Helper Functions
|
||||||
@ -539,25 +542,21 @@ nsUnknownContentTypeDialog.prototype = {
|
|||||||
|
|
||||||
this.mDialog.setTimeout("dialog.postShowCallback()", 0);
|
this.mDialog.setTimeout("dialog.postShowCallback()", 0);
|
||||||
|
|
||||||
let acceptDelay = Services.prefs.getIntPref("security.dialog_enable_delay");
|
this.delayHelper = new EnableDelayHelper({
|
||||||
this.mDialog.document.documentElement.getButton("accept").disabled = true;
|
disableDialog: () => {
|
||||||
this._showTimer = Components.classes["@mozilla.org/timer;1"]
|
this.mDialog.document.documentElement.getButton("accept").disabled = true;
|
||||||
.createInstance(nsITimer);
|
},
|
||||||
this._showTimer.initWithCallback(this, acceptDelay, nsITimer.TYPE_ONE_SHOT);
|
enableDialog: () => {
|
||||||
|
this.mDialog.document.documentElement.getButton("accept").disabled = false;
|
||||||
|
},
|
||||||
|
focusTarget: this.mDialog
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function (aTimer) {
|
notify: function (aTimer) {
|
||||||
if (aTimer == this._showTimer) {
|
if (aTimer == this._showTimer) {
|
||||||
if (!this.mDialog) {
|
if (!this.mDialog) {
|
||||||
this.reallyShow();
|
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.
|
// The timer won't release us, so we have to release it.
|
||||||
this._showTimer = null;
|
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.
|
// Returns true if opening the default application makes sense.
|
||||||
openWithDefaultOK: function() {
|
openWithDefaultOK: function() {
|
||||||
// The checking is different on Windows...
|
// The checking is different on Windows...
|
||||||
|
@ -36,4 +36,5 @@ skip-if = os != 'win' && toolkit != 'cocoa'
|
|||||||
# disabled for very frequent orange--bug 630567
|
# disabled for very frequent orange--bug 630567
|
||||||
skip-if = os != 'win' || true
|
skip-if = os != 'win' || true
|
||||||
[test_ui_stays_open_on_alert_clickback.xul]
|
[test_ui_stays_open_on_alert_clickback.xul]
|
||||||
|
[test_unknownContentType_delayedbutton.xul]
|
||||||
[test_unknownContentType_dialog_layout.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