--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -18,17 +18,21 @@ Cu.import("chrome://marionette/content/a
Cu.import("chrome://marionette/content/atom.js");
const {
browser,
WindowState,
} = Cu.import("chrome://marionette/content/browser.js", {});
Cu.import("chrome://marionette/content/capture.js");
Cu.import("chrome://marionette/content/cert.js");
Cu.import("chrome://marionette/content/cookie.js");
-Cu.import("chrome://marionette/content/element.js");
+const {
+ ChromeWebElement,
+ element,
+ WebElement,
+} = Cu.import("chrome://marionette/content/element.js", {});
const {
ElementNotInteractableError,
InsecureCertificateError,
InvalidArgumentError,
InvalidCookieDomainError,
InvalidSelectorError,
NoAlertOpenError,
NoSuchFrameError,
@@ -334,17 +338,17 @@ GeckoDriver.prototype.switchToGlobalMess
* <tt>Marionette:SUFFIX</tt>.
* @param {Object=} msg
* Optional JSON serialisable object to send to the listener.
* @param {number=} commandID
* Optional command ID to ensure synchronisity.
*/
GeckoDriver.prototype.sendAsync = function(name, data, commandID) {
name = "Marionette:" + name;
- let payload = copy(data);
+ let payload = evaluate.toJSON(data, this.seenEls);
// TODO(ato): When proxy.AsyncMessageChannel
// is used for all chrome <-> content communication
// this can be removed.
if (commandID) {
payload.commandID = commandID;
}
@@ -593,17 +597,17 @@ GeckoDriver.prototype.registerBrowser =
// curBrowser holds all the registered frames in knownFrames
this.curBrowser.register(id, be);
}
this.wins.set(id, listenerWindow);
if (nullPrevious && (this.curBrowser.curFrameId !== null)) {
this.sendAsync(
"newSession",
- this.capabilities.toJSON(),
+ this.capabilities,
this.newSessionCommandId);
if (this.curBrowser.isNewSession) {
this.newSessionCommandId = null;
}
}
return [id, this.capabilities.toJSON()];
};
@@ -1674,28 +1678,25 @@ GeckoDriver.prototype.setWindowHandle =
GeckoDriver.prototype.getActiveFrame = function(cmd, resp) {
assert.window(this.getCurrentWindow());
switch (this.context) {
case Context.CHROME:
// no frame means top-level
resp.body.value = null;
if (this.curFrame) {
- let elRef = this.curBrowser.seenEls
- .add(this.curFrame.frameElement);
- let el = element.makeWebElement(elRef);
- resp.body.value = el;
+ resp.body.value = this.curBrowser.seenEls.add(
+ this.curFrame.frameElement);
}
break;
case Context.CONTENT:
resp.body.value = null;
if (this.currentFrameElement !== null) {
- let el = element.makeWebElement(this.currentFrameElement);
- resp.body.value = el;
+ resp.body.value = this.currentFrameElement;
}
break;
}
};
/**
* Set the current browsing context for future commands to the parent
* of the current browsing context.
@@ -1763,20 +1764,21 @@ GeckoDriver.prototype.switchToFrame = as
if (focus) {
this.mainFrame.focus();
}
checkTimer.initWithCallback(
checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
- // by element
- if (this.curBrowser.seenEls.has(element)) {
- // HTMLIFrameElement
- let wantedFrame = this.curBrowser.seenEls.get(element);
+ // by element (HTMLIFrameElement)
+ if (typeof element != "undefined") {
+ let webEl = WebElement.fromUUID(element, Context.CHROME);
+ let wantedFrame = this.curBrowser.seenEls.get(webEl);
+
// Deal with an embedded xul:browser case
if (wantedFrame.tagName == "xul:browser" ||
wantedFrame.tagName == "browser") {
curWindow = wantedFrame.contentWindow;
this.curFrame = curWindow;
if (focus) {
this.curFrame.focus();
}
@@ -1826,17 +1828,17 @@ GeckoDriver.prototype.switchToFrame = as
checkTimer.initWithCallback(
checkLoad.bind(this), 100, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
}
}
switch (typeof id) {
- case "string" :
+ case "string":
let foundById = null;
let frames = curWindow.document.getElementsByTagName("iframe");
let numFrames = frames.length;
for (let i = 0; i < numFrames; i++) {
// give precedence to name
let frame = frames[i];
if (frame.getAttribute("name") == id) {
foundFrame = i;
@@ -1846,16 +1848,17 @@ GeckoDriver.prototype.switchToFrame = as
foundById = i;
}
}
if (foundFrame === null && foundById !== null) {
foundFrame = foundById;
curWindow = frames[foundById].contentWindow;
}
break;
+
case "number":
if (typeof curWindow.frames[id] != "undefined") {
foundFrame = id;
let frameEl = curWindow.frames[foundFrame].frameElement;
curWindow = frameEl.contentWindow;
}
break;
}
@@ -1921,25 +1924,26 @@ GeckoDriver.prototype.setTimeouts = func
this.timeouts = session.Timeouts.fromJSON(merged);
};
/** Single tap. */
GeckoDriver.prototype.singleTap = async function(cmd) {
assert.window(this.getCurrentWindow());
let {id, x, y} = cmd.parameters;
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
throw new UnsupportedOperationError(
"Command 'singleTap' is not yet available in chrome context");
case Context.CONTENT:
this.addFrameCloseListener("tap");
- await this.listener.singleTap(id, x, y);
+ await this.listener.singleTap(webEl, x, y);
break;
}
};
/**
* Perform a series of grouped actions at the specified points in time.
*
* @param {Array.<?>} actions
@@ -2058,91 +2062,90 @@ GeckoDriver.prototype.multiAction = asyn
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.findElement = async function(cmd, resp) {
const win = assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let strategy = cmd.parameters.using;
- let expr = cmd.parameters.value;
+ let {using, value} = cmd.parameters;
+ let startNode;
+ if (typeof cmd.parameters.element != "undefined") {
+ startNode = WebElement.fromUUID(cmd.parameters.element, this.context);
+ }
+
let opts = {
- startNode: cmd.parameters.element,
+ startNode,
timeout: this.timeouts.implicit,
all: false,
};
switch (this.context) {
case Context.CHROME:
- if (!SUPPORTED_STRATEGIES.has(strategy)) {
- throw new InvalidSelectorError(`Strategy not supported: ${strategy}`);
+ if (!SUPPORTED_STRATEGIES.has(using)) {
+ throw new InvalidSelectorError(`Strategy not supported: ${using}`);
}
let container = {frame: win};
if (opts.startNode) {
opts.startNode = this.curBrowser.seenEls.get(opts.startNode);
}
- let el = await element.find(container, strategy, expr, opts);
- let elRef = this.curBrowser.seenEls.add(el);
- let webEl = element.makeWebElement(elRef);
-
+ let el = await element.find(container, using, value, opts);
+ let webEl = this.curBrowser.seenEls.add(el);
resp.body.value = webEl;
break;
case Context.CONTENT:
resp.body.value = await this.listener.findElementContent(
- strategy,
- expr,
- opts);
+ using, value, opts);
break;
}
};
/**
* Find elements using the indicated search strategy.
*
* @param {string} using
* Indicates which search method to use.
* @param {string} value
* Value the client is looking for.
*/
GeckoDriver.prototype.findElements = async function(cmd, resp) {
- let win = assert.window(this.getCurrentWindow());
-
- let strategy = cmd.parameters.using;
- let expr = cmd.parameters.value;
+ const win = assert.window(this.getCurrentWindow());
+
+ let {using, value} = cmd.parameters;
+ let startNode;
+ if (typeof cmd.parameters.element != "undefined") {
+ startNode = WebElement.fromUUID(cmd.parameters.element, this.context);
+ }
+
let opts = {
- startNode: cmd.parameters.element,
+ startNode,
timeout: this.timeouts.implicit,
all: true,
};
switch (this.context) {
case Context.CHROME:
- if (!SUPPORTED_STRATEGIES.has(strategy)) {
- throw new InvalidSelectorError(`Strategy not supported: ${strategy}`);
+ if (!SUPPORTED_STRATEGIES.has(using)) {
+ throw new InvalidSelectorError(`Strategy not supported: ${using}`);
}
let container = {frame: win};
- if (opts.startNode) {
+ if (startNode) {
opts.startNode = this.curBrowser.seenEls.get(opts.startNode);
}
- let els = await element.find(container, strategy, expr, opts);
-
- let elRefs = this.curBrowser.seenEls.addAll(els);
- let webEls = elRefs.map(element.makeWebElement);
+ let els = await element.find(container, using, value, opts);
+ let webEls = this.curBrowser.seenEls.addAll(els);
resp.body = webEls;
break;
case Context.CONTENT:
- resp.body = await this.listener.findElementsContent(
- cmd.parameters.using,
- cmd.parameters.value,
- opts);
+ resp.body = await this.listener.findElementsContent(using, value, opts);
break;
}
};
/**
* Return the active element on the page.
*
* @return {WebElement}
@@ -2164,56 +2167,61 @@ GeckoDriver.prototype.getActiveElement =
};
/**
* Send click event to element.
*
* @param {string} id
* Reference ID to the element that will be clicked.
*
+ * @throws {InvalidArgumentError}
+ * If element <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.clickElement = async function(cmd) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
await interaction.clickElement(el, this.a11yChecks);
break;
case Context.CONTENT:
// We need to protect against the click causing an OOP frame
// to close. This fires the mozbrowserclose event when it closes
// so we need to listen for it and then just send an error back.
// The person making the call should be aware something is not right
// and handle accordingly.
this.addFrameCloseListener("click");
let click = this.listener.clickElement(
- {id, pageTimeout: this.timeouts.pageLoad});
+ {webElRef: webEl.toJSON(), pageTimeout: this.timeouts.pageLoad});
// If a reload of the frame script interrupts our page load, this will
// never return. We need to re-issue this request to correctly poll for
// readyState and send errors.
this.curBrowser.pendingCommands.push(() => {
let parameters = {
// TODO(ato): Bug 1242595
commandID: this.listener.activeMessageId,
pageTimeout: this.timeouts.pageLoad,
startTime: new Date().getTime(),
};
this.mm.broadcastAsyncMessage(
- "Marionette:waitForPageLoaded" + this.curBrowser.curFrameId,
+ `Marionette:waitForPageLoaded${this.curBrowser.curFrameId}`,
parameters);
});
await click;
break;
}
};
@@ -2223,387 +2231,452 @@ GeckoDriver.prototype.clickElement = asy
* @param {string} id
* Web element reference ID to the element that will be inspected.
* @param {string} name
* Name of the attribute which value to retrieve.
*
* @return {string}
* Value of the attribute.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> or <var>name</var> are not strings.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementAttribute = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let {id, name} = cmd.parameters;
+ let id = assert.string(cmd.parameters.id);
+ let name = assert.string(cmd.parameters.name);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = el.getAttribute(name);
break;
case Context.CONTENT:
- resp.body.value = await this.listener.getElementAttribute(id, name);
+ resp.body.value = await this.listener.getElementAttribute(webEl, name);
break;
}
};
/**
* Returns the value of a property associated with given element.
*
* @param {string} id
* Web element reference ID to the element that will be inspected.
* @param {string} name
* Name of the property which value to retrieve.
*
* @return {string}
* Value of the property.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> or <var>name</var> are not strings.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementProperty = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let {id, name} = cmd.parameters;
+ let id = assert.string(cmd.parameters.id);
+ let name = assert.string(cmd.parameters.name);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = el[name];
break;
case Context.CONTENT:
- resp.body.value = await this.listener.getElementProperty(id, name);
+ resp.body.value = await this.listener.getElementProperty(webEl, name);
break;
}
};
/**
* Get the text of an element, if any. Includes the text of all child
* elements.
*
* @param {string} id
* Reference ID to the element that will be inspected.
*
* @return {string}
* Element's text "as rendered".
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementText = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
// for chrome, we look at text nodes, and any node with a "label" field
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
let lines = [];
this.getVisibleText(el, lines);
resp.body.value = lines.join("\n");
break;
case Context.CONTENT:
- resp.body.value = await this.listener.getElementText(id);
+ resp.body.value = await this.listener.getElementText(webEl);
break;
}
};
/**
* Get the tag name of the element.
*
* @param {string} id
* Reference ID to the element that will be inspected.
*
* @return {string}
* Local tag name of element.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementTagName = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = el.tagName.toLowerCase();
break;
case Context.CONTENT:
- resp.body.value = await this.listener.getElementTagName(id);
+ resp.body.value = await this.listener.getElementTagName(webEl);
break;
}
};
/**
* Check if element is displayed.
*
* @param {string} id
* Reference ID to the element that will be inspected.
*
* @return {boolean}
* True if displayed, false otherwise.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.isElementDisplayed = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = await interaction.isElementDisplayed(
el, this.a11yChecks);
break;
case Context.CONTENT:
- resp.body.value = await this.listener.isElementDisplayed(id);
+ resp.body.value = await this.listener.isElementDisplayed(webEl);
break;
}
};
/**
* Return the property of the computed style of an element.
*
* @param {string} id
* Reference ID to the element that will be checked.
* @param {string} propertyName
* CSS rule that is being requested.
*
* @return {string}
* Value of |propertyName|.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> or <var>propertyName</var> are not strings.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementValueOfCssProperty = async function(
cmd, resp) {
const win = assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let {id, propertyName: prop} = cmd.parameters;
+ let id = assert.string(cmd.parameters.id);
+ let prop = assert.string(cmd.parameters.propertyName);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
let sty = win.document.defaultView.getComputedStyle(el);
resp.body.value = sty.getPropertyValue(prop);
break;
case Context.CONTENT:
resp.body.value = await this.listener
- .getElementValueOfCssProperty(id, prop);
+ .getElementValueOfCssProperty(webEl, prop);
break;
}
};
/**
* Check if element is enabled.
*
* @param {string} id
* Reference ID to the element that will be checked.
*
* @return {boolean}
* True if enabled, false if disabled.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.isElementEnabled = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
// Selenium atom doesn't quite work here
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = await interaction.isElementEnabled(
el, this.a11yChecks);
break;
case Context.CONTENT:
- resp.body.value = await this.listener.isElementEnabled(id);
+ resp.body.value = await this.listener.isElementEnabled(webEl);
break;
}
};
/**
* Check if element is selected.
*
* @param {string} id
* Reference ID to the element that will be checked.
*
* @return {boolean}
* True if selected, false if unselected.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.isElementSelected = async function(cmd, resp) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
// Selenium atom doesn't quite work here
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
resp.body.value = await interaction.isElementSelected(
el, this.a11yChecks);
break;
case Context.CONTENT:
- resp.body.value = await this.listener.isElementSelected(id);
+ resp.body.value = await this.listener.isElementSelected(webEl);
break;
}
};
/**
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.getElementRect = async function(cmd, resp) {
const win = assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
let rect = el.getBoundingClientRect();
resp.body = {
x: rect.x + win.pageXOffset,
y: rect.y + win.pageYOffset,
width: rect.width,
height: rect.height,
};
break;
case Context.CONTENT:
- resp.body = await this.listener.getElementRect(id);
+ resp.body = await this.listener.getElementRect(webEl);
break;
}
};
/**
* Send key presses to element after focusing on it.
*
* @param {string} id
* Reference ID to the element that will be checked.
- * @param {string} value
+ * @param {string} text
* Value to send to the element.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> or <var>text</var> are not strings.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.sendKeysToElement = async function(cmd) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let {id, text} = cmd.parameters;
- assert.string(text);
+ let id = assert.string(cmd.parameters.id);
+ let text = assert.string(cmd.parameters.text);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
await interaction.sendKeysToElement(
el, text, true, this.a11yChecks);
break;
case Context.CONTENT:
- await this.listener.sendKeysToElement(id, text);
+ await this.listener.sendKeysToElement(webEl, text);
break;
}
};
/**
* Clear the text of an element.
*
* @param {string} id
* Reference ID to the element that will be cleared.
*
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
* @throws {NoSuchWindowError}
* Top-level browsing context has been discarded.
* @throws {UnexpectedAlertOpenError}
* A modal dialog is open, blocking this operation.
*/
GeckoDriver.prototype.clearElement = async function(cmd) {
assert.window(this.getCurrentWindow());
assert.noUserPrompt(this.dialog);
- let id = cmd.parameters.id;
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
switch (this.context) {
case Context.CHROME:
// the selenium atom doesn't work here
- let el = this.curBrowser.seenEls.get(id);
+ let el = this.curBrowser.seenEls.get(webEl);
if (el.nodeName == "textbox") {
el.value = "";
} else if (el.nodeName == "checkbox") {
el.checked = false;
}
break;
case Context.CONTENT:
- await this.listener.clearElement(id);
+ await this.listener.clearElement(webEl);
break;
}
};
/**
* Switch to shadow root of the given host element.
*
- * @param {string} id element id.
+ * @param {string} id
+ * Reference ID to the element.
+ *
+ * @throws {InvalidArgumentError}
+ * If <var>id</var> is not a string.
+ * @throws {NoSuchElementError}
+ * If element represented by reference <var>id</var> is unknown.
*/
GeckoDriver.prototype.switchToShadowRoot = async function(cmd) {
assert.content(this.context);
assert.window(this.getCurrentWindow());
- let id = cmd.parameters.id;
- await this.listener.switchToShadowRoot(id);
+ let id = assert.string(cmd.parameters.id);
+ let webEl = WebElement.fromUUID(id, this.context);
+ await this.listener.switchToShadowRoot(webEl);
};
/**
* Add a single cookie to the cookie store associated with the active
* document's address.
*
* @param {Map.<string, (string|number|boolean)> cookie
* Cookie object.
@@ -2887,29 +2960,31 @@ GeckoDriver.prototype.takeScreenshot = f
let win = assert.window(this.getCurrentWindow());
let {id, highlights, full, hash} = cmd.parameters;
highlights = highlights || [];
let format = hash ? capture.Format.Hash : capture.Format.Base64;
switch (this.context) {
case Context.CHROME:
- let highlightEls = highlights.map(
- ref => this.curBrowser.seenEls.get(ref));
+ let highlightEls = highlights
+ .map(ref => WebElement.fromUUID(ref, Context.CHROME))
+ .map(webEl => this.curBrowser.seenEls.get(webEl));
// viewport
let canvas;
if (!id && !full) {
canvas = capture.viewport(win, highlightEls);
// element or full document element
} else {
let node;
if (id) {
- node = this.curBrowser.seenEls.get(id);
+ let webEl = WebElement.fromUUID(id, Context.CHROME);
+ node = this.curBrowser.seenEls.get(webEl);
} else {
node = win.document.documentElement;
}
canvas = capture.element(node, highlightEls);
}
switch (format) {
@@ -3365,19 +3440,25 @@ GeckoDriver.prototype.receiveMessage = f
case "Marionette:switchedToFrame":
if (message.json.restorePrevious) {
this.currentFrameElement = this.previousFrameElement;
} else {
// we don't arbitrarily save previousFrameElement, since
// we allow frame switching after modals appear, which would
// override this value and we'd lose our reference
if (message.json.storePrevious) {
- this.previousFrameElement = this.currentFrameElement;
+ this.previousFrameElement =
+ new ChromeWebElement(this.currentFrameElement);
}
- this.currentFrameElement = message.json.frameValue;
+ if (message.json.frameValue) {
+ this.currentFrameElement =
+ new ChromeWebElement(message.json.frameValue);
+ } else {
+ this.currentFrameElement = null;
+ }
}
break;
case "Marionette:emitTouchEvent":
globalMessageManager.broadcastAsyncMessage(
"MarionetteMainListener:emitTouchEvent", message.json);
break;
@@ -3387,17 +3468,17 @@ GeckoDriver.prototype.receiveMessage = f
let rv = this.registerBrowser(wid, be);
return rv;
case "Marionette:listenersAttached":
if (message.json.listenerId === this.curBrowser.curFrameId) {
// If the frame script gets reloaded we need to call newSession.
// In the case of desktop this just sets up a small amount of state
// that doesn't change over the course of a session.
- this.sendAsync("newSession", this.capabilities.toJSON());
+ this.sendAsync("newSession", this.capabilities);
this.curBrowser.flushPendingCommands();
}
break;
}
};
/* eslint-enable consistent-return */
GeckoDriver.prototype.responseCompleted = function() {
@@ -3676,25 +3757,16 @@ GeckoDriver.prototype.commands = {
"singleTap": GeckoDriver.prototype.singleTap,
"switchToFrame": GeckoDriver.prototype.switchToFrame,
"switchToParentFrame": GeckoDriver.prototype.switchToParentFrame,
"switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot,
"switchToWindow": GeckoDriver.prototype.switchToWindow,
"takeScreenshot": GeckoDriver.prototype.takeScreenshot,
};
-function copy(obj) {
- if (Array.isArray(obj)) {
- return obj.slice();
- } else if (typeof obj == "object") {
- return Object.assign({}, obj);
- }
- return obj;
-}
-
function getOuterWindowId(win) {
return win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.outerWindowID;
}
/**
* Exit fullscreen and wait for <var>window</var> to resize.
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -637,23 +637,16 @@ element.isCollection = function(seq) {
case "[object NodeList]":
return true;
default:
return false;
}
};
-element.makeWebElement = function(uuid) {
- return {
- [element.Key]: uuid,
- [element.LegacyKey]: uuid,
- };
-};
-
/**
* Determines if <var>el</var> is stale.
*
* A stale element is an element no longer attached to the DOM or which
* node document is not the active document.
*
* @param {Element} el
* DOM element to check for staleness.
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -13,17 +13,16 @@ Cu.import("resource://gre/modules/XPCOMU
const {
element,
WebElement,
} = Cu.import("chrome://marionette/content/element.js", {});
const {
JavaScriptError,
ScriptTimeoutError,
- WebDriverError,
} = Cu.import("chrome://marionette/content/error.js", {});
const log = Log.repository.getLogger("Marionette");
this.EXPORTED_SYMBOLS = ["evaluate", "sandbox", "Sandboxes"];
const ARGUMENTS = "__webDriverArguments";
const CALLBACK = "__webDriverCallback";
--- a/testing/marionette/legacyaction.js
+++ b/testing/marionette/legacyaction.js
@@ -2,17 +2,20 @@
* 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/. */
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("chrome://marionette/content/element.js");
+const {
+ element,
+ WebElement,
+} = Cu.import("chrome://marionette/content/element.js", {});
Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
const CONTEXT_MENU_DELAY_PREF = "ui.click_hold_context_menus.delay";
const DEFAULT_CONTEXT_MENU_DELAY = 750; // ms
this.EXPORTED_SYMBOLS = ["legacyaction"];
@@ -173,16 +176,17 @@ action.Chain.prototype.actions = functio
if (i == chain.length) {
cb(touchId || null);
this.resetValues();
return;
}
let pack = chain[i];
let command = pack[0];
+ let webEl;
let el;
let c;
i++;
if (["press", "wait", "keyDown", "keyUp", "click"].indexOf(command) == -1) {
// if mouseEventsOnly, then touchIds isn't used
if (!(touchId in this.touchIds) && !this.mouseEventsOnly) {
this.resetValues();
@@ -197,17 +201,18 @@ action.Chain.prototype.actions = functio
break;
case "keyUp":
event.sendKeyUp(pack[1], keyModifiers, this.container.frame);
this.actions(chain, touchId, i, keyModifiers, cb);
break;
case "click":
- el = this.seenEls.get(pack[1]);
+ webEl = WebElement.fromUUID(pack[1], "content");
+ el = this.seenEls.get(webEl);
let button = pack[2];
let clickCount = pack[3];
c = element.coordinates(el);
this.mouseTap(el.ownerDocument, c.x, c.y, button, clickCount, keyModifiers);
if (button == 2) {
this.emitMouseEvent(el.ownerDocument, "contextmenu", c.x, c.y,
button, clickCount, keyModifiers);
}
@@ -228,17 +233,18 @@ action.Chain.prototype.actions = functio
"Invalid Command: press cannot follow an active touch event");
}
// look ahead to check if we're scrolling,
// needed for APZ touch dispatching
if ((i != chain.length) && (chain[i][0].indexOf('move') !== -1)) {
this.scrolling = true;
}
- el = this.seenEls.get(pack[1]);
+ webEl = WebElement.fromUUID(pack[1], "content");
+ el = this.seenEls.get(webEl);
c = element.coordinates(el, pack[2], pack[3]);
touchId = this.generateEvents("press", c.x, c.y, null, el, keyModifiers);
this.actions(chain, touchId, i, keyModifiers, cb);
break;
case "release":
this.generateEvents(
"release",
@@ -247,17 +253,18 @@ action.Chain.prototype.actions = functio
touchId,
null,
keyModifiers);
this.actions(chain, null, i, keyModifiers, cb);
this.scrolling = false;
break;
case "move":
- el = this.seenEls.get(pack[1]);
+ webEl = WebElement.fromUUID(pack[1], "content");
+ el = this.seenEls.get(webEl);
c = element.coordinates(el);
this.generateEvents("move", c.x, c.y, touchId, null, keyModifiers);
this.actions(chain, touchId, i, keyModifiers, cb);
break;
case "moveByOffset":
this.generateEvents(
"move",
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -16,26 +16,30 @@ Cu.import("resource://gre/modules/FileUt
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("chrome://marionette/content/accessibility.js");
Cu.import("chrome://marionette/content/action.js");
Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/capture.js");
-Cu.import("chrome://marionette/content/element.js");
+const {
+ element,
+ WebElement,
+} = Cu.import("chrome://marionette/content/element.js", {});
const {
ElementNotInteractableError,
error,
InsecureCertificateError,
InvalidArgumentError,
InvalidElementStateError,
InvalidSelectorError,
NoSuchElementError,
NoSuchFrameError,
+ pprint,
TimeoutError,
UnknownError,
} = Cu.import("chrome://marionette/content/error.js", {});
Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
const {ContentEventObserverService} = Cu.import("chrome://marionette/content/dom.js", {});
Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/legacyaction.js");
@@ -764,34 +768,24 @@ function checkForInterrupted() {
sendSyncMessage("Marionette:switchToModalOrigin");
}
sendSyncMessage("Marionette:switchedToFrame", {restorePrevious: true});
}
}
async function execute(script, args, timeout, opts) {
opts.timeout = timeout;
-
let sb = sandbox.createMutable(curContainer.frame);
- let wargs = evaluate.fromJSON(
- args, seenEls, curContainer.frame, curContainer.shadowRoot);
- let res = await evaluate.sandbox(sb, script, wargs, opts);
-
- return evaluate.toJSON(res, seenEls);
+ return evaluate.sandbox(sb, script, args, opts);
}
async function executeInSandbox(script, args, timeout, opts) {
opts.timeout = timeout;
-
let sb = sandboxes.get(opts.sandboxName, opts.newSandbox);
- let wargs = evaluate.fromJSON(
- args, seenEls, curContainer.frame, curContainer.shadowRoot);
-
- let res = await evaluate.sandbox(sb, script, wargs, opts);
- return evaluate.toJSON(res, seenEls);
+ return evaluate.sandbox(sb, script, args, opts);
}
function emitTouchEvent(type, touch) {
if (!wasInterrupted()) {
logger.info(`Emitting Touch event of type ${type} ` +
`to element with id: ${touch.target.id} ` +
`and tag name: ${touch.target.tagName} ` +
`at coordinates (${touch.clientX}), ` +
@@ -842,18 +836,17 @@ function emitTouchEvent(type, touch) {
1,
0);
}
}
/**
* Function that perform a single tap
*/
-async function singleTap(id, corx, cory) {
- let el = seenEls.get(id);
+async function singleTap(el, corx, cory) {
// after this block, the element will be scrolled into view
let visible = element.isVisible(el, corx, cory);
if (!visible) {
throw new ElementNotInteractableError(
"Element is not currently visible and may not be manipulated");
}
let a11y = accessibility.get(capabilities.get("moz:accessibilityChecks"));
@@ -1131,17 +1124,16 @@ function cancelRequest() {
* @param {number} pageTimeout
* Timeout in seconds the method has to wait for the page being
* finished loading.
* @param {number} startTime
* Unix timestap when the navitation request got triggred.
*/
function waitForPageLoaded(msg) {
let {commandID, pageTimeout, startTime} = msg.json;
-
loadListener.waitForLoadAfterFramescriptReload(
commandID, pageTimeout, startTime);
}
/**
* Navigate to the given URL. The operation will be performed on the
* current browsing context, which means it handles the case where we
* navigate within an iframe. All other navigation is handled by the driver
@@ -1266,250 +1258,204 @@ function getPageSource() {
*/
async function findElementContent(strategy, selector, opts = {}) {
if (!SUPPORTED_STRATEGIES.has(strategy)) {
throw new InvalidSelectorError("Strategy not supported: " + strategy);
}
opts.all = false;
if (opts.startNode) {
- opts.startNode = seenEls.get(opts.startNode);
+ opts.startNode = opts.startNode;
}
let el = await element.find(curContainer, strategy, selector, opts);
- let elRef = seenEls.add(el);
- let webEl = element.makeWebElement(elRef);
- return webEl;
+ return seenEls.add(el);
}
/**
* Find elements in the current browsing context's document using the
* given search strategy.
*/
async function findElementsContent(strategy, selector, opts = {}) {
if (!SUPPORTED_STRATEGIES.has(strategy)) {
throw new InvalidSelectorError("Strategy not supported: " + strategy);
}
opts.all = true;
- if (opts.startNode) {
- opts.startNode = seenEls.get(opts.startNode);
- }
-
let els = await element.find(curContainer, strategy, selector, opts);
- let elRefs = seenEls.addAll(els);
- let webEls = elRefs.map(element.makeWebElement);
+ let webEls = seenEls.addAll(els);
return webEls;
}
/** Find and return the active element on the page. */
function getActiveElement() {
let el = curContainer.frame.document.activeElement;
return evaluate.toJSON(el, seenEls);
}
/**
* Send click event to element.
*
* @param {number} commandID
* ID of the currently handled message between the driver and
* listener.
- * @param {WebElement} id
+ * @param {WebElement} el
* Reference to the web element to click.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being
* finished loading.
*/
function clickElement(msg) {
- let {commandID, id, pageTimeout} = msg.json;
+ let {commandID, webElRef, pageTimeout} = msg.json;
+ let webEl = WebElement.fromJSON(webElRef);
+ let el = seenEls.get(webEl);
try {
let loadEventExpected = true;
- let target = getElementAttribute(id, "target");
+ let target = getElementAttribute(el, "target");
if (target === "_blank") {
loadEventExpected = false;
}
loadListener.navigate(() => {
return interaction.clickElement(
- seenEls.get(id),
+ el,
capabilities.get("moz:accessibilityChecks"),
capabilities.get("moz:webdriverClick")
);
}, commandID, pageTimeout, loadEventExpected, true);
} catch (e) {
sendError(e, commandID);
}
}
-function getElementAttribute(id, name) {
- let el = seenEls.get(id);
+function getElementAttribute(el, name) {
if (element.isBooleanAttribute(el, name)) {
if (el.hasAttribute(name)) {
return "true";
}
return null;
}
return el.getAttribute(name);
}
-function getElementProperty(id, name) {
- let el = seenEls.get(id);
+function getElementProperty(el, name) {
return typeof el[name] != "undefined" ? el[name] : null;
}
/**
- * Get the text of this element. This includes text from child elements.
- *
- * @param {WebElement} id
- * Reference to web element.
- *
- * @return {string}
- * Text of element.
+ * Get the text of this element. This includes text from child
+ * elements.
*/
-function getElementText(id) {
- let el = seenEls.get(id);
+function getElementText(el) {
return atom.getElementText(el, curContainer.frame);
}
/**
* Get the tag name of an element.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {string}
* Tag name of element.
*/
-function getElementTagName(id) {
- let el = seenEls.get(id);
+function getElementTagName(el) {
return el.tagName.toLowerCase();
}
/**
* Determine the element displayedness of the given web element.
*
* Also performs additional accessibility checks if enabled by session
* capability.
*/
-function isElementDisplayed(id) {
- let el = seenEls.get(id);
+function isElementDisplayed(el) {
return interaction.isElementDisplayed(
el, capabilities.get("moz:accessibilityChecks"));
}
/**
* Retrieves the computed value of the given CSS property of the given
* web element.
- *
- * @param {String} id
- * Web element reference.
- * @param {String} prop
- * The CSS property to get.
- *
- * @return {String}
- * Effective value of the requested CSS property.
*/
-function getElementValueOfCssProperty(id, prop) {
- let el = seenEls.get(id);
+function getElementValueOfCssProperty(el, prop) {
let st = curContainer.frame.document.defaultView.getComputedStyle(el);
return st.getPropertyValue(prop);
}
/**
* Get the position and dimensions of the element.
*
- * @param {WebElement} id
- * Reference to web element.
- *
* @return {Object.<string, number>}
* The x, y, width, and height properties of the element.
*/
-function getElementRect(id) {
- let el = seenEls.get(id);
+function getElementRect(el) {
let clientRect = el.getBoundingClientRect();
return {
x: clientRect.x + curContainer.frame.pageXOffset,
y: clientRect.y + curContainer.frame.pageYOffset,
width: clientRect.width,
height: clientRect.height,
};
}
-/**
- * Check if element is enabled.
- *
- * @param {WebElement} id
- * Reference to web element.
- *
- * @return {boolean}
- * True if enabled, false otherwise.
- */
-function isElementEnabled(id) {
- let el = seenEls.get(id);
+function isElementEnabled(el) {
return interaction.isElementEnabled(
el, capabilities.get("moz:accessibilityChecks"));
}
/**
* Determines if the referenced element is selected or not.
*
* This operation only makes sense on input elements of the Checkbox-
* and Radio Button states, or option elements.
*/
-function isElementSelected(id) {
- let el = seenEls.get(id);
+function isElementSelected(el) {
return interaction.isElementSelected(
el, capabilities.get("moz:accessibilityChecks"));
}
-async function sendKeysToElement(id, val) {
- let el = seenEls.get(id);
+async function sendKeysToElement(el, val) {
if (el.type == "file") {
await interaction.uploadFile(el, val);
} else if ((el.type == "date" || el.type == "time") &&
Preferences.get("dom.forms.datetime")) {
interaction.setFormControlValue(el, val);
} else {
await interaction.sendKeysToElement(
el, val, false, capabilities.get("moz:accessibilityChecks"));
}
}
/** Clear the text of an element. */
-function clearElement(id) {
+function clearElement(el) {
try {
- let el = seenEls.get(id);
if (el.type == "file") {
el.value = null;
} else {
atom.clearElement(el, curContainer.frame);
}
} catch (e) {
// Bug 964738: Newer atoms contain status codes which makes wrapping
// this in an error prototype that has a status property unnecessary
if (e.name == "InvalidElementStateError") {
throw new InvalidElementStateError(e.message);
} else {
throw e;
}
}
}
-/**
- * Switch the current context to the specified host's Shadow DOM.
- *
- * @param {WebElement} id
- * Reference to web element.
- */
-function switchToShadowRoot(id) {
- if (!id) {
+/** Switch the current context to the specified host's Shadow DOM. */
+function switchToShadowRoot(el) {
+ if (!el) {
// If no host element is passed, attempt to find a parent shadow
// root or, if none found, unset the current shadow root
if (curContainer.shadowRoot) {
let parent;
try {
parent = curContainer.shadowRoot.host;
} catch (e) {
// There is a chance that host element is dead and we are trying to
@@ -1520,35 +1466,33 @@ function switchToShadowRoot(id) {
while (parent && !(parent instanceof curContainer.frame.ShadowRoot)) {
parent = parent.parentNode;
}
curContainer.shadowRoot = parent;
}
return;
}
- let foundShadowRoot;
- let hostEl = seenEls.get(id);
- foundShadowRoot = hostEl.shadowRoot;
+ let foundShadowRoot = el.shadowRoot;
if (!foundShadowRoot) {
- throw new NoSuchElementError("Unable to locate shadow root: " + id);
+ throw new NoSuchElementError(pprint`Unable to locate shadow root: ${el}`);
}
curContainer.shadowRoot = foundShadowRoot;
}
/**
* Switch to the parent frame of the current frame. If the frame is the
* top most is the current frame then no action will happen.
*/
function switchToParentFrame(msg) {
curContainer.frame = curContainer.frame.parent;
let parentElement = seenEls.add(curContainer.frame);
sendSyncMessage(
- "Marionette:switchedToFrame", {frameValue: parentElement});
+ "Marionette:switchedToFrame", {frameValue: parentElement.uuid});
sendOk(msg.json.commandID);
}
/**
* Switch to frame given either the server-assigned element id,
* its index in window.frames, or the iframe's name or id.
*/
@@ -1584,23 +1528,27 @@ function switchToFrame(msg) {
if (msg.json.focus == true) {
curContainer.frame.focus();
}
sendOk(commandID);
return;
}
- let id = msg.json.element;
- if (seenEls.has(id)) {
+ let webEl;
+ if (typeof msg.json.element != "undefined") {
+ webEl = WebElement.fromUUID(msg.json.element, "content");
+ }
+ if (webEl && seenEls.has(webEl)) {
let wantedFrame;
try {
- wantedFrame = seenEls.get(id);
+ wantedFrame = seenEls.get(webEl);
} catch (e) {
sendError(e, commandID);
+ return;
}
if (frames.length > 0) {
for (let i = 0; i < frames.length; i++) {
// use XPCNativeWrapper to compare elements; see bug 834266
let frameEl = frames[i].frameElement;
let wrappedItem = new XPCNativeWrapper(frameEl);
let wrappedWanted = new XPCNativeWrapper(wantedFrame);
@@ -1625,17 +1573,17 @@ function switchToFrame(msg) {
curContainer.frame = iframes[i];
foundFrame = i;
}
}
}
}
if (foundFrame === null) {
- if (typeof(msg.json.id) === "number") {
+ if (typeof msg.json.id === "number") {
try {
foundFrame = frames[msg.json.id].frameElement;
if (foundFrame !== null) {
curContainer.frame = foundFrame;
foundFrame = seenEls.add(curContainer.frame);
} else {
// If foundFrame is null at this point then we have the top
// level browsing context so should treat it accordingly.
@@ -1667,19 +1615,18 @@ function switchToFrame(msg) {
let failedFrame = msg.json.id || msg.json.element;
let err = new NoSuchFrameError(`Unable to locate frame: ${failedFrame}`);
sendError(err, commandID);
return;
}
// send a synchronous message to let the server update the currently active
// frame element (for getActiveFrame)
- let frameValue = evaluate.toJSON(
- curContainer.frame.wrappedJSObject, seenEls)[element.Key];
- sendSyncMessage("Marionette:switchedToFrame", {"frameValue": frameValue});
+ let frameWebEl = seenEls.add(curContainer.frame.wrappedJSObject);
+ sendSyncMessage("Marionette:switchedToFrame", {"frameValue": frameWebEl.uuid});
if (curContainer.frame.contentWindow === null) {
// The frame we want to switch to is a remote/OOP frame;
// notify our parent to handle the switch
curContainer.frame = content;
let rv = {win: parWindow, frame: foundFrame};
sendResponse(rv, commandID);
@@ -1721,29 +1668,31 @@ function switchToFrame(msg) {
* Base64 encoded string or a SHA-256 hash of the screenshot.
*/
function takeScreenshot(format, opts = {}) {
let id = opts.id;
let full = !!opts.full;
let highlights = opts.highlights || [];
let scroll = !!opts.scroll;
- let highlightEls = highlights.map(ref => seenEls.get(ref));
-
let canvas;
+ let highlightEls = highlights
+ .map(ref => WebElement.fromUUID(ref, "content"))
+ .map(webEl => seenEls.get(webEl));
// viewport
if (!id && !full) {
canvas = capture.viewport(curContainer.frame, highlightEls);
// element or full document element
} else {
let el;
if (id) {
- el = seenEls.get(id);
+ let webEl = WebElement.fromUUID(id, "content");
+ el = seenEls.get(webEl);
if (scroll) {
element.scrollIntoView(el);
}
} else {
el = curContainer.frame.document.documentElement;
}
canvas = capture.element(el, highlightEls);