Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / remote / marionette / web-reference.sys.mjs
blob25b63bb96516f1d041529824c8586fe24e248431
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/. */
5 const lazy = {};
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",
13 });
15 /**
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.
18  *
19  * In Marionette this abstraction can represent DOM elements,
20  * WindowProxies, and XUL elements.
21  */
22 export class WebReference {
23   /**
24    * @param {string} uuid
25    *     Identifier that must be unique across all browsing contexts
26    *     for the contract to be upheld.
27    */
28   constructor(uuid) {
29     this.uuid = lazy.assert.string(
30       uuid,
31       lazy.pprint`Expected "uuid" to be a string, got ${uuid}`
32     );
33   }
35   /**
36    * Performs an equality check between this web element and
37    * <var>other</var>.
38    *
39    * @param {WebReference} other
40    *     Web element to compare with this.
41    *
42    * @returns {boolean}
43    *     True if this and <var>other</var> are the same.  False
44    *     otherwise.
45    */
46   is(other) {
47     return other instanceof WebReference && this.uuid === other.uuid;
48   }
50   toString() {
51     return `[object ${this.constructor.name} uuid=${this.uuid}]`;
52   }
54   /**
55    * Returns a new {@link WebReference} reference for a DOM or XUL element,
56    * <code>WindowProxy</code>, or <code>ShadowRoot</code>.
57    *
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.
63    *
64    * @returns {WebReference}
65    *     Web reference for <var>node</var>.
66    *
67    * @throws {InvalidArgumentError}
68    *     If <var>node</var> is neither a <code>WindowProxy</code>,
69    *     DOM or XUL element, or <code>ShadowRoot</code>.
70    */
71   static from(node, uuid) {
72     if (uuid === undefined) {
73       uuid = lazy.generateUUID();
74     }
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
79       // See Bug 1743541
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);
86       }
87       return new WebFrame(uuid);
88     }
90     throw new lazy.error.InvalidArgumentError(
91       "Expected DOM window/element " + lazy.pprint`or XUL element, got: ${node}`
92     );
93   }
95   /**
96    * Unmarshals a JSON Object to one of {@link ShadowRoot}, {@link WebElement},
97    * {@link WebFrame}, or {@link WebWindow}.
98    *
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.
103    *
104    * @returns {WebReference}
105    *     Web reference for the JSON object.
106    *
107    * @throws {InvalidArgumentError}
108    *     If <var>json</var> is not a web reference.
109    */
110   static fromJSON(json) {
111     lazy.assert.object(
112       json,
113       lazy.pprint`Expected web reference to be an object, got ${json}`
114     );
115     if (json instanceof WebReference) {
116       return json;
117     }
118     let keys = Object.keys(json);
120     for (let key of keys) {
121       switch (key) {
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);
133       }
134     }
136     throw new lazy.error.InvalidArgumentError(
137       lazy.pprint`Expected web reference, got: ${json}`
138     );
139   }
141   /**
142    * Checks if <var>obj<var> is a {@link WebReference} reference.
143    *
144    * @param {Record<string, string>} obj
145    *     Object that represents a {@link WebReference}.
146    *
147    * @returns {boolean}
148    *     True if <var>obj</var> is a {@link WebReference}, false otherwise.
149    */
150   static isReference(obj) {
151     if (Object.prototype.toString.call(obj) != "[object Object]") {
152       return false;
153     }
155     if (
156       ShadowRoot.Identifier in obj ||
157       WebElement.Identifier in obj ||
158       WebFrame.Identifier in obj ||
159       WebWindow.Identifier in obj
160     ) {
161       return true;
162     }
163     return false;
164   }
168  * Shadow Root elements are represented as shadow root references when they are
169  * transported over the wire protocol
170  */
171 export class ShadowRoot extends WebReference {
172   toJSON() {
173     return { [ShadowRoot.Identifier]: this.uuid };
174   }
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}`
182       );
183     }
185     let uuid = json[Identifier];
186     return new ShadowRoot(uuid);
187   }
189   /**
190    * Constructs a {@link ShadowRoot} from a string <var>uuid</var>.
191    *
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.
195    *
196    * @param {string} uuid
197    *     UUID to be associated with the web reference.
198    *
199    * @returns {ShadowRoot}
200    *     The shadow root reference.
201    *
202    * @throws {InvalidArgumentError}
203    *     If <var>uuid</var> is not a string.
204    */
205   static fromUUID(uuid) {
206     lazy.assert.string(
207       uuid,
208       lazy.pprint`Expected "uuid" to be a string, got: ${uuid}`
209     );
211     return new ShadowRoot(uuid);
212   }
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.
220  */
221 export class WebElement extends WebReference {
222   toJSON() {
223     return { [WebElement.Identifier]: this.uuid };
224   }
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}`
232       );
233     }
235     let uuid = json[Identifier];
236     return new WebElement(uuid);
237   }
239   /**
240    * Constructs a {@link WebElement} from a string <var>uuid</var>.
241    *
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.
245    *
246    * @param {string} uuid
247    *     UUID to be associated with the web reference.
248    *
249    * @returns {WebElement}
250    *     The web element reference.
251    *
252    * @throws {InvalidArgumentError}
253    *     If <var>uuid</var> is not a string.
254    */
255   static fromUUID(uuid) {
256     return new WebElement(uuid);
257   }
260 WebElement.Identifier = "element-6066-11e4-a52e-4f735466cecf";
263  * Nested browsing contexts, such as the <code>WindowProxy</code>
264  * associated with <tt>&lt;frame&gt;</tt> and <tt>&lt;iframe&gt;</tt>,
265  * are represented as web frames over the wire protocol.
266  */
267 export class WebFrame extends WebReference {
268   toJSON() {
269     return { [WebFrame.Identifier]: this.uuid };
270   }
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}`
276       );
277     }
278     let uuid = json[WebFrame.Identifier];
279     return new WebFrame(uuid);
280   }
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.
289  */
290 export class WebWindow extends WebReference {
291   toJSON() {
292     return { [WebWindow.Identifier]: this.uuid };
293   }
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}`
299       );
300     }
301     let uuid = json[WebWindow.Identifier];
302     return new WebWindow(uuid);
303   }
306 WebWindow.Identifier = "window-fcc6-11e5-b4f8-330a88ab9d7f";