summaryrefslogtreecommitdiffstats
path: root/remote/marionette/web-reference.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/marionette/web-reference.sys.mjs')
-rw-r--r--remote/marionette/web-reference.sys.mjs297
1 files changed, 297 insertions, 0 deletions
diff --git a/remote/marionette/web-reference.sys.mjs b/remote/marionette/web-reference.sys.mjs
new file mode 100644
index 0000000000..5d2d510265
--- /dev/null
+++ b/remote/marionette/web-reference.sys.mjs
@@ -0,0 +1,297 @@
+/* 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/. */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
+ dom: "chrome://remote/content/shared/DOM.sys.mjs",
+ error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
+ generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
+ pprint: "chrome://remote/content/shared/Format.sys.mjs",
+});
+
+/**
+ * A web reference is an abstraction used to identify an element when
+ * it is transported via the protocol, between remote- and local ends.
+ *
+ * In Marionette this abstraction can represent DOM elements,
+ * WindowProxies, and XUL elements.
+ */
+export class WebReference {
+ /**
+ * @param {string} uuid
+ * Identifier that must be unique across all browsing contexts
+ * for the contract to be upheld.
+ */
+ constructor(uuid) {
+ this.uuid = lazy.assert.string(uuid);
+ }
+
+ /**
+ * Performs an equality check between this web element and
+ * <var>other</var>.
+ *
+ * @param {WebReference} other
+ * Web element to compare with this.
+ *
+ * @returns {boolean}
+ * True if this and <var>other</var> are the same. False
+ * otherwise.
+ */
+ is(other) {
+ return other instanceof WebReference && this.uuid === other.uuid;
+ }
+
+ toString() {
+ return `[object ${this.constructor.name} uuid=${this.uuid}]`;
+ }
+
+ /**
+ * Returns a new {@link WebReference} reference for a DOM or XUL element,
+ * <code>WindowProxy</code>, or <code>ShadowRoot</code>.
+ *
+ * @param {(Element|ShadowRoot|WindowProxy|MockXULElement)} node
+ * Node to construct a web element reference for.
+ * @param {string=} uuid
+ * Optional unique identifier of the WebReference if already known.
+ * If not defined a new unique identifier will be created.
+ *
+ * @returns {WebReference}
+ * Web reference for <var>node</var>.
+ *
+ * @throws {InvalidArgumentError}
+ * If <var>node</var> is neither a <code>WindowProxy</code>,
+ * DOM or XUL element, or <code>ShadowRoot</code>.
+ */
+ static from(node, uuid) {
+ if (uuid === undefined) {
+ uuid = lazy.generateUUID();
+ }
+
+ if (lazy.dom.isShadowRoot(node) && !lazy.dom.isInPrivilegedDocument(node)) {
+ // When we support Chrome Shadowroots we will need to
+ // do a check here of shadowroot.host being in a privileged document
+ // See Bug 1743541
+ return new ShadowRoot(uuid);
+ } else if (lazy.dom.isElement(node)) {
+ return new WebElement(uuid);
+ } else if (lazy.dom.isDOMWindow(node)) {
+ if (node.parent === node) {
+ return new WebWindow(uuid);
+ }
+ return new WebFrame(uuid);
+ }
+
+ throw new lazy.error.InvalidArgumentError(
+ "Expected DOM window/element " + lazy.pprint`or XUL element, got: ${node}`
+ );
+ }
+
+ /**
+ * Unmarshals a JSON Object to one of {@link ShadowRoot}, {@link WebElement},
+ * {@link WebFrame}, or {@link WebWindow}.
+ *
+ * @param {Object<string, string>} json
+ * Web reference, which is supposed to be a JSON Object
+ * where the key is one of the {@link WebReference} concrete
+ * classes' UUID identifiers.
+ *
+ * @returns {WebReference}
+ * Web reference for the JSON object.
+ *
+ * @throws {InvalidArgumentError}
+ * If <var>json</var> is not a web reference.
+ */
+ static fromJSON(json) {
+ lazy.assert.object(json);
+ if (json instanceof WebReference) {
+ return json;
+ }
+ let keys = Object.keys(json);
+
+ for (let key of keys) {
+ switch (key) {
+ case ShadowRoot.Identifier:
+ return ShadowRoot.fromJSON(json);
+
+ case WebElement.Identifier:
+ return WebElement.fromJSON(json);
+
+ case WebFrame.Identifier:
+ return WebFrame.fromJSON(json);
+
+ case WebWindow.Identifier:
+ return WebWindow.fromJSON(json);
+ }
+ }
+
+ throw new lazy.error.InvalidArgumentError(
+ lazy.pprint`Expected web reference, got: ${json}`
+ );
+ }
+
+ /**
+ * Checks if <var>obj<var> is a {@link WebReference} reference.
+ *
+ * @param {Object<string, string>} obj
+ * Object that represents a {@link WebReference}.
+ *
+ * @returns {boolean}
+ * True if <var>obj</var> is a {@link WebReference}, false otherwise.
+ */
+ static isReference(obj) {
+ if (Object.prototype.toString.call(obj) != "[object Object]") {
+ return false;
+ }
+
+ if (
+ ShadowRoot.Identifier in obj ||
+ WebElement.Identifier in obj ||
+ WebFrame.Identifier in obj ||
+ WebWindow.Identifier in obj
+ ) {
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Shadow Root elements are represented as shadow root references when they are
+ * transported over the wire protocol
+ */
+export class ShadowRoot extends WebReference {
+ toJSON() {
+ return { [ShadowRoot.Identifier]: this.uuid };
+ }
+
+ static fromJSON(json) {
+ const { Identifier } = ShadowRoot;
+
+ if (!(Identifier in json)) {
+ throw new lazy.error.InvalidArgumentError(
+ lazy.pprint`Expected shadow root reference, got: ${json}`
+ );
+ }
+
+ let uuid = json[Identifier];
+ return new ShadowRoot(uuid);
+ }
+
+ /**
+ * Constructs a {@link ShadowRoot} from a string <var>uuid</var>.
+ *
+ * This whole function is a workaround for the fact that clients
+ * to Marionette occasionally pass <code>{id: <uuid>}</code> JSON
+ * Objects instead of shadow root representations.
+ *
+ * @param {string} uuid
+ * UUID to be associated with the web reference.
+ *
+ * @returns {ShadowRoot}
+ * The shadow root reference.
+ *
+ * @throws {InvalidArgumentError}
+ * If <var>uuid</var> is not a string.
+ */
+ static fromUUID(uuid) {
+ lazy.assert.string(uuid);
+
+ return new ShadowRoot(uuid);
+ }
+}
+
+ShadowRoot.Identifier = "shadow-6066-11e4-a52e-4f735466cecf";
+
+/**
+ * DOM elements are represented as web elements when they are
+ * transported over the wire protocol.
+ */
+export class WebElement extends WebReference {
+ toJSON() {
+ return { [WebElement.Identifier]: this.uuid };
+ }
+
+ static fromJSON(json) {
+ const { Identifier } = WebElement;
+
+ if (!(Identifier in json)) {
+ throw new lazy.error.InvalidArgumentError(
+ lazy.pprint`Expected web element reference, got: ${json}`
+ );
+ }
+
+ let uuid = json[Identifier];
+ return new WebElement(uuid);
+ }
+
+ /**
+ * Constructs a {@link WebElement} from a string <var>uuid</var>.
+ *
+ * This whole function is a workaround for the fact that clients
+ * to Marionette occasionally pass <code>{id: <uuid>}</code> JSON
+ * Objects instead of web element representations.
+ *
+ * @param {string} uuid
+ * UUID to be associated with the web reference.
+ *
+ * @returns {WebElement}
+ * The web element reference.
+ *
+ * @throws {InvalidArgumentError}
+ * If <var>uuid</var> is not a string.
+ */
+ static fromUUID(uuid) {
+ return new WebElement(uuid);
+ }
+}
+
+WebElement.Identifier = "element-6066-11e4-a52e-4f735466cecf";
+
+/**
+ * Nested browsing contexts, such as the <code>WindowProxy</code>
+ * associated with <tt>&lt;frame&gt;</tt> and <tt>&lt;iframe&gt;</tt>,
+ * are represented as web frames over the wire protocol.
+ */
+export class WebFrame extends WebReference {
+ toJSON() {
+ return { [WebFrame.Identifier]: this.uuid };
+ }
+
+ static fromJSON(json) {
+ if (!(WebFrame.Identifier in json)) {
+ throw new lazy.error.InvalidArgumentError(
+ lazy.pprint`Expected web frame reference, got: ${json}`
+ );
+ }
+ let uuid = json[WebFrame.Identifier];
+ return new WebFrame(uuid);
+ }
+}
+
+WebFrame.Identifier = "frame-075b-4da1-b6ba-e579c2d3230a";
+
+/**
+ * Top-level browsing contexts, such as <code>WindowProxy</code>
+ * whose <code>opener</code> is null, are represented as web windows
+ * over the wire protocol.
+ */
+export class WebWindow extends WebReference {
+ toJSON() {
+ return { [WebWindow.Identifier]: this.uuid };
+ }
+
+ static fromJSON(json) {
+ if (!(WebWindow.Identifier in json)) {
+ throw new lazy.error.InvalidArgumentError(
+ lazy.pprint`Expected web window reference, got: ${json}`
+ );
+ }
+ let uuid = json[WebWindow.Identifier];
+ return new WebWindow(uuid);
+ }
+}
+
+WebWindow.Identifier = "window-fcc6-11e5-b4f8-330a88ab9d7f";