1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
9 dom: "chrome://remote/content/shared/DOM.sys.mjs",
10 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
11 generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
12 pprint: "chrome://remote/content/shared/Format.sys.mjs",
16 * A web reference is an abstraction used to identify an element when
17 * it is transported via the protocol, between remote- and local ends.
19 * In Marionette this abstraction can represent DOM elements,
20 * WindowProxies, and XUL elements.
22 export class WebReference {
24 * @param {string} uuid
25 * Identifier that must be unique across all browsing contexts
26 * for the contract to be upheld.
29 this.uuid = lazy.assert.string(
31 lazy.pprint`Expected "uuid" to be a string, got ${uuid}`
36 * Performs an equality check between this web element and
39 * @param {WebReference} other
40 * Web element to compare with this.
43 * True if this and <var>other</var> are the same. False
47 return other instanceof WebReference && this.uuid === other.uuid;
51 return `[object ${this.constructor.name} uuid=${this.uuid}]`;
55 * Returns a new {@link WebReference} reference for a DOM or XUL element,
56 * <code>WindowProxy</code>, or <code>ShadowRoot</code>.
58 * @param {(Element|ShadowRoot|WindowProxy|MockXULElement)} node
59 * Node to construct a web element reference for.
60 * @param {string=} uuid
61 * Optional unique identifier of the WebReference if already known.
62 * If not defined a new unique identifier will be created.
64 * @returns {WebReference}
65 * Web reference for <var>node</var>.
67 * @throws {InvalidArgumentError}
68 * If <var>node</var> is neither a <code>WindowProxy</code>,
69 * DOM or XUL element, or <code>ShadowRoot</code>.
71 static from(node, uuid) {
72 if (uuid === undefined) {
73 uuid = lazy.generateUUID();
76 if (lazy.dom.isShadowRoot(node) && !lazy.dom.isInPrivilegedDocument(node)) {
77 // When we support Chrome Shadowroots we will need to
78 // do a check here of shadowroot.host being in a privileged document
80 return new ShadowRoot(uuid);
81 } else if (lazy.dom.isElement(node)) {
82 return new WebElement(uuid);
83 } else if (lazy.dom.isDOMWindow(node)) {
84 if (node.parent === node) {
85 return new WebWindow(uuid);
87 return new WebFrame(uuid);
90 throw new lazy.error.InvalidArgumentError(
91 "Expected DOM window/element " + lazy.pprint`or XUL element, got: ${node}`
96 * Unmarshals a JSON Object to one of {@link ShadowRoot}, {@link WebElement},
97 * {@link WebFrame}, or {@link WebWindow}.
99 * @param {Record<string, string>} json
100 * Web reference, which is supposed to be a JSON Object
101 * where the key is one of the {@link WebReference} concrete
102 * classes' UUID identifiers.
104 * @returns {WebReference}
105 * Web reference for the JSON object.
107 * @throws {InvalidArgumentError}
108 * If <var>json</var> is not a web reference.
110 static fromJSON(json) {
113 lazy.pprint`Expected web reference to be an object, got ${json}`
115 if (json instanceof WebReference) {
118 let keys = Object.keys(json);
120 for (let key of keys) {
122 case ShadowRoot.Identifier:
123 return ShadowRoot.fromJSON(json);
125 case WebElement.Identifier:
126 return WebElement.fromJSON(json);
128 case WebFrame.Identifier:
129 return WebFrame.fromJSON(json);
131 case WebWindow.Identifier:
132 return WebWindow.fromJSON(json);
136 throw new lazy.error.InvalidArgumentError(
137 lazy.pprint`Expected web reference, got: ${json}`
142 * Checks if <var>obj<var> is a {@link WebReference} reference.
144 * @param {Record<string, string>} obj
145 * Object that represents a {@link WebReference}.
148 * True if <var>obj</var> is a {@link WebReference}, false otherwise.
150 static isReference(obj) {
151 if (Object.prototype.toString.call(obj) != "[object Object]") {
156 ShadowRoot.Identifier in obj ||
157 WebElement.Identifier in obj ||
158 WebFrame.Identifier in obj ||
159 WebWindow.Identifier in obj
168 * Shadow Root elements are represented as shadow root references when they are
169 * transported over the wire protocol
171 export class ShadowRoot extends WebReference {
173 return { [ShadowRoot.Identifier]: this.uuid };
176 static fromJSON(json) {
177 const { Identifier } = ShadowRoot;
179 if (!(Identifier in json)) {
180 throw new lazy.error.InvalidArgumentError(
181 lazy.pprint`Expected shadow root reference, got: ${json}`
185 let uuid = json[Identifier];
186 return new ShadowRoot(uuid);
190 * Constructs a {@link ShadowRoot} from a string <var>uuid</var>.
192 * This whole function is a workaround for the fact that clients
193 * to Marionette occasionally pass <code>{id: <uuid>}</code> JSON
194 * Objects instead of shadow root representations.
196 * @param {string} uuid
197 * UUID to be associated with the web reference.
199 * @returns {ShadowRoot}
200 * The shadow root reference.
202 * @throws {InvalidArgumentError}
203 * If <var>uuid</var> is not a string.
205 static fromUUID(uuid) {
208 lazy.pprint`Expected "uuid" to be a string, got: ${uuid}`
211 return new ShadowRoot(uuid);
215 ShadowRoot.Identifier = "shadow-6066-11e4-a52e-4f735466cecf";
218 * DOM elements are represented as web elements when they are
219 * transported over the wire protocol.
221 export class WebElement extends WebReference {
223 return { [WebElement.Identifier]: this.uuid };
226 static fromJSON(json) {
227 const { Identifier } = WebElement;
229 if (!(Identifier in json)) {
230 throw new lazy.error.InvalidArgumentError(
231 lazy.pprint`Expected web element reference, got: ${json}`
235 let uuid = json[Identifier];
236 return new WebElement(uuid);
240 * Constructs a {@link WebElement} from a string <var>uuid</var>.
242 * This whole function is a workaround for the fact that clients
243 * to Marionette occasionally pass <code>{id: <uuid>}</code> JSON
244 * Objects instead of web element representations.
246 * @param {string} uuid
247 * UUID to be associated with the web reference.
249 * @returns {WebElement}
250 * The web element reference.
252 * @throws {InvalidArgumentError}
253 * If <var>uuid</var> is not a string.
255 static fromUUID(uuid) {
256 return new WebElement(uuid);
260 WebElement.Identifier = "element-6066-11e4-a52e-4f735466cecf";
263 * Nested browsing contexts, such as the <code>WindowProxy</code>
264 * associated with <tt><frame></tt> and <tt><iframe></tt>,
265 * are represented as web frames over the wire protocol.
267 export class WebFrame extends WebReference {
269 return { [WebFrame.Identifier]: this.uuid };
272 static fromJSON(json) {
273 if (!(WebFrame.Identifier in json)) {
274 throw new lazy.error.InvalidArgumentError(
275 lazy.pprint`Expected web frame reference, got: ${json}`
278 let uuid = json[WebFrame.Identifier];
279 return new WebFrame(uuid);
283 WebFrame.Identifier = "frame-075b-4da1-b6ba-e579c2d3230a";
286 * Top-level browsing contexts, such as <code>WindowProxy</code>
287 * whose <code>opener</code> is null, are represented as web windows
288 * over the wire protocol.
290 export class WebWindow extends WebReference {
292 return { [WebWindow.Identifier]: this.uuid };
295 static fromJSON(json) {
296 if (!(WebWindow.Identifier in json)) {
297 throw new lazy.error.InvalidArgumentError(
298 lazy.pprint`Expected web window reference, got: ${json}`
301 let uuid = json[WebWindow.Identifier];
302 return new WebWindow(uuid);
306 WebWindow.Identifier = "window-fcc6-11e5-b4f8-330a88ab9d7f";