mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1107706: Part 11: Global modal dialogue support
This commit is contained in:
parent
18f01362a5
commit
e3da054553
@ -26,6 +26,7 @@ Cu.import("chrome://marionette/content/emulator.js");
|
||||
Cu.import("chrome://marionette/content/error.js");
|
||||
Cu.import("chrome://marionette/content/marionette-elements.js");
|
||||
Cu.import("chrome://marionette/content/marionette-simpletest.js");
|
||||
Cu.import("chrome://marionette/content/modal.js");
|
||||
|
||||
loader.loadSubScript("chrome://marionette/content/marionette-common.js");
|
||||
|
||||
@ -45,8 +46,6 @@ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
|
||||
const CLICK_TO_START_PREF = "marionette.debugging.clicktostart";
|
||||
const CONTENT_LISTENER_PREF = "marionette.contentListener";
|
||||
const COMMON_DIALOG_LOADED = "common-dialog-loaded";
|
||||
const TABMODAL_DIALOG_LOADED = "tabmodal-dialog-loaded";
|
||||
|
||||
const logger = Log.repository.getLogger("Marionette");
|
||||
const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
||||
@ -105,7 +104,6 @@ this.Context.fromString = function(s) {
|
||||
let ListenerProxy = function(mmFn, sendAsyncFn, curBrowserFn) {
|
||||
this.curCmdId = null;
|
||||
this.sendAsync = sendAsyncFn;
|
||||
this.ondialog = d => {};
|
||||
|
||||
this.mmFn_ = mmFn;
|
||||
this.curBrowserFn_ = curBrowserFn;
|
||||
@ -123,82 +121,52 @@ ListenerProxy.prototype.__noSuchMethod__ = function*(name, args) {
|
||||
const ok = "Marionette:ok";
|
||||
const val = "Marionette:done";
|
||||
const err = "Marionette:error";
|
||||
const all = [ok, val, err];
|
||||
|
||||
let proxy = new Promise((resolve, reject) => {
|
||||
let listeners = [];
|
||||
let obs = new Map();
|
||||
obs.add = function(modalHandler) {
|
||||
if (Services.appinfo.name != "Firefox")
|
||||
return;
|
||||
this.set(COMMON_DIALOG_LOADED, modalHandler);
|
||||
this.set(TABMODAL_DIALOG_LOADED, modalHandler);
|
||||
for (let [t,o] of this) {
|
||||
Services.obs.addObserver(o, t, false);
|
||||
}
|
||||
};
|
||||
obs.remove = function() {
|
||||
for (let [t,o] of this) {
|
||||
Services.obs.removeObserver(o, t);
|
||||
}
|
||||
};
|
||||
|
||||
let okListener = () => resolve();
|
||||
let valListener = msg => resolve(msg.json.value);
|
||||
let errListener = msg => reject(
|
||||
"error" in msg.objects ? msg.objects.error : msg.json);
|
||||
|
||||
let handleDialogLoad = function(subject, topic) {
|
||||
obs.remove();
|
||||
this.cancelRequest();
|
||||
|
||||
// we shouldn't return to the client due to the modal associated with the
|
||||
// jsdebugger
|
||||
let clickToStart;
|
||||
try {
|
||||
clickToStart = Services.prefs.getBoolPref(CLICK_TO_START_PREF);
|
||||
} catch (e) {}
|
||||
if (clickToStart) {
|
||||
Services.prefs.setBoolPref(CLICK_TO_START_PREF, false);
|
||||
return;
|
||||
}
|
||||
|
||||
let winr;
|
||||
if (topic == COMMON_DIALOG_LOADED)
|
||||
winr = Cu.getWeakReference(subject);
|
||||
let d = new ModalDialog(() => this.curBrowser, winr);
|
||||
this.ondialog(d);
|
||||
|
||||
// shortcut to return a response immediately,
|
||||
// causes next reply from listener to be out-of-sync
|
||||
resolve();
|
||||
};
|
||||
|
||||
let removeListeners = (name, listenerFn) => {
|
||||
let fn = msg => {
|
||||
let removeListeners = (name, fn) => {
|
||||
let rmFn = msg => {
|
||||
if (this.isOutOfSync(msg.json.command_id)) {
|
||||
logger.warn("Skipping out-of-sync response from listener: " +
|
||||
msg.name + msg.json.toSource());
|
||||
return;
|
||||
}
|
||||
|
||||
listeners.map(l => this.mm.removeMessageListener(l[0], l[1]));
|
||||
obs.remove();
|
||||
listeners.remove();
|
||||
modal.removeHandler(handleDialog);
|
||||
|
||||
listenerFn(msg);
|
||||
fn(msg);
|
||||
this.curCmdId = null;
|
||||
};
|
||||
|
||||
listeners.push([name, fn]);
|
||||
return fn;
|
||||
listeners.push([name, rmFn]);
|
||||
return rmFn;
|
||||
};
|
||||
|
||||
this.mm.addMessageListener(ok, removeListeners(ok, okListener));
|
||||
this.mm.addMessageListener(val, removeListeners(val, valListener));
|
||||
this.mm.addMessageListener(err, removeListeners(err, errListener));
|
||||
let listeners = [];
|
||||
listeners.add = () => {
|
||||
this.mm.addMessageListener(ok, removeListeners(ok, okListener));
|
||||
this.mm.addMessageListener(val, removeListeners(val, valListener));
|
||||
this.mm.addMessageListener(err, removeListeners(err, errListener));
|
||||
};
|
||||
listeners.remove = () =>
|
||||
listeners.map(l => this.mm.removeMessageListener(l[0], l[1]));
|
||||
|
||||
// install observers for global- and tab modal dialogues
|
||||
obs.add(handleDialogLoad.bind(this));
|
||||
let okListener = () => resolve();
|
||||
let valListener = msg => resolve(msg.json.value);
|
||||
let errListener = msg => reject(
|
||||
"error" in msg.objects ? msg.objects.error : msg.json);
|
||||
|
||||
let handleDialog = function(subject, topic) {
|
||||
listeners.remove();
|
||||
modal.removeHandler(handleDialog);
|
||||
this.sendAsync("cancelRequest");
|
||||
resolve();
|
||||
}.bind(this);
|
||||
|
||||
// start content process listeners, and install observers for global-
|
||||
// and tab modal dialogues
|
||||
listeners.add();
|
||||
modal.addHandler(handleDialog);
|
||||
|
||||
// convert to array if passed arguments
|
||||
let msg;
|
||||
@ -269,7 +237,6 @@ this.GeckoDriver = function(appName, device, emulator) {
|
||||
this.oopFrameId = null;
|
||||
this.observing = null;
|
||||
this._browserIds = new WeakMap();
|
||||
this.dialog = null;
|
||||
|
||||
this.sessionCapabilities = {
|
||||
// Mandated capabilities
|
||||
@ -302,7 +269,15 @@ this.GeckoDriver = function(appName, device, emulator) {
|
||||
() => this.mm,
|
||||
this.sendAsync.bind(this),
|
||||
() => this.curBrowser);
|
||||
this.listener.ondialog = d => this.dialog = d;
|
||||
|
||||
this.dialog = null;
|
||||
let handleDialog = (subject, topic) => {
|
||||
let winr;
|
||||
if (topic == modal.COMMON_DIALOG_LOADED)
|
||||
winr = Cu.getWeakReference(subject);
|
||||
this.dialog = new modal.Dialog(() => this.curBrowser, winr);
|
||||
};
|
||||
modal.addHandler(handleDialog);
|
||||
};
|
||||
|
||||
GeckoDriver.prototype.QueryInterface = XPCOMUtils.generateQI([
|
||||
@ -3036,46 +3011,6 @@ GeckoDriver.prototype.commands = {
|
||||
"sendKeysToDialog": GeckoDriver.prototype.sendKeysToDialog
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the current modal dialogue.
|
||||
*
|
||||
* @param {function(): BrowserObj} curBrowserFn
|
||||
* Function that returns the current BrowserObj.
|
||||
* @param {?nsIWeakReference} winRef
|
||||
* A weak reference to the current ChromeWindow.
|
||||
*/
|
||||
this.ModalDialog = function(curBrowserFn, winRef=null) {
|
||||
Object.defineProperty(this, "curBrowser", {
|
||||
get() { return curBrowserFn(); }
|
||||
});
|
||||
this.win_ = winRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the ChromeWindow associated with an open dialog window if it is
|
||||
* currently attached to the dom.
|
||||
*
|
||||
*/
|
||||
Object.defineProperty(ModalDialog.prototype, "window", {
|
||||
get() {
|
||||
if (this.win_ !== null) {
|
||||
let win = this.win_.get();
|
||||
if (win && win.parent)
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(ModalDialog.prototype, "ui", {
|
||||
get() {
|
||||
let win = this.window;
|
||||
if (win)
|
||||
return win.Dialog.ui;
|
||||
return this.curBrowser.getTabModalUI();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a BrowserObj. BrowserObjs handle interactions with the
|
||||
* browser, according to the current environment (desktop, b2g, etc.).
|
||||
|
@ -19,6 +19,7 @@ marionette.jar:
|
||||
content/command.js (command.js)
|
||||
content/dispatcher.js (dispatcher.js)
|
||||
content/emulator.js (emulator.js)
|
||||
content/modal.js (modal.js)
|
||||
#ifdef ENABLE_TESTS
|
||||
content/test.xul (client/marionette/chrome/test.xul)
|
||||
content/test2.xul (client/marionette/chrome/test2.xul)
|
||||
|
113
testing/marionette/modal.js
Normal file
113
testing/marionette/modal.js
Normal file
@ -0,0 +1,113 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["modal"];
|
||||
|
||||
let isFirefox = () => Services.appinfo.name == "Firefox";
|
||||
|
||||
this.modal = {};
|
||||
modal = {
|
||||
COMMON_DIALOG_LOADED: "common-dialog-loaded",
|
||||
TABMODAL_DIALOG_LOADED: "tabmodal-dialog-loaded",
|
||||
handlers: {
|
||||
"common-dialog-loaded": new Set(),
|
||||
"tabmodal-dialog-loaded": new Set()
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add handler that will be called when a global- or tab modal dialogue
|
||||
* appears.
|
||||
*
|
||||
* This is achieved by installing observers for common-
|
||||
* and tab modal loaded events.
|
||||
*
|
||||
* This function is a no-op if called on any other product than Firefox.
|
||||
*
|
||||
* @param {function(Object, string)} handler
|
||||
* The handler to be called, which is passed the
|
||||
* subject (e.g. ChromeWindow) and the topic (one of
|
||||
* {@code modal.COMMON_DIALOG_LOADED} or
|
||||
* {@code modal.TABMODAL_DIALOG_LOADED}.
|
||||
*/
|
||||
modal.addHandler = function(handler) {
|
||||
if (!isFirefox()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(this.handlers).map(topic => {
|
||||
this.handlers[topic].add(handler);
|
||||
Services.obs.addObserver(handler, topic, false);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove modal dialogue handler by function reference.
|
||||
*
|
||||
* This function is a no-op if called on any other product than Firefox.
|
||||
*
|
||||
* @param {function} toRemove
|
||||
* The handler previously passed to modal.addHandler which will now
|
||||
* be removed.
|
||||
*/
|
||||
modal.removeHandler = function(toRemove) {
|
||||
if (!isFirefox()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let topic of Object.keys(this.handlers)) {
|
||||
let handlers = this.handlers[topic];
|
||||
for (let handler of handlers) {
|
||||
if (handler == toRemove) {
|
||||
Services.obs.removeObserver(handler, topic);
|
||||
handlers.delete(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the current modal dialogue.
|
||||
*
|
||||
* @param {function(): BrowserObj} curBrowserFn
|
||||
* Function that returns the current BrowserObj.
|
||||
* @param {?nsIWeakReference} winRef
|
||||
* A weak reference to the current ChromeWindow.
|
||||
*/
|
||||
modal.Dialog = function(curBrowserFn, winRef=null) {
|
||||
Object.defineProperty(this, "curBrowser", {
|
||||
get() { return curBrowserFn(); }
|
||||
});
|
||||
this.win_ = winRef;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the ChromeWindow associated with an open dialog window if it
|
||||
* is currently attached to the DOM.
|
||||
*/
|
||||
Object.defineProperty(modal.Dialog.prototype, "window", {
|
||||
get() {
|
||||
if (this.win_ !== null) {
|
||||
let win = this.win_.get();
|
||||
if (win && win.parent)
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(modal.Dialog.prototype, "ui", {
|
||||
get() {
|
||||
let win = this.window;
|
||||
if (win)
|
||||
return win.Dialog.ui;
|
||||
return this.curBrowser.getTabModalUI();
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user