From 201cbd12be29704b656183451bdc12d49d4c01c8 Mon Sep 17 00:00:00 2001 From: Henrik Skupin Date: Thu, 23 Jul 2020 14:22:07 +0000 Subject: [PATCH] Bug 1654454 - [marionette] Port WebDriver:FindElement and WebDriver:FindElements to JSWindowActor. r=marionette-reviewers,maja_zf Differential Revision: https://phabricator.services.mozilla.com/D84568 --- .../actors/MarionetteFrameChild.jsm | 78 +++++++++++++++++++ .../actors/MarionetteFrameParent.jsm | 42 +++++++++- testing/marionette/driver.js | 34 +++++--- 3 files changed, 142 insertions(+), 12 deletions(-) diff --git a/testing/marionette/actors/MarionetteFrameChild.jsm b/testing/marionette/actors/MarionetteFrameChild.jsm index 5dd14ca311e8..ce0f02704651 100644 --- a/testing/marionette/actors/MarionetteFrameChild.jsm +++ b/testing/marionette/actors/MarionetteFrameChild.jsm @@ -10,11 +10,28 @@ const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); +const { element } = ChromeUtils.import( + "chrome://marionette/content/element.js" +); +const { error } = ChromeUtils.import("chrome://marionette/content/error.js"); +const { evaluate } = ChromeUtils.import( + "chrome://marionette/content/evaluate.js" +); const { Log } = ChromeUtils.import("chrome://marionette/content/log.js"); XPCOMUtils.defineLazyGetter(this, "logger", Log.get); class MarionetteFrameChild extends JSWindowActorChild { + constructor() { + super(); + + this.seenEls = new element.Store(); + } + + get content() { + return this.docShell.domWindow; + } + get id() { return this.browsingContext.id; } @@ -43,4 +60,65 @@ class MarionetteFrameChild extends JSWindowActorChild { break; } } + + async receiveMessage(msg) { + const { name, data: serializedData } = msg; + const data = evaluate.fromJSON(serializedData); + + try { + let result; + + switch (name) { + case "MarionetteFrameParent:findElement": + result = await this.findElement(data); + break; + case "MarionetteFrameParent:findElements": + result = await this.findElements(data); + break; + } + + return { data: evaluate.toJSON(result) }; + } catch (e) { + // Always wrap errors as WebDriverError + return { error: error.wrap(e).toJSON() }; + } + } + + // Implementation of WebDriver commands + + /** + * Find an element in the current browsing context's document using the + * given search strategy. + */ + async findElement(options = {}) { + const { strategy, selector, opts } = options; + + opts.all = false; + + if (opts.startNode) { + opts.startNode = this.seenEls.get(opts.startNode); + } + + const container = { frame: this.content }; + const el = await element.find(container, strategy, selector, opts); + return this.seenEls.add(el); + } + + /** + * Find elements in the current browsing context's document using the + * given search strategy. + */ + async findElements(options = {}) { + const { strategy, selector, opts } = options; + + opts.all = true; + + if (opts.startNode) { + opts.startNode = this.seenEls.get(opts.startNode); + } + + const container = { frame: this.content }; + const el = await element.find(container, strategy, selector, opts); + return this.seenEls.addAll(el); + } } diff --git a/testing/marionette/actors/MarionetteFrameParent.jsm b/testing/marionette/actors/MarionetteFrameParent.jsm index 69446a05cf95..bc03d5aa8ff4 100644 --- a/testing/marionette/actors/MarionetteFrameParent.jsm +++ b/testing/marionette/actors/MarionetteFrameParent.jsm @@ -2,7 +2,7 @@ * 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"; +("use strict"); const EXPORTED_SYMBOLS = ["MarionetteFrameParent"]; @@ -14,6 +14,12 @@ const { EventEmitter } = ChromeUtils.import( "resource://gre/modules/EventEmitter.jsm" ); +const { WebDriverError } = ChromeUtils.import( + "chrome://marionette/content/error.js" +); +const { evaluate } = ChromeUtils.import( + "chrome://marionette/content/evaluate.js" +); const { Log } = ChromeUtils.import("chrome://marionette/content/log.js"); XPCOMUtils.defineLazyGetter(this, "logger", Log.get); @@ -29,11 +35,43 @@ class MarionetteFrameParent extends JSWindowActorParent { logger.trace(`[${this.browsingContext.id}] Parent actor created`); } - receiveMessage({ name, data }) { + receiveMessage(msg) { + const { name, data } = msg; + switch (name) { case "MarionetteFrameChild:PageLoadEvent": this.emit("page-load-event", data); break; } } + + async sendQuery(name, data) { + const serializedData = evaluate.toJSON(data); + const result = await super.sendQuery(name, serializedData); + + if ("error" in result) { + throw WebDriverError.fromJSON(result.error); + } else { + return evaluate.fromJSON(result.data); + } + } + + // Proxying methods for WebDriver commands + // TODO: Maybe using a proxy class instead similar to proxy.js + + findElement(strategy, selector, opts) { + return this.sendQuery("MarionetteFrameParent:findElement", { + strategy, + selector, + opts, + }); + } + + findElements(strategy, selector, opts) { + return this.sendQuery("MarionetteFrameParent:findElements", { + strategy, + selector, + opts, + }); + } } diff --git a/testing/marionette/driver.js b/testing/marionette/driver.js index 0d47ce1bf5f7..7502f2138135 100644 --- a/testing/marionette/driver.js +++ b/testing/marionette/driver.js @@ -2053,16 +2053,18 @@ GeckoDriver.prototype.multiAction = async function(cmd) { * A modal dialog is open, blocking this operation. */ GeckoDriver.prototype.findElement = async function(cmd) { - const win = assert.open(this.getCurrentWindow()); - await this._handleUserPrompts(); + const { element: el, using, value } = cmd.parameters; - let { using, value } = cmd.parameters; if (!SUPPORTED_STRATEGIES.has(using)) { throw new InvalidSelectorError(`Strategy not supported: ${using}`); } + + const win = assert.open(this.getCurrentWindow()); + await this._handleUserPrompts(); + let startNode; - if (typeof cmd.parameters.element != "undefined") { - startNode = WebElement.fromUUID(cmd.parameters.element, this.context); + if (typeof el != "undefined") { + startNode = WebElement.fromUUID(el, this.context); } let opts = { @@ -2071,6 +2073,11 @@ GeckoDriver.prototype.findElement = async function(cmd) { all: false, }; + if (MarionettePrefs.useActors) { + const actor = await this.getActor(); + return actor.findElement(using, value, opts); + } + switch (this.context) { case Context.Chrome: let container = { frame: win }; @@ -2097,16 +2104,18 @@ GeckoDriver.prototype.findElement = async function(cmd) { * Value the client is looking for. */ GeckoDriver.prototype.findElements = async function(cmd) { - const win = assert.open(this.getCurrentWindow()); - await this._handleUserPrompts(); + const { element: el, using, value } = cmd.parameters; - let { using, value } = cmd.parameters; if (!SUPPORTED_STRATEGIES.has(using)) { throw new InvalidSelectorError(`Strategy not supported: ${using}`); } + + const win = assert.open(this.getCurrentWindow()); + await this._handleUserPrompts(); + let startNode; - if (typeof cmd.parameters.element != "undefined") { - startNode = WebElement.fromUUID(cmd.parameters.element, this.context); + if (typeof el != "undefined") { + startNode = WebElement.fromUUID(el, this.context); } let opts = { @@ -2115,6 +2124,11 @@ GeckoDriver.prototype.findElements = async function(cmd) { all: true, }; + if (MarionettePrefs.useActors) { + const actor = await this.getActor(); + return actor.findElements(using, value, opts); + } + switch (this.context) { case Context.Chrome: let container = { frame: win };