gecko-dev/testing/marionette/dom.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

215 строки
5.6 KiB
JavaScript
Исходник Обычный вид История

/* 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";
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 21:18:31 +03:00
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
Bug 1514594: Part 3 - Change ChromeUtils.import API. *** Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8 This changes the behavior of ChromeUtils.import() to return an exports object, rather than a module global, in all cases except when `null` is passed as a second argument, and changes the default behavior not to pollute the global scope with the module's exports. Thus, the following code written for the old model: ChromeUtils.import("resource://gre/modules/Services.jsm"); is approximately the same as the following, in the new model: var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); Since the two behaviors are mutually incompatible, this patch will land with a scripted rewrite to update all existing callers to use the new model rather than the old. *** Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs This was done using the followng script: https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm *** Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8 Differential Revision: https://phabricator.services.mozilla.com/D16747 *** Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16748 *** Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16749 *** Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs *** Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs Differential Revision: https://phabricator.services.mozilla.com/D16750 --HG-- extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895 extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 21:18:31 +03:00
const { Log } = ChromeUtils.import("chrome://marionette/content/log.js");
XPCOMUtils.defineLazyGetter(this, "logger", Log.get);
this.EXPORTED_SYMBOLS = [
"ContentEventObserverService",
"WebElementEventTarget",
];
/**
* The ``EventTarget`` for web elements can be used to observe DOM
* events in the content document.
*
* A caveat of the current implementation is that it is only possible
* to listen for top-level ``window`` global events.
*
* It needs to be backed by a :js:class:`ContentEventObserverService`
* in a content frame script.
*
* Usage::
*
* let observer = new WebElementEventTarget(messageManager);
* await new Promise(resolve => {
* observer.addEventListener("visibilitychange", resolve, {once: true});
* chromeWindow.minimize();
* });
*/
class WebElementEventTarget {
/**
* @param {function(): nsIMessageListenerManager} messageManagerFn
* Message manager to the current browser.
*/
constructor(messageManager) {
this.mm = messageManager;
this.listeners = {};
this.mm.addMessageListener("Marionette:DOM:OnEvent", this);
}
/**
* Register an event handler of a specific event type from the content
* frame.
*
* @param {string} type
* Event type to listen for.
* @param {EventListener} listener
* Object which receives a notification (a ``BareEvent``)
* when an event of the specified type occurs. This must be
* an object implementing the ``EventListener`` interface,
* or a JavaScript function.
* @param {boolean=} once
* Indicates that the ``listener`` should be invoked at
* most once after being added. If true, the ``listener``
* would automatically be removed when invoked.
*/
addEventListener(type, listener, { once = false } = {}) {
if (!(type in this.listeners)) {
this.listeners[type] = [];
}
if (!this.listeners[type].includes(listener)) {
listener.once = once;
this.listeners[type].push(listener);
}
this.mm.sendAsyncMessage("Marionette:DOM:AddEventListener", { type });
}
/**
* Removes an event listener.
*
* @param {string} type
* Type of event to cease listening for.
* @param {EventListener} listener
* Event handler to remove from the event target.
*/
removeEventListener(type, listener) {
if (!(type in this.listeners)) {
return;
}
let stack = this.listeners[type];
for (let i = stack.length - 1; i >= 0; --i) {
if (stack[i] === listener) {
stack.splice(i, 1);
if (stack.length == 0) {
this.mm.sendAsyncMessage("Marionette:DOM:RemoveEventListener", {
type,
});
}
return;
}
}
}
dispatchEvent(event) {
if (!(event.type in this.listeners)) {
return;
}
event.target = this;
let stack = this.listeners[event.type].slice(0);
stack.forEach(listener => {
if (typeof listener.handleEvent == "function") {
listener.handleEvent(event);
} else {
listener(event);
}
if (listener.once) {
this.removeEventListener(event.type, listener);
}
});
}
receiveMessage({ name, data, objects }) {
if (name != "Marionette:DOM:OnEvent") {
return;
}
let ev = {
type: data.type,
target: objects.target,
};
this.dispatchEvent(ev);
}
}
this.WebElementEventTarget = WebElementEventTarget;
/**
* Provides the frame script backend for the
* :js:class:`WebElementEventTarget`.
*
* This service receives requests for new DOM events to listen for and
* to cease listening for, and despatches IPC messages to the browser
* when they fire.
*/
class ContentEventObserverService {
/**
* @param {WindowProxy} windowGlobal
* Window.
* @param {nsIMessageSender.sendAsyncMessage} sendAsyncMessage
* Function for sending an async message to the parent browser.
*/
constructor(windowGlobal, sendAsyncMessage) {
this.window = windowGlobal;
this.sendAsyncMessage = sendAsyncMessage;
this.events = new Set();
}
/**
* Observe a new DOM event.
*
* When the DOM event of ``type`` fires, a message is passed to
* the parent browser's event observer.
*
* If event type is already being observed, only a single message
* is sent. E.g. multiple registration for events will only ever emit
* a maximum of one message.
*
* @param {string} type
* DOM event to listen for.
*/
add(type) {
if (this.events.has(type)) {
return;
}
this.window.addEventListener(type, this);
this.events.add(type);
}
/**
* Ceases observing a DOM event.
*
* @param {string} type
* DOM event to stop listening for.
*/
remove(type) {
if (!this.events.has(type)) {
return;
}
this.window.removeEventListener(type, this);
this.events.delete(type);
}
/** Ceases observing all previously registered DOM events. */
clear() {
for (let ev of this) {
this.remove(ev);
}
}
*[Symbol.iterator]() {
for (let ev of this.events) {
yield ev;
}
}
handleEvent({ type, target }) {
logger.trace(`Received DOM event ${type}`);
this.sendAsyncMessage("Marionette:DOM:OnEvent", { type }, { target });
}
}
this.ContentEventObserverService = ContentEventObserverService;