forked from mirrors/gecko-dev
		
	Bug 1691683 - [marionette] Remove unused WebElementEventTarget class. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D167688
This commit is contained in:
		
							parent
							
								
									c4409979ae
								
							
						
					
					
						commit
						f8b3f248f1
					
				
					 5 changed files with 5 additions and 491 deletions
				
			
		|  | @ -11,7 +11,6 @@ ChromeUtils.defineESModuleGetters(lazy, { | |||
|   MessageManagerDestroyedPromise: | ||||
|     "chrome://remote/content/marionette/sync.sys.mjs", | ||||
|   TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", | ||||
|   WebElementEventTarget: "chrome://remote/content/marionette/dom.sys.mjs", | ||||
|   windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs", | ||||
| }); | ||||
| 
 | ||||
|  | @ -312,9 +311,11 @@ browser.Context = class { | |||
|       await lazy.TabManager.selectTab(this.tab); | ||||
|     } | ||||
| 
 | ||||
|     // TODO(ato): Currently tied to curBrowser, but should be moved to
 | ||||
|     // WebReference when introduced by https://bugzil.la/1400256.
 | ||||
|     this.eventObserver = new lazy.WebElementEventTarget(this.messageManager); | ||||
|     // By accessing the content browser's message manager a new browsing
 | ||||
|     // context is created for browserless tabs, which is needed to successfully
 | ||||
|     // run the WebDriver's is browsing context open step. This is temporary
 | ||||
|     // until we find a better solution on bug 1812258.
 | ||||
|     this.messageManager; | ||||
| 
 | ||||
|     return this.tab; | ||||
|   } | ||||
|  |  | |||
|  | @ -1,208 +0,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/. */
 | ||||
| 
 | ||||
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; | ||||
| 
 | ||||
| const lazy = {}; | ||||
| 
 | ||||
| ChromeUtils.defineESModuleGetters(lazy, { | ||||
|   Log: "chrome://remote/content/shared/Log.sys.mjs", | ||||
| }); | ||||
| 
 | ||||
| XPCOMUtils.defineLazyGetter(lazy, "logger", () => | ||||
|   lazy.Log.get(lazy.Log.TYPES.MARIONETTE) | ||||
| ); | ||||
| 
 | ||||
| /** | ||||
|  * 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(); | ||||
|  *     }); | ||||
|  */ | ||||
| export 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) { | ||||
|           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 }) { | ||||
|     if (name != "Marionette:DOM:OnEvent") { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     let ev = { | ||||
|       type: data.type, | ||||
|     }; | ||||
|     this.dispatchEvent(ev); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 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. | ||||
|  */ | ||||
| export 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 }) { | ||||
|     lazy.logger.trace(`Received DOM event ${type}`); | ||||
|     this.sendAsyncMessage("Marionette:DOM:OnEvent", { type }); | ||||
|   } | ||||
| } | ||||
|  | @ -17,7 +17,6 @@ remote.jar: | |||
|   content/marionette/browser.sys.mjs (browser.sys.mjs) | ||||
|   content/marionette/cert.sys.mjs (cert.sys.mjs) | ||||
|   content/marionette/cookie.sys.mjs (cookie.sys.mjs) | ||||
|   content/marionette/dom.sys.mjs (dom.sys.mjs) | ||||
|   content/marionette/driver.sys.mjs (driver.sys.mjs) | ||||
|   content/marionette/element.sys.mjs (element.sys.mjs) | ||||
|   content/marionette/evaluate.sys.mjs (evaluate.sys.mjs) | ||||
|  |  | |||
|  | @ -1,277 +0,0 @@ | |||
| const { | ||||
|   ContentEventObserverService, | ||||
|   WebElementEventTarget, | ||||
| } = ChromeUtils.importESModule( | ||||
|   "chrome://remote/content/marionette/dom.sys.mjs" | ||||
| ); | ||||
| 
 | ||||
| class MessageSender { | ||||
|   constructor() { | ||||
|     this.listeners = {}; | ||||
|     this.sent = []; | ||||
|   } | ||||
| 
 | ||||
|   addMessageListener(name, listener) { | ||||
|     this.listeners[name] = listener; | ||||
|   } | ||||
| 
 | ||||
|   sendAsyncMessage(name, data) { | ||||
|     this.sent.push({ name, data }); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| class Window { | ||||
|   constructor() { | ||||
|     this.events = []; | ||||
|   } | ||||
| 
 | ||||
|   addEventListener(type) { | ||||
|     this.events.push(type); | ||||
|   } | ||||
| 
 | ||||
|   removeEventListener(type) { | ||||
|     for (let i = 0; i < this.events.length; ++i) { | ||||
|       if (this.events[i] === type) { | ||||
|         this.events.splice(i, 1); | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| add_test(function test_WebElementEventTarget_addEventListener_init() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
|   equal(Object.keys(eventTarget.listeners).length, 0); | ||||
|   equal(Object.keys(ipc.listeners).length, 1); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_addEventListener() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   let listener = () => {}; | ||||
|   eventTarget.addEventListener("click", listener); | ||||
| 
 | ||||
|   // click listener was appended
 | ||||
|   equal(Object.keys(eventTarget.listeners).length, 1); | ||||
|   ok("click" in eventTarget.listeners); | ||||
|   equal(eventTarget.listeners.click.length, 1); | ||||
|   equal(eventTarget.listeners.click[0], listener); | ||||
| 
 | ||||
|   // should have sent a registration message
 | ||||
|   deepEqual(ipc.sent[0], { | ||||
|     name: "Marionette:DOM:AddEventListener", | ||||
|     data: { type: "click" }, | ||||
|   }); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_addEventListener_sameReference() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   let listener = () => {}; | ||||
|   eventTarget.addEventListener("click", listener); | ||||
|   eventTarget.addEventListener("click", listener); | ||||
|   equal(eventTarget.listeners.click.length, 1); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_WebElementEventTarget_addEventListener_once() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   eventTarget.addEventListener("click", () => {}, { once: true }); | ||||
|   equal(eventTarget.listeners.click[0].once, true); | ||||
| 
 | ||||
|   eventTarget.dispatchEvent({ type: "click" }); | ||||
|   equal(eventTarget.listeners.click.length, 0); | ||||
|   deepEqual(ipc.sent[1], { | ||||
|     name: "Marionette:DOM:RemoveEventListener", | ||||
|     data: { type: "click" }, | ||||
|   }); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_WebElementEventTarget_removeEventListener() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   equal(Object.keys(eventTarget.listeners).length, 0); | ||||
|   eventTarget.removeEventListener("click", () => {}); | ||||
|   equal(Object.keys(eventTarget.listeners).length, 0); | ||||
| 
 | ||||
|   let firstListener = () => {}; | ||||
|   eventTarget.addEventListener("click", firstListener); | ||||
|   equal(eventTarget.listeners.click.length, 1); | ||||
|   ok(eventTarget.listeners.click[0] === firstListener); | ||||
| 
 | ||||
|   let secondListener = () => {}; | ||||
|   eventTarget.addEventListener("click", secondListener); | ||||
|   equal(eventTarget.listeners.click.length, 2); | ||||
|   ok(eventTarget.listeners.click[1] === secondListener); | ||||
| 
 | ||||
|   ok(eventTarget.listeners.click[0] !== eventTarget.listeners.click[1]); | ||||
| 
 | ||||
|   eventTarget.removeEventListener("click", secondListener); | ||||
|   equal(eventTarget.listeners.click.length, 1); | ||||
|   ok(eventTarget.listeners.click[0] === firstListener); | ||||
| 
 | ||||
|   // event should not have been unregistered
 | ||||
|   // because there still exists another click event
 | ||||
|   equal(ipc.sent[ipc.sent.length - 1].name, "Marionette:DOM:AddEventListener"); | ||||
| 
 | ||||
|   eventTarget.removeEventListener("click", firstListener); | ||||
|   equal(eventTarget.listeners.click.length, 0); | ||||
|   deepEqual(ipc.sent[ipc.sent.length - 1], { | ||||
|     name: "Marionette:DOM:RemoveEventListener", | ||||
|     data: { type: "click" }, | ||||
|   }); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_WebElementEventTarget_dispatchEvent() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   let listenerCalled = false; | ||||
|   let listener = () => (listenerCalled = true); | ||||
|   eventTarget.addEventListener("click", listener); | ||||
|   eventTarget.dispatchEvent({ type: "click" }); | ||||
|   ok(listenerCalled); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_WebElementEventTarget_dispatchEvent_multipleListeners() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let eventTarget = new WebElementEventTarget(ipc); | ||||
| 
 | ||||
|   let clicksA = 0; | ||||
|   let clicksB = 0; | ||||
|   let listenerA = () => ++clicksA; | ||||
|   let listenerB = () => ++clicksB; | ||||
| 
 | ||||
|   // the same listener should only be added, and consequently fire, once
 | ||||
|   eventTarget.addEventListener("click", listenerA); | ||||
|   eventTarget.addEventListener("click", listenerA); | ||||
|   eventTarget.addEventListener("click", listenerB); | ||||
|   eventTarget.dispatchEvent({ type: "click" }); | ||||
|   equal(clicksA, 1); | ||||
|   equal(clicksB, 1); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_ContentEventObserverService_add() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let win = new Window(); | ||||
|   let obs = new ContentEventObserverService( | ||||
|     win, | ||||
|     ipc.sendAsyncMessage.bind(ipc) | ||||
|   ); | ||||
| 
 | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   obs.add("foo"); | ||||
|   equal(obs.events.size, 1); | ||||
|   equal(win.events.length, 1); | ||||
|   equal(obs.events.values().next().value, "foo"); | ||||
|   equal(win.events[0], "foo"); | ||||
| 
 | ||||
|   obs.add("foo"); | ||||
|   equal(obs.events.size, 1); | ||||
|   equal(win.events.length, 1); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_ContentEventObserverService_remove() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let win = new Window(); | ||||
|   let obs = new ContentEventObserverService( | ||||
|     win, | ||||
|     ipc.sendAsyncMessage.bind(ipc) | ||||
|   ); | ||||
| 
 | ||||
|   obs.remove("foo"); | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   obs.add("bar"); | ||||
|   equal(obs.events.size, 1); | ||||
|   equal(win.events.length, 1); | ||||
| 
 | ||||
|   obs.remove("bar"); | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   obs.add("baz"); | ||||
|   obs.add("baz"); | ||||
|   equal(obs.events.size, 1); | ||||
|   equal(win.events.length, 1); | ||||
| 
 | ||||
|   obs.add("bah"); | ||||
|   equal(obs.events.size, 2); | ||||
|   equal(win.events.length, 2); | ||||
| 
 | ||||
|   obs.remove("baz"); | ||||
|   equal(obs.events.size, 1); | ||||
|   equal(win.events.length, 1); | ||||
| 
 | ||||
|   obs.remove("bah"); | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_ContentEventObserverService_clear() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let win = new Window(); | ||||
|   let obs = new ContentEventObserverService( | ||||
|     win, | ||||
|     ipc.sendAsyncMessage.bind(ipc) | ||||
|   ); | ||||
| 
 | ||||
|   obs.clear(); | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   obs.add("foo"); | ||||
|   obs.add("foo"); | ||||
|   obs.add("bar"); | ||||
|   equal(obs.events.size, 2); | ||||
|   equal(win.events.length, 2); | ||||
| 
 | ||||
|   obs.clear(); | ||||
|   equal(obs.events.size, 0); | ||||
|   equal(win.events.length, 0); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
| 
 | ||||
| add_test(function test_ContentEventObserverService_handleEvent() { | ||||
|   let ipc = new MessageSender(); | ||||
|   let win = new Window(); | ||||
|   let obs = new ContentEventObserverService( | ||||
|     win, | ||||
|     ipc.sendAsyncMessage.bind(ipc) | ||||
|   ); | ||||
| 
 | ||||
|   obs.handleEvent({ type: "click", target: win }); | ||||
|   deepEqual(ipc.sent[0], { | ||||
|     name: "Marionette:DOM:OnEvent", | ||||
|     data: { type: "click" }, | ||||
|   }); | ||||
| 
 | ||||
|   run_next_test(); | ||||
| }); | ||||
|  | @ -10,7 +10,6 @@ skip-if = appname == "thunderbird" | |||
| [test_actors.js] | ||||
| [test_browser.js] | ||||
| [test_cookie.js] | ||||
| [test_dom.js] | ||||
| [test_element.js] | ||||
| [test_json.js] | ||||
| [test_message.js] | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Henrik Skupin
						Henrik Skupin