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 Addon: "chrome://remote/content/marionette/addon.sys.mjs",
9 AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
10 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
11 atom: "chrome://remote/content/marionette/atom.sys.mjs",
12 browser: "chrome://remote/content/marionette/browser.sys.mjs",
13 capture: "chrome://remote/content/shared/Capture.sys.mjs",
14 Context: "chrome://remote/content/marionette/browser.sys.mjs",
15 cookie: "chrome://remote/content/marionette/cookie.sys.mjs",
16 DebounceCallback: "chrome://remote/content/marionette/sync.sys.mjs",
18 "chrome://remote/content/marionette/actors/MarionetteEventsParent.sys.mjs",
19 dom: "chrome://remote/content/shared/DOM.sys.mjs",
21 "chrome://remote/content/marionette/actors/MarionetteEventsParent.sys.mjs",
22 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
23 EventPromise: "chrome://remote/content/shared/Sync.sys.mjs",
24 getMarionetteCommandsActorProxy:
25 "chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
26 IdlePromise: "chrome://remote/content/marionette/sync.sys.mjs",
27 l10n: "chrome://remote/content/marionette/l10n.sys.mjs",
28 Log: "chrome://remote/content/shared/Log.sys.mjs",
29 Marionette: "chrome://remote/content/components/Marionette.sys.mjs",
30 MarionettePrefs: "chrome://remote/content/marionette/prefs.sys.mjs",
31 modal: "chrome://remote/content/shared/Prompt.sys.mjs",
32 navigate: "chrome://remote/content/marionette/navigate.sys.mjs",
33 permissions: "chrome://remote/content/marionette/permissions.sys.mjs",
34 pprint: "chrome://remote/content/shared/Format.sys.mjs",
35 print: "chrome://remote/content/shared/PDF.sys.mjs",
37 "chrome://remote/content/shared/listeners/PromptListener.sys.mjs",
38 quit: "chrome://remote/content/shared/Browser.sys.mjs",
39 reftest: "chrome://remote/content/marionette/reftest.sys.mjs",
40 registerCommandsActor:
41 "chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
42 RemoteAgent: "chrome://remote/content/components/RemoteAgent.sys.mjs",
43 ShadowRoot: "chrome://remote/content/marionette/web-reference.sys.mjs",
44 TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
45 TimedPromise: "chrome://remote/content/marionette/sync.sys.mjs",
46 Timeouts: "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs",
47 UnhandledPromptBehavior:
48 "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs",
49 unregisterCommandsActor:
50 "chrome://remote/content/marionette/actors/MarionetteCommandsParent.sys.mjs",
51 waitForInitialNavigationCompleted:
52 "chrome://remote/content/shared/Navigate.sys.mjs",
53 webauthn: "chrome://remote/content/marionette/webauthn.sys.mjs",
54 WebDriverSession: "chrome://remote/content/shared/webdriver/Session.sys.mjs",
55 WebElement: "chrome://remote/content/marionette/web-reference.sys.mjs",
56 windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs",
57 WindowState: "chrome://remote/content/marionette/browser.sys.mjs",
60 ChromeUtils.defineLazyGetter(lazy, "logger", () =>
61 lazy.Log.get(lazy.Log.TYPES.MARIONETTE)
64 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
66 ChromeUtils.defineLazyGetter(
68 "supportedStrategies",
71 lazy.dom.Strategy.ClassName,
72 lazy.dom.Strategy.Selector,
74 lazy.dom.Strategy.Name,
75 lazy.dom.Strategy.LinkText,
76 lazy.dom.Strategy.PartialLinkText,
77 lazy.dom.Strategy.TagName,
78 lazy.dom.Strategy.XPath,
82 // Timeout used to abort fullscreen, maximize, and minimize
83 // commands if no window manager is present.
84 const TIMEOUT_NO_WINDOW_MANAGER = 5000;
86 // Observer topic to wait for until the browser window is ready.
87 const TOPIC_BROWSER_READY = "browser-delayed-startup-finished";
88 // Observer topic to perform clean up when application quit is requested.
89 const TOPIC_QUIT_APPLICATION_REQUESTED = "quit-application-requested";
92 * The Marionette WebDriver services provides a standard conforming
93 * implementation of the W3C WebDriver specification.
95 * @see {@link https://w3c.github.io/webdriver/webdriver-spec.html}
100 * Implements (parts of) the W3C WebDriver protocol. GeckoDriver lives
101 * in chrome space and mediates calls to the current browsing context's actor.
103 * Throughout this prototype, functions with the argument <var>cmd</var>'s
104 * documentation refers to the contents of the <code>cmd.parameter</code>
109 * @param {MarionetteServer} server
110 * The instance of Marionette server.
112 export function GeckoDriver(server) {
113 this._server = server;
116 this._currentSession = null;
120 // points to current browser
121 this.curBrowser = null;
122 // top-most chrome window
123 this.mainFrame = null;
125 // Use content context by default
126 this.context = lazy.Context.Content;
128 // used for modal dialogs
130 this.promptListener = null;
134 * The current context decides if commands are executed in chrome- or
137 Object.defineProperty(GeckoDriver.prototype, "context", {
139 return this._context;
143 this._context = lazy.Context.fromString(context);
148 * The current WebDriver Session.
150 Object.defineProperty(GeckoDriver.prototype, "currentSession", {
152 if (lazy.RemoteAgent.webDriverBiDi) {
153 return lazy.RemoteAgent.webDriverBiDi.session;
156 return this._currentSession;
161 * Returns the current URL of the ChromeWindow or content browser,
162 * depending on context.
165 * Read-only property containing the currently loaded URL.
167 Object.defineProperty(GeckoDriver.prototype, "currentURL", {
169 const browsingContext = this.getBrowsingContext({ top: true });
170 return new URL(browsingContext.currentWindowGlobal.documentURI.spec);
175 * Returns the title of the ChromeWindow or content browser,
176 * depending on context.
179 * Read-only property containing the title of the loaded URL.
181 Object.defineProperty(GeckoDriver.prototype, "title", {
183 const browsingContext = this.getBrowsingContext({ top: true });
184 return browsingContext.currentWindowGlobal.documentTitle;
188 Object.defineProperty(GeckoDriver.prototype, "windowType", {
190 return this.curBrowser.window.document.documentElement.getAttribute(
196 GeckoDriver.prototype.QueryInterface = ChromeUtils.generateQI([
198 "nsISupportsWeakReference",
202 * Callback used to observe the closing of modal dialogs
203 * during the session's lifetime.
205 GeckoDriver.prototype.handleClosedModalDialog = function () {
210 * Callback used to observe the creation of new modal dialogs
211 * during the session's lifetime.
213 GeckoDriver.prototype.handleOpenModalDialog = function (eventName, data) {
214 this.dialog = data.prompt;
215 this.getActor().notifyDialogOpened();
219 * Get the current visible URL.
221 GeckoDriver.prototype._getCurrentURL = function () {
222 const browsingContext = this.getBrowsingContext({ top: true });
223 return new URL(browsingContext.currentURI.spec);
227 * Get the current "MarionetteCommands" parent actor.
229 * @param {object} options
230 * @param {boolean=} options.top
231 * If set to true use the window's top-level browsing context for the actor,
232 * otherwise the one from the currently selected frame. Defaults to false.
234 * @returns {MarionetteCommandsParent}
237 GeckoDriver.prototype.getActor = function (options = {}) {
238 return lazy.getMarionetteCommandsActorProxy(() =>
239 this.getBrowsingContext(options)
244 * Get the selected BrowsingContext for the current context.
246 * @param {object} options
247 * @param {Context=} options.context
248 * Context (content or chrome) for which to retrieve the browsing context.
249 * Defaults to the current one.
250 * @param {boolean=} options.parent
251 * If set to true return the window's parent browsing context,
252 * otherwise the one from the currently selected frame. Defaults to false.
253 * @param {boolean=} options.top
254 * If set to true return the window's top-level browsing context,
255 * otherwise the one from the currently selected frame. Defaults to false.
257 * @returns {BrowsingContext}
258 * The browsing context, or `null` if none is available
260 GeckoDriver.prototype.getBrowsingContext = function (options = {}) {
261 const { context = this.context, parent = false, top = false } = options;
263 let browsingContext = null;
264 if (context === lazy.Context.Chrome) {
265 browsingContext = this.currentSession?.chromeBrowsingContext;
267 browsingContext = this.currentSession?.contentBrowsingContext;
270 if (browsingContext && parent) {
271 browsingContext = browsingContext.parent;
274 if (browsingContext && top) {
275 browsingContext = browsingContext.top;
278 return browsingContext;
282 * Get the currently selected window.
284 * It will return the outer {@link ChromeWindow} previously selected by
285 * window handle through {@link #switchToWindow}, or the first window that
288 * @param {object} options
289 * @param {Context=} options.context
290 * Optional name of the context to use for finding the window.
291 * It will be required if a command always needs a specific context,
292 * whether which context is currently set. Defaults to the current
295 * @returns {ChromeWindow}
296 * The current top-level browsing context.
298 GeckoDriver.prototype.getCurrentWindow = function (options = {}) {
299 const { context = this.context } = options;
303 case lazy.Context.Chrome:
304 if (this.curBrowser) {
305 win = this.curBrowser.window;
309 case lazy.Context.Content:
310 if (this.curBrowser && this.curBrowser.contentBrowser) {
311 win = this.curBrowser.window;
319 GeckoDriver.prototype.isReftestBrowser = function (element) {
323 element.tagName === "xul:browser" &&
324 element.parentElement &&
325 element.parentElement.id === "reftest"
330 * Create a new browsing context for window and add to known browsers.
332 * @param {ChromeWindow} win
333 * Window for which we will create a browsing context.
336 * Returns the unique server-assigned ID of the window.
338 GeckoDriver.prototype.addBrowser = function (win) {
339 let context = new lazy.browser.Context(win, this);
340 let winId = lazy.windowManager.getIdForWindow(win);
342 this.browsers[winId] = context;
343 this.curBrowser = this.browsers[winId];
347 * Recursively get all labeled text.
349 * @param {Element} el
350 * The parent element.
351 * @param {Array.<string>} lines
352 * Array that holds the text lines.
354 GeckoDriver.prototype.getVisibleText = function (el, lines) {
356 if (lazy.atom.isElementDisplayed(el, this.getCurrentWindow())) {
358 lines.push(el.value);
360 for (let child in el.childNodes) {
361 this.getVisibleText(el.childNodes[child], lines);
365 if (el.nodeName == "#text") {
366 lines.push(el.textContent);
372 * Handles registration of new content browsers. Depending on
373 * their type they are either accepted or ignored.
375 * @param {XULBrowser} browserElement
377 GeckoDriver.prototype.registerBrowser = function (browserElement) {
378 // We want to ignore frames that are XUL browsers that aren't in the "main"
379 // tabbrowser, but accept things on Fennec (which doesn't have a
380 // xul:tabbrowser), and accept HTML iframes (because tests depend on it),
381 // as well as XUL frames. Ideally this should be cleaned up and we should
382 // keep track of browsers a different way.
384 !lazy.AppInfo.isFirefox ||
385 browserElement.namespaceURI != XUL_NS ||
386 browserElement.nodeName != "browser" ||
387 browserElement.getTabBrowser()
389 this.curBrowser.register(browserElement);
394 * Create a new WebDriver session.
396 * @param {object} cmd
397 * @param {Object<string, *>=} cmd.parameters
398 * JSON Object containing any of the recognised capabilities as listed
399 * on the `WebDriverSession` class.
402 * Session ID and capabilities offered by the WebDriver service.
404 * @throws {SessionNotCreatedError}
405 * If, for whatever reason, a session could not be created.
407 GeckoDriver.prototype.newSession = async function (cmd) {
408 if (this.currentSession) {
409 throw new lazy.error.SessionNotCreatedError(
410 "Maximum number of active sessions"
414 const { parameters: capabilities } = cmd;
417 // If the WebDriver BiDi protocol is active always use the Remote Agent
418 // to handle the WebDriver session. If it's not the case then Marionette
419 // itself needs to handle it, and has to nullify the "webSocketUrl"
421 if (lazy.RemoteAgent.webDriverBiDi) {
422 await lazy.RemoteAgent.webDriverBiDi.createSession(capabilities);
424 this._currentSession = new lazy.WebDriverSession(capabilities);
425 this._currentSession.capabilities.delete("webSocketUrl");
428 // Don't wait for the initial window when Marionette is in windowless mode
429 if (!this.currentSession.capabilities.get("moz:windowless")) {
430 // Creating a WebDriver session too early can cause issues with
431 // clients in not being able to find any available window handle.
432 // Also when closing the application while it's still starting up can
433 // cause shutdown hangs. As such Marionette will return a new session
434 // once the initial application window has finished initializing.
435 lazy.logger.debug(`Waiting for initial application window`);
436 await lazy.Marionette.browserStartupFinished;
439 await lazy.windowManager.waitForInitialApplicationWindowLoaded();
441 if (lazy.MarionettePrefs.clickToStart) {
442 Services.prompt.alert(
445 "Click to start execution of marionette tests"
449 this.addBrowser(appWin);
450 this.mainFrame = appWin;
452 // Setup observer for modal dialogs
453 this.promptListener = new lazy.PromptListener(() => this.curBrowser);
454 this.promptListener.on("closed", this.handleClosedModalDialog.bind(this));
455 this.promptListener.on("opened", this.handleOpenModalDialog.bind(this));
456 this.promptListener.startListening();
458 for (let win of lazy.windowManager.windows) {
459 this.registerWindow(win, { registerBrowsers: true });
462 if (this.mainFrame) {
463 this.currentSession.chromeBrowsingContext =
464 this.mainFrame.browsingContext;
465 this.mainFrame.focus();
468 if (this.curBrowser.tab) {
469 const browsingContext = this.curBrowser.contentBrowser.browsingContext;
470 this.currentSession.contentBrowsingContext = browsingContext;
472 // Bug 1838381 - Only use a longer unload timeout for desktop, because
473 // on Android only the initial document is loaded, and loading a
474 // specific page during startup doesn't succeed.
476 if (!lazy.AppInfo.isAndroid) {
477 options.unloadTimeout = 5000;
480 await lazy.waitForInitialNavigationCompleted(
481 browsingContext.webProgress,
485 this.curBrowser.contentBrowser.focus();
488 // Check if there is already an open dialog for the selected browser window.
489 this.dialog = lazy.modal.findPrompt(this.curBrowser);
492 lazy.registerCommandsActor(this.currentSession.id);
493 lazy.enableEventsActor();
495 Services.obs.addObserver(this, TOPIC_BROWSER_READY);
497 throw new lazy.error.SessionNotCreatedError(e);
501 sessionId: this.currentSession.id,
502 capabilities: this.currentSession.capabilities,
507 * Start observing the specified window.
509 * @param {ChromeWindow} win
510 * Chrome window to register event listeners for.
511 * @param {object=} options
512 * @param {boolean=} options.registerBrowsers
513 * If true, register all content browsers of found tabs. Defaults to false.
515 GeckoDriver.prototype.registerWindow = function (win, options = {}) {
516 const { registerBrowsers = false } = options;
517 const tabBrowser = lazy.TabManager.getTabBrowser(win);
519 if (registerBrowsers && tabBrowser) {
520 for (const tab of tabBrowser.tabs) {
521 const contentBrowser = lazy.TabManager.getBrowserForTab(tab);
522 this.registerBrowser(contentBrowser);
526 // Listen for any kind of top-level process switch
527 tabBrowser?.addEventListener("XULFrameLoaderCreated", this);
531 * Stop observing the specified window.
533 * @param {ChromeWindow} win
534 * Chrome window to unregister event listeners for.
536 GeckoDriver.prototype.stopObservingWindow = function (win) {
537 const tabBrowser = lazy.TabManager.getTabBrowser(win);
539 tabBrowser?.removeEventListener("XULFrameLoaderCreated", this);
542 GeckoDriver.prototype.handleEvent = function ({ target, type }) {
544 case "XULFrameLoaderCreated":
545 if (target === this.curBrowser.contentBrowser) {
547 "Remoteness change detected. Set new top-level browsing context " +
548 `to ${target.browsingContext.id}`
551 this.currentSession.contentBrowsingContext = target.browsingContext;
557 GeckoDriver.prototype.observe = async function (subject, topic, data) {
559 case TOPIC_BROWSER_READY:
560 this.registerWindow(subject);
563 case TOPIC_QUIT_APPLICATION_REQUESTED:
564 // Run Marionette specific cleanup steps before allowing
565 // the application to shutdown
566 await this._server.setAcceptConnections(false);
567 this.deleteSession();
573 * Send the current session's capabilities to the client.
575 * Capabilities informs the client of which WebDriver features are
576 * supported by Firefox and Marionette. They are immutable for the
577 * length of the session.
579 * The return value is an immutable map of string keys
580 * ("capabilities") to values, which may be of types boolean,
581 * numerical or string.
583 GeckoDriver.prototype.getSessionCapabilities = function () {
584 return { capabilities: this.currentSession.capabilities };
588 * Sets the context of the subsequent commands.
590 * All subsequent requests to commands that in some way involve
591 * interaction with a browsing context will target the chosen browsing
594 * @param {object} cmd
595 * @param {string} cmd.parameters.value
596 * Name of the context to be switched to. Must be one of "chrome" or
599 * @throws {InvalidArgumentError}
600 * If <var>value</var> is not a string.
601 * @throws {WebDriverError}
602 * If <var>value</var> is not a valid browsing context.
604 GeckoDriver.prototype.setContext = function (cmd) {
605 let value = lazy.assert.string(cmd.parameters.value);
607 this.context = value;
611 * Gets the context type that is Marionette's current target for
612 * browsing context scoped commands.
614 * You may choose a context through the {@link #setContext} command.
616 * The default browsing context is {@link Context.Content}.
621 GeckoDriver.prototype.getContext = function () {
626 * Executes a JavaScript function in the context of the current browsing
627 * context, if in content space, or in chrome space otherwise, and returns
628 * the return value of the function.
630 * It is important to note that if the <var>sandboxName</var> parameter
631 * is left undefined, the script will be evaluated in a mutable sandbox,
632 * causing any change it makes on the global state of the document to have
633 * lasting side-effects.
635 * @param {object} cmd
636 * @param {string} cmd.parameters.script
637 * Script to evaluate as a function body.
638 * @param {Array.<(string|boolean|number|object|WebReference)>} cmd.parameters.args
639 * Arguments exposed to the script in <code>arguments</code>.
640 * The array items must be serialisable to the WebDriver protocol.
641 * @param {string=} cmd.parameters.sandbox
642 * Name of the sandbox to evaluate the script in. The sandbox is
643 * cached for later re-use on the same Window object if
644 * <var>newSandbox</var> is false. If he parameter is undefined,
645 * the script is evaluated in a mutable sandbox. If the parameter
646 * is "system", it will be evaluted in a sandbox with elevated system
647 * privileges, equivalent to chrome space.
648 * @param {boolean=} cmd.parameters.newSandbox
649 * Forces the script to be evaluated in a fresh sandbox. Note that if
650 * it is undefined, the script will normally be evaluted in a fresh
652 * @param {string=} cmd.parameters.filename
653 * Filename of the client's program where this script is evaluated.
654 * @param {number=} cmd.parameters.line
655 * Line in the client's program where this script is evaluated.
657 * @returns {(string|boolean|number|object|WebReference)}
658 * Return value from the script, or null which signifies either the
659 * JavaScript notion of null or undefined.
661 * @throws {JavaScriptError}
662 * If an {@link Error} was thrown whilst evaluating the script.
663 * @throws {NoSuchElementError}
664 * If an element that was passed as part of <var>args</var> is unknown.
665 * @throws {NoSuchWindowError}
666 * Browsing context has been discarded.
667 * @throws {ScriptTimeoutError}
668 * If the script was interrupted due to reaching the session's
670 * @throws {StaleElementReferenceError}
671 * If an element that was passed as part of <var>args</var> or that is
672 * returned as result has gone stale.
674 GeckoDriver.prototype.executeScript = function (cmd) {
675 let { script, args } = cmd.parameters;
677 script: cmd.parameters.script,
678 args: cmd.parameters.args,
679 sandboxName: cmd.parameters.sandbox,
680 newSandbox: cmd.parameters.newSandbox,
681 file: cmd.parameters.filename,
682 line: cmd.parameters.line,
685 return this.execute_(script, args, opts);
689 * Executes a JavaScript function in the context of the current browsing
690 * context, if in content space, or in chrome space otherwise, and returns
691 * the object passed to the callback.
693 * The callback is always the last argument to the <var>arguments</var>
694 * list passed to the function scope of the script. It can be retrieved
698 * let callback = arguments[arguments.length - 1];
700 * // "foo" is returned
703 * It is important to note that if the <var>sandboxName</var> parameter
704 * is left undefined, the script will be evaluated in a mutable sandbox,
705 * causing any change it makes on the global state of the document to have
706 * lasting side-effects.
708 * @param {object} cmd
709 * @param {string} cmd.parameters.script
710 * Script to evaluate as a function body.
711 * @param {Array.<(string|boolean|number|object|WebReference)>} cmd.parameters.args
712 * Arguments exposed to the script in <code>arguments</code>.
713 * The array items must be serialisable to the WebDriver protocol.
714 * @param {string=} cmd.parameters.sandbox
715 * Name of the sandbox to evaluate the script in. The sandbox is
716 * cached for later re-use on the same Window object if
717 * <var>newSandbox</var> is false. If the parameter is undefined,
718 * the script is evaluated in a mutable sandbox. If the parameter
719 * is "system", it will be evaluted in a sandbox with elevated system
720 * privileges, equivalent to chrome space.
721 * @param {boolean=} cmd.parameters.newSandbox
722 * Forces the script to be evaluated in a fresh sandbox. Note that if
723 * it is undefined, the script will normally be evaluted in a fresh
725 * @param {string=} cmd.parameters.filename
726 * Filename of the client's program where this script is evaluated.
727 * @param {number=} cmd.parameters.line
728 * Line in the client's program where this script is evaluated.
730 * @returns {(string|boolean|number|object|WebReference)}
731 * Return value from the script, or null which signifies either the
732 * JavaScript notion of null or undefined.
734 * @throws {JavaScriptError}
735 * If an Error was thrown whilst evaluating the script.
736 * @throws {NoSuchElementError}
737 * If an element that was passed as part of <var>args</var> is unknown.
738 * @throws {NoSuchWindowError}
739 * Browsing context has been discarded.
740 * @throws {ScriptTimeoutError}
741 * If the script was interrupted due to reaching the session's
743 * @throws {StaleElementReferenceError}
744 * If an element that was passed as part of <var>args</var> or that is
745 * returned as result has gone stale.
747 GeckoDriver.prototype.executeAsyncScript = function (cmd) {
748 let { script, args } = cmd.parameters;
750 script: cmd.parameters.script,
751 args: cmd.parameters.args,
752 sandboxName: cmd.parameters.sandbox,
753 newSandbox: cmd.parameters.newSandbox,
754 file: cmd.parameters.filename,
755 line: cmd.parameters.line,
759 return this.execute_(script, args, opts);
762 GeckoDriver.prototype.execute_ = async function (
773 lazy.assert.open(this.getBrowsingContext());
774 await this._handleUserPrompts();
778 lazy.pprint`Expected "script" to be a string: ${script}`
782 lazy.pprint`Expected script args to be an array: ${args}`
784 if (sandboxName !== null) {
787 lazy.pprint`Expected sandbox name to be a string: ${sandboxName}`
792 lazy.pprint`Expected newSandbox to be boolean: ${newSandbox}`
794 lazy.assert.string(file, lazy.pprint`Expected file to be a string: ${file}`);
795 lazy.assert.number(line, lazy.pprint`Expected line to be a number: ${line}`);
798 timeout: this.currentSession.timeouts.script,
806 return this.getActor().executeScript(script, args, opts);
810 * Navigate to given URL.
812 * Navigates the current browsing context to the given URL and waits for
813 * the document to load or the session's page timeout duration to elapse
816 * The command will return with a failure if there is an error loading
817 * the document or the URL is blocked. This can occur if it fails to
818 * reach host, the URL is malformed, or if there is a certificate issue
819 * to name some examples.
821 * The document is considered successfully loaded when the
822 * DOMContentLoaded event on the frame element associated with the
823 * current window triggers and document.readyState is "complete".
825 * In chrome context it will change the current window's location to
826 * the supplied URL and wait until document.readyState equals "complete"
827 * or the page timeout duration has elapsed.
829 * @param {object} cmd
830 * @param {string} cmd.parameters.url
831 * URL to navigate to.
833 * @throws {NoSuchWindowError}
834 * Top-level browsing context has been discarded.
835 * @throws {UnexpectedAlertOpenError}
836 * A modal dialog is open, blocking this operation.
837 * @throws {UnsupportedOperationError}
838 * Not available in current context.
840 GeckoDriver.prototype.navigateTo = async function (cmd) {
841 lazy.assert.content(this.context);
842 const browsingContext = lazy.assert.open(
843 this.getBrowsingContext({ top: true })
845 await this._handleUserPrompts();
849 validURL = new URL(cmd.parameters.url);
851 throw new lazy.error.InvalidArgumentError(`Malformed URL: ${e.message}`);
854 // Switch to the top-level browsing context before navigating
855 this.currentSession.contentBrowsingContext = browsingContext;
857 const loadEventExpected = lazy.navigate.isLoadEventExpected(
858 this._getCurrentURL(),
864 await lazy.navigate.waitForNavigationCompleted(
867 lazy.navigate.navigateTo(browsingContext, validURL);
869 { loadEventExpected }
872 this.curBrowser.contentBrowser.focus();
876 * Get a string representing the current URL.
878 * On Desktop this returns a string representation of the URL of the
879 * current top level browsing context. This is equivalent to
880 * document.location.href.
882 * When in the context of the chrome, this returns the canonical URL
883 * of the current resource.
885 * @throws {NoSuchWindowError}
886 * Top-level browsing context has been discarded.
887 * @throws {UnexpectedAlertOpenError}
888 * A modal dialog is open, blocking this operation.
890 GeckoDriver.prototype.getCurrentUrl = async function () {
891 lazy.assert.open(this.getBrowsingContext({ top: true }));
892 await this._handleUserPrompts();
894 return this._getCurrentURL().href;
898 * Gets the current title of the window.
901 * Document title of the top-level browsing context.
903 * @throws {NoSuchWindowError}
904 * Top-level browsing context has been discarded.
905 * @throws {UnexpectedAlertOpenError}
906 * A modal dialog is open, blocking this operation.
908 GeckoDriver.prototype.getTitle = async function () {
909 lazy.assert.open(this.getBrowsingContext({ top: true }));
910 await this._handleUserPrompts();
916 * Gets the current type of the window.
921 * @throws {NoSuchWindowError}
922 * Top-level browsing context has been discarded.
924 GeckoDriver.prototype.getWindowType = function () {
925 lazy.assert.open(this.getBrowsingContext({ top: true }));
927 return this.windowType;
931 * Gets the page source of the content document.
934 * String serialisation of the DOM of the current browsing context's
937 * @throws {NoSuchWindowError}
938 * Browsing context has been discarded.
939 * @throws {UnexpectedAlertOpenError}
940 * A modal dialog is open, blocking this operation.
942 GeckoDriver.prototype.getPageSource = async function () {
943 lazy.assert.open(this.getBrowsingContext());
944 await this._handleUserPrompts();
946 return this.getActor().getPageSource();
950 * Cause the browser to traverse one step backward in the joint history
951 * of the current browsing context.
953 * @throws {NoSuchWindowError}
954 * Top-level browsing context has been discarded.
955 * @throws {UnexpectedAlertOpenError}
956 * A modal dialog is open, blocking this operation.
957 * @throws {UnsupportedOperationError}
958 * Not available in current context.
960 GeckoDriver.prototype.goBack = async function () {
961 lazy.assert.content(this.context);
962 const browsingContext = lazy.assert.open(
963 this.getBrowsingContext({ top: true })
965 await this._handleUserPrompts();
967 // If there is no history, just return
968 if (!browsingContext.embedderElement?.canGoBack) {
972 await lazy.navigate.waitForNavigationCompleted(this, () => {
973 browsingContext.goBack();
978 * Cause the browser to traverse one step forward in the joint history
979 * of the current browsing context.
981 * @throws {NoSuchWindowError}
982 * Top-level browsing context has been discarded.
983 * @throws {UnexpectedAlertOpenError}
984 * A modal dialog is open, blocking this operation.
985 * @throws {UnsupportedOperationError}
986 * Not available in current context.
988 GeckoDriver.prototype.goForward = async function () {
989 lazy.assert.content(this.context);
990 const browsingContext = lazy.assert.open(
991 this.getBrowsingContext({ top: true })
993 await this._handleUserPrompts();
995 // If there is no history, just return
996 if (!browsingContext.embedderElement?.canGoForward) {
1000 await lazy.navigate.waitForNavigationCompleted(this, () => {
1001 browsingContext.goForward();
1006 * Causes the browser to reload the page in current top-level browsing
1009 * @throws {NoSuchWindowError}
1010 * Top-level browsing context has been discarded.
1011 * @throws {UnexpectedAlertOpenError}
1012 * A modal dialog is open, blocking this operation.
1013 * @throws {UnsupportedOperationError}
1014 * Not available in current context.
1016 GeckoDriver.prototype.refresh = async function () {
1017 lazy.assert.content(this.context);
1018 const browsingContext = lazy.assert.open(
1019 this.getBrowsingContext({ top: true })
1021 await this._handleUserPrompts();
1023 // Switch to the top-level browsing context before navigating
1024 this.currentSession.contentBrowsingContext = browsingContext;
1026 await lazy.navigate.waitForNavigationCompleted(this, () => {
1027 lazy.navigate.refresh(browsingContext);
1032 * Get the current window's handle. On desktop this typically corresponds
1033 * to the currently selected tab.
1035 * For chrome scope it returns the window identifier for the current chrome
1036 * window for tests interested in managing the chrome window and tab separately.
1038 * Return an opaque server-assigned identifier to this window that
1039 * uniquely identifies it within this Marionette instance. This can
1040 * be used to switch to this window at a later point.
1043 * Unique window handle.
1045 * @throws {NoSuchWindowError}
1046 * Top-level browsing context has been discarded.
1048 GeckoDriver.prototype.getWindowHandle = function () {
1049 lazy.assert.open(this.getBrowsingContext({ top: true }));
1051 if (this.context == lazy.Context.Chrome) {
1052 return lazy.windowManager.getIdForWindow(this.curBrowser.window);
1054 return lazy.TabManager.getIdForBrowser(this.curBrowser.contentBrowser);
1058 * Get a list of top-level browsing contexts. On desktop this typically
1059 * corresponds to the set of open tabs for browser windows, or the window
1060 * itself for non-browser chrome windows.
1062 * For chrome scope it returns identifiers for each open chrome window for
1063 * tests interested in managing a set of chrome windows and tabs separately.
1065 * Each window handle is assigned by the server and is guaranteed unique,
1066 * however the return array does not have a specified ordering.
1068 * @returns {Array.<string>}
1069 * Unique window handles.
1071 GeckoDriver.prototype.getWindowHandles = function () {
1072 if (this.context == lazy.Context.Chrome) {
1073 return lazy.windowManager.chromeWindowHandles.map(String);
1075 return lazy.TabManager.allBrowserUniqueIds.map(String);
1079 * Get the current position and size of the browser window currently in focus.
1081 * Will return the current browser window size in pixels. Refers to
1082 * window outerWidth and outerHeight values, which include scroll bars,
1085 * @returns {Object<string, number>}
1086 * Object with |x| and |y| coordinates, and |width| and |height|
1087 * of browser window.
1089 * @throws {NoSuchWindowError}
1090 * Top-level browsing context has been discarded.
1091 * @throws {UnexpectedAlertOpenError}
1092 * A modal dialog is open, blocking this operation.
1094 GeckoDriver.prototype.getWindowRect = async function () {
1095 lazy.assert.open(this.getBrowsingContext({ top: true }));
1096 await this._handleUserPrompts();
1098 return this.curBrowser.rect;
1102 * Set the window position and size of the browser on the operating
1103 * system window manager.
1105 * The supplied `width` and `height` values refer to the window `outerWidth`
1106 * and `outerHeight` values, which include browser chrome and OS-level
1109 * @param {object} cmd
1110 * @param {number} cmd.parameters.x
1111 * X coordinate of the top/left of the window that it will be
1113 * @param {number} cmd.parameters.y
1114 * Y coordinate of the top/left of the window that it will be
1116 * @param {number} cmd.parameters.width
1117 * Width to resize the window to.
1118 * @param {number} cmd.parameters.height
1119 * Height to resize the window to.
1121 * @returns {Object<string, number>}
1122 * Object with `x` and `y` coordinates and `width` and `height`
1125 * @throws {NoSuchWindowError}
1126 * Top-level browsing context has been discarded.
1127 * @throws {UnexpectedAlertOpenError}
1128 * A modal dialog is open, blocking this operation.
1129 * @throws {UnsupportedOperationError}
1130 * Not applicable to application.
1132 GeckoDriver.prototype.setWindowRect = async function (cmd) {
1133 lazy.assert.desktop();
1134 lazy.assert.open(this.getBrowsingContext({ top: true }));
1135 await this._handleUserPrompts();
1137 const { x = null, y = null, width = null, height = null } = cmd.parameters;
1139 lazy.assert.integer(x);
1142 lazy.assert.integer(y);
1144 if (height !== null) {
1145 lazy.assert.positiveInteger(height);
1147 if (width !== null) {
1148 lazy.assert.positiveInteger(width);
1151 const win = this.getCurrentWindow();
1152 switch (lazy.WindowState.from(win.windowState)) {
1153 case lazy.WindowState.Fullscreen:
1154 await exitFullscreen(win);
1157 case lazy.WindowState.Maximized:
1158 case lazy.WindowState.Minimized:
1159 await restoreWindow(win);
1163 function geometryMatches() {
1167 (win.outerWidth !== width || win.outerHeight !== height)
1171 if (x !== null && y !== null && (win.screenX !== x || win.screenY !== y)) {
1174 lazy.logger.trace(`Requested window geometry matches`);
1178 if (!geometryMatches()) {
1179 // There might be more than one resize or MozUpdateWindowPos event due
1180 // to previous geometry changes, such as from restoreWindow(), so
1181 // wait longer if window geometry does not match.
1182 const options = { checkFn: geometryMatches, timeout: 500 };
1183 const promises = [];
1184 if (width !== null && height !== null) {
1185 promises.push(new lazy.EventPromise(win, "resize", options));
1186 win.resizeTo(width, height);
1188 if (x !== null && y !== null) {
1190 new lazy.EventPromise(win.windowRoot, "MozUpdateWindowPos", options)
1195 await Promise.race(promises);
1197 if (e instanceof lazy.error.TimeoutError) {
1198 // The operating system might not honor the move or resize, in which
1199 // case assume that geometry will have been adjusted "as close as
1200 // possible" to that requested. There may be no event received if the
1201 // geometry is already as close as possible.
1208 return this.curBrowser.rect;
1212 * Switch current top-level browsing context by name or server-assigned
1213 * ID. Searches for windows by name, then ID. Content windows take
1216 * @param {object} cmd
1217 * @param {string} cmd.parameters.handle
1218 * Handle of the window to switch to.
1219 * @param {boolean=} cmd.parameters.focus
1220 * A boolean value which determines whether to focus
1221 * the window. Defaults to true.
1223 * @throws {InvalidArgumentError}
1224 * If <var>handle</var> is not a string or <var>focus</var> not a boolean.
1225 * @throws {NoSuchWindowError}
1226 * Top-level browsing context has been discarded.
1228 GeckoDriver.prototype.switchToWindow = async function (cmd) {
1229 const { focus = true, handle } = cmd.parameters;
1233 lazy.pprint`Expected "handle" to be a string, got ${handle}`
1235 lazy.assert.boolean(
1237 lazy.pprint`Expected "focus" to be a boolean, got ${focus}`
1240 const found = lazy.windowManager.findWindowByHandle(handle);
1242 let selected = false;
1245 await this.setWindowHandle(found, focus);
1248 lazy.logger.error(e);
1253 throw new lazy.error.NoSuchWindowError(
1254 `Unable to locate window: ${handle}`
1260 * Switch the marionette window to a given window. If the browser in
1261 * the window is unregistered, register that browser and wait for
1262 * the registration is complete. If |focus| is true then set the focus
1265 * @param {object} winProperties
1266 * Object containing window properties such as returned from
1267 * :js:func:`GeckoDriver#getWindowProperties`
1268 * @param {boolean=} focus
1269 * A boolean value which determines whether to focus the window.
1272 GeckoDriver.prototype.setWindowHandle = async function (
1276 if (!(winProperties.id in this.browsers)) {
1277 // Initialise Marionette if the current chrome window has not been seen
1278 // before. Also register the initial tab, if one exists.
1279 this.addBrowser(winProperties.win);
1280 this.mainFrame = winProperties.win;
1282 this.currentSession.chromeBrowsingContext = this.mainFrame.browsingContext;
1284 if (!winProperties.hasTabBrowser) {
1285 this.currentSession.contentBrowsingContext = null;
1287 const tabBrowser = lazy.TabManager.getTabBrowser(winProperties.win);
1289 // For chrome windows such as a reftest window, `getTabBrowser` is not
1290 // a tabbrowser, it is the content browser which should be used here.
1291 const contentBrowser = tabBrowser.tabs
1292 ? tabBrowser.selectedBrowser
1295 this.currentSession.contentBrowsingContext =
1296 contentBrowser.browsingContext;
1297 this.registerBrowser(contentBrowser);
1300 // Otherwise switch to the known chrome window
1301 this.curBrowser = this.browsers[winProperties.id];
1302 this.mainFrame = this.curBrowser.window;
1304 // Activate the tab if it's a content window.
1306 if (winProperties.hasTabBrowser) {
1307 tab = await this.curBrowser.switchToTab(
1308 winProperties.tabIndex,
1314 this.currentSession.chromeBrowsingContext = this.mainFrame.browsingContext;
1315 this.currentSession.contentBrowsingContext =
1316 tab?.linkedBrowser.browsingContext;
1319 // Check for an existing dialog for the new window
1320 this.dialog = lazy.modal.findPrompt(this.curBrowser);
1322 // If there is an open window modal dialog the underlying chrome window
1323 // cannot be focused.
1324 if (focus && !this.dialog?.isWindowModal) {
1325 await this.curBrowser.focusWindow();
1330 * Set the current browsing context for future commands to the parent
1331 * of the current browsing context.
1333 * @throws {NoSuchWindowError}
1334 * Browsing context has been discarded.
1335 * @throws {UnexpectedAlertOpenError}
1336 * A modal dialog is open, blocking this operation.
1338 GeckoDriver.prototype.switchToParentFrame = async function () {
1339 let browsingContext = this.getBrowsingContext();
1340 if (browsingContext && !browsingContext.parent) {
1344 browsingContext = lazy.assert.open(browsingContext?.parent);
1346 this.currentSession.contentBrowsingContext = browsingContext;
1350 * Switch to a given frame within the current window.
1352 * @param {object} cmd
1353 * @param {(string | object)=} cmd.parameters.element
1354 * A web element reference of the frame or its element id.
1355 * @param {number=} cmd.parameters.id
1356 * The index of the frame to switch to.
1357 * If both element and id are not defined, switch to top-level frame.
1359 * @throws {NoSuchElementError}
1360 * If element represented by reference <var>element</var> is unknown.
1361 * @throws {NoSuchWindowError}
1362 * Browsing context has been discarded.
1363 * @throws {StaleElementReferenceError}
1364 * If element represented by reference <var>element</var> has gone stale.
1365 * @throws {UnexpectedAlertOpenError}
1366 * A modal dialog is open, blocking this operation.
1368 GeckoDriver.prototype.switchToFrame = async function (cmd) {
1369 const { element: el, id } = cmd.parameters;
1371 if (typeof id == "number") {
1372 lazy.assert.unsignedShort(
1374 `Expected id to be unsigned short, got ${id}`
1378 const top = id == null && el == null;
1379 lazy.assert.open(this.getBrowsingContext({ top }));
1380 await this._handleUserPrompts();
1382 // Bug 1495063: Elements should be passed as WebReference reference
1384 if (typeof el == "string") {
1385 byFrame = lazy.WebElement.fromUUID(el).toJSON();
1390 const { browsingContext } = await this.getActor({ top }).switchToFrame(
1394 this.currentSession.contentBrowsingContext = browsingContext;
1397 GeckoDriver.prototype.getTimeouts = function () {
1398 return this.currentSession.timeouts;
1402 * Set timeout for page loading, searching, and scripts.
1404 * @param {object} cmd
1405 * @param {Object<string, number>} cmd.parameters
1406 * Dictionary of timeout types and their new value, where all timeout
1407 * types are optional.
1409 * @throws {InvalidArgumentError}
1410 * If timeout type key is unknown, or the value provided with it is
1413 GeckoDriver.prototype.setTimeouts = function (cmd) {
1414 // merge with existing timeouts
1415 let merged = Object.assign(
1416 this.currentSession.timeouts.toJSON(),
1420 this.currentSession.timeouts = lazy.Timeouts.fromJSON(merged);
1424 * Perform a series of grouped actions at the specified points in time.
1426 * @param {object} cmd
1427 * @param {Array<?>} cmd.parameters.actions
1428 * Array of objects that each represent an action sequence.
1430 * @throws {NoSuchElementError}
1431 * If an element that is used as part of the action chain is unknown.
1432 * @throws {NoSuchWindowError}
1433 * Browsing context has been discarded.
1434 * @throws {StaleElementReferenceError}
1435 * If an element that is used as part of the action chain has gone stale.
1436 * @throws {UnexpectedAlertOpenError}
1437 * A modal dialog is open, blocking this operation.
1438 * @throws {UnsupportedOperationError}
1439 * Not yet available in current context.
1441 GeckoDriver.prototype.performActions = async function (cmd) {
1442 lazy.assert.open(this.getBrowsingContext());
1443 await this._handleUserPrompts();
1445 const actions = cmd.parameters.actions;
1446 await this.getActor().performActions(actions);
1450 * Release all the keys and pointer buttons that are currently depressed.
1452 * @throws {NoSuchWindowError}
1453 * Browsing context has been discarded.
1454 * @throws {UnexpectedAlertOpenError}
1455 * A modal dialog is open, blocking this operation.
1456 * @throws {UnsupportedOperationError}
1457 * Not available in current context.
1459 GeckoDriver.prototype.releaseActions = async function () {
1460 lazy.assert.open(this.getBrowsingContext());
1461 await this._handleUserPrompts();
1463 await this.getActor().releaseActions();
1467 * Find an element using the indicated search strategy.
1469 * @param {object} cmd
1470 * @param {string=} cmd.parameters.element
1471 * Web element reference ID to the element that will be used as start node.
1472 * @param {string} cmd.parameters.using
1473 * Indicates which search method to use.
1474 * @param {string} cmd.parameters.value
1475 * Value the client is looking for.
1477 * @returns {WebElement}
1478 * Return the found element.
1480 * @throws {NoSuchElementError}
1481 * If element represented by reference <var>element</var> is unknown.
1482 * @throws {NoSuchWindowError}
1483 * Browsing context has been discarded.
1484 * @throws {StaleElementReferenceError}
1485 * If element represented by reference <var>element</var> has gone stale.
1486 * @throws {UnexpectedAlertOpenError}
1487 * A modal dialog is open, blocking this operation.
1489 GeckoDriver.prototype.findElement = async function (cmd) {
1490 const { element: el, using, value } = cmd.parameters;
1492 if (!lazy.supportedStrategies.has(using)) {
1493 throw new lazy.error.InvalidSelectorError(
1494 `Strategy not supported: ${using}`
1498 lazy.assert.defined(value);
1499 lazy.assert.open(this.getBrowsingContext());
1500 await this._handleUserPrompts();
1503 if (typeof el != "undefined") {
1504 startNode = lazy.WebElement.fromUUID(el).toJSON();
1509 timeout: this.currentSession.timeouts.implicit,
1513 return this.getActor().findElement(using, value, opts);
1517 * Find an element within shadow root using the indicated search strategy.
1519 * @param {object} cmd
1520 * @param {string} cmd.parameters.shadowRoot
1521 * Shadow root reference ID.
1522 * @param {string} cmd.parameters.using
1523 * Indicates which search method to use.
1524 * @param {string} cmd.parameters.value
1525 * Value the client is looking for.
1527 * @returns {WebElement}
1528 * Return the found element.
1530 * @throws {DetachedShadowRootError}
1531 * If shadow root represented by reference <var>id</var> is
1532 * no longer attached to the DOM.
1533 * @throws {NoSuchElementError}
1534 * If the element which is looked for with <var>value</var> was
1536 * @throws {NoSuchShadowRoot}
1537 * If shadow root represented by reference <var>shadowRoot</var> is unknown.
1538 * @throws {NoSuchWindowError}
1539 * Browsing context has been discarded.
1540 * @throws {UnexpectedAlertOpenError}
1541 * A modal dialog is open, blocking this operation.
1543 GeckoDriver.prototype.findElementFromShadowRoot = async function (cmd) {
1544 const { shadowRoot, using, value } = cmd.parameters;
1546 if (!lazy.supportedStrategies.has(using)) {
1547 throw new lazy.error.InvalidSelectorError(
1548 `Strategy not supported: ${using}`
1552 lazy.assert.defined(value);
1553 lazy.assert.open(this.getBrowsingContext());
1554 await this._handleUserPrompts();
1558 startNode: lazy.ShadowRoot.fromUUID(shadowRoot).toJSON(),
1559 timeout: this.currentSession.timeouts.implicit,
1562 return this.getActor().findElement(using, value, opts);
1566 * Find elements using the indicated search strategy.
1568 * @param {object} cmd
1569 * @param {string=} cmd.parameters.element
1570 * Web element reference ID to the element that will be used as start node.
1571 * @param {string} cmd.parameters.using
1572 * Indicates which search method to use.
1573 * @param {string} cmd.parameters.value
1574 * Value the client is looking for.
1576 * @returns {Array<WebElement>}
1577 * Return the array of found elements.
1579 * @throws {NoSuchElementError}
1580 * If element represented by reference <var>element</var> is unknown.
1581 * @throws {NoSuchWindowError}
1582 * Browsing context has been discarded.
1583 * @throws {StaleElementReferenceError}
1584 * If element represented by reference <var>element</var> has gone stale.
1585 * @throws {UnexpectedAlertOpenError}
1586 * A modal dialog is open, blocking this operation.
1588 GeckoDriver.prototype.findElements = async function (cmd) {
1589 const { element: el, using, value } = cmd.parameters;
1591 if (!lazy.supportedStrategies.has(using)) {
1592 throw new lazy.error.InvalidSelectorError(
1593 `Strategy not supported: ${using}`
1597 lazy.assert.defined(value);
1598 lazy.assert.open(this.getBrowsingContext());
1599 await this._handleUserPrompts();
1602 if (typeof el != "undefined") {
1603 startNode = lazy.WebElement.fromUUID(el).toJSON();
1608 timeout: this.currentSession.timeouts.implicit,
1612 return this.getActor().findElements(using, value, opts);
1616 * Find elements within shadow root using the indicated search strategy.
1618 * @param {object} cmd
1619 * @param {string} cmd.parameters.shadowRoot
1620 * Shadow root reference ID.
1621 * @param {string} cmd.parameters.using
1622 * Indicates which search method to use.
1623 * @param {string} cmd.parameters.value
1624 * Value the client is looking for.
1626 * @returns {Array<WebElement>}
1627 * Return the array of found elements.
1629 * @throws {DetachedShadowRootError}
1630 * If shadow root represented by reference <var>id</var> is
1631 * no longer attached to the DOM.
1632 * @throws {NoSuchShadowRoot}
1633 * If shadow root represented by reference <var>shadowRoot</var> is unknown.
1634 * @throws {NoSuchWindowError}
1635 * Browsing context has been discarded.
1636 * @throws {UnexpectedAlertOpenError}
1637 * A modal dialog is open, blocking this operation.
1639 GeckoDriver.prototype.findElementsFromShadowRoot = async function (cmd) {
1640 const { shadowRoot, using, value } = cmd.parameters;
1642 if (!lazy.supportedStrategies.has(using)) {
1643 throw new lazy.error.InvalidSelectorError(
1644 `Strategy not supported: ${using}`
1648 lazy.assert.defined(value);
1649 lazy.assert.open(this.getBrowsingContext());
1650 await this._handleUserPrompts();
1654 startNode: lazy.ShadowRoot.fromUUID(shadowRoot).toJSON(),
1655 timeout: this.currentSession.timeouts.implicit,
1658 return this.getActor().findElements(using, value, opts);
1662 * Return the shadow root of an element in the document.
1664 * @param {object} cmd
1665 * @param {id} cmd.parameters.id
1666 * A web element id reference.
1667 * @returns {ShadowRoot}
1668 * ShadowRoot of the element.
1670 * @throws {InvalidArgumentError}
1671 * If element <var>id</var> is not a string.
1672 * @throws {NoSuchElementError}
1673 * If element represented by reference <var>id</var> is unknown.
1674 * @throws {NoSuchShadowRoot}
1675 * Element does not have a shadow root attached.
1676 * @throws {NoSuchWindowError}
1677 * Browsing context has been discarded.
1678 * @throws {StaleElementReferenceError}
1679 * If element represented by reference <var>id</var> has gone stale.
1680 * @throws {UnexpectedAlertOpenError}
1681 * A modal dialog is open, blocking this operation.
1682 * @throws {UnsupportedOperationError}
1683 * Not available in chrome current context.
1685 GeckoDriver.prototype.getShadowRoot = async function (cmd) {
1686 // Bug 1743541: Add support for chrome scope.
1687 lazy.assert.content(this.context);
1688 lazy.assert.open(this.getBrowsingContext());
1689 await this._handleUserPrompts();
1691 let id = lazy.assert.string(
1693 lazy.pprint`Expected "id" to be a string, got ${cmd.parameters.id}`
1695 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1697 return this.getActor().getShadowRoot(webEl);
1701 * Return the active element in the document.
1703 * @returns {WebReference}
1704 * Active element of the current browsing context's document
1705 * element, if the document element is non-null.
1707 * @throws {NoSuchElementError}
1708 * If the document does not have an active element, i.e. if
1709 * its document element has been deleted.
1710 * @throws {NoSuchWindowError}
1711 * Browsing context has been discarded.
1712 * @throws {UnexpectedAlertOpenError}
1713 * A modal dialog is open, blocking this operation.
1714 * @throws {UnsupportedOperationError}
1715 * Not available in chrome context.
1717 GeckoDriver.prototype.getActiveElement = async function () {
1718 lazy.assert.content(this.context);
1719 lazy.assert.open(this.getBrowsingContext());
1720 await this._handleUserPrompts();
1722 return this.getActor().getActiveElement();
1726 * Send click event to element.
1728 * @param {object} cmd
1729 * @param {string} cmd.parameters.id
1730 * Reference ID to the element that will be clicked.
1732 * @throws {InvalidArgumentError}
1733 * If element <var>id</var> is not a string.
1734 * @throws {NoSuchElementError}
1735 * If element represented by reference <var>id</var> is unknown.
1736 * @throws {NoSuchWindowError}
1737 * Browsing context has been discarded.
1738 * @throws {StaleElementReferenceError}
1739 * If element represented by reference <var>id</var> has gone stale.
1740 * @throws {UnexpectedAlertOpenError}
1741 * A modal dialog is open, blocking this operation.
1743 GeckoDriver.prototype.clickElement = async function (cmd) {
1744 const browsingContext = lazy.assert.open(this.getBrowsingContext());
1745 await this._handleUserPrompts();
1747 let id = lazy.assert.string(cmd.parameters.id);
1748 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1750 const actor = this.getActor();
1752 const loadEventExpected = lazy.navigate.isLoadEventExpected(
1753 this._getCurrentURL(),
1756 target: await actor.getElementAttribute(webEl, "target"),
1760 await lazy.navigate.waitForNavigationCompleted(
1762 () => actor.clickElement(webEl, this.currentSession.capabilities),
1765 // The click might trigger a navigation, so don't count on it.
1766 requireBeforeUnload: false,
1772 * Get a given attribute of an element.
1774 * @param {object} cmd
1775 * @param {string} cmd.parameters.id
1776 * Web element reference ID to the element that will be inspected.
1777 * @param {string} cmd.parameters.name
1778 * Name of the attribute which value to retrieve.
1781 * Value of the attribute.
1783 * @throws {InvalidArgumentError}
1784 * If <var>id</var> or <var>name</var> are not strings.
1785 * @throws {NoSuchElementError}
1786 * If element represented by reference <var>id</var> is unknown.
1787 * @throws {NoSuchWindowError}
1788 * Browsing context has been discarded.
1789 * @throws {StaleElementReferenceError}
1790 * If element represented by reference <var>id</var> has gone stale.
1791 * @throws {UnexpectedAlertOpenError}
1792 * A modal dialog is open, blocking this operation.
1794 GeckoDriver.prototype.getElementAttribute = async function (cmd) {
1795 lazy.assert.open(this.getBrowsingContext());
1796 await this._handleUserPrompts();
1798 const id = lazy.assert.string(cmd.parameters.id);
1799 const name = lazy.assert.string(cmd.parameters.name);
1800 const webEl = lazy.WebElement.fromUUID(id).toJSON();
1802 return this.getActor().getElementAttribute(webEl, name);
1806 * Returns the value of a property associated with given element.
1808 * @param {object} cmd
1809 * @param {string} cmd.parameters.id
1810 * Web element reference ID to the element that will be inspected.
1811 * @param {string} cmd.parameters.name
1812 * Name of the property which value to retrieve.
1815 * Value of the property.
1817 * @throws {InvalidArgumentError}
1818 * If <var>id</var> or <var>name</var> are not strings.
1819 * @throws {NoSuchElementError}
1820 * If element represented by reference <var>id</var> is unknown.
1821 * @throws {NoSuchWindowError}
1822 * Browsing context has been discarded.
1823 * @throws {StaleElementReferenceError}
1824 * If element represented by reference <var>id</var> has gone stale.
1825 * @throws {UnexpectedAlertOpenError}
1826 * A modal dialog is open, blocking this operation.
1828 GeckoDriver.prototype.getElementProperty = async function (cmd) {
1829 lazy.assert.open(this.getBrowsingContext());
1830 await this._handleUserPrompts();
1832 const id = lazy.assert.string(cmd.parameters.id);
1833 const name = lazy.assert.string(cmd.parameters.name);
1834 const webEl = lazy.WebElement.fromUUID(id).toJSON();
1836 return this.getActor().getElementProperty(webEl, name);
1840 * Get the text of an element, if any. Includes the text of all child
1843 * @param {object} cmd
1844 * @param {string} cmd.parameters.id
1845 * Reference ID to the element that will be inspected.
1848 * Element's text "as rendered".
1850 * @throws {InvalidArgumentError}
1851 * If <var>id</var> is not a string.
1852 * @throws {NoSuchElementError}
1853 * If element represented by reference <var>id</var> is unknown.
1854 * @throws {NoSuchWindowError}
1855 * Browsing context has been discarded.
1856 * @throws {StaleElementReferenceError}
1857 * If element represented by reference <var>id</var> has gone stale.
1858 * @throws {UnexpectedAlertOpenError}
1859 * A modal dialog is open, blocking this operation.
1861 GeckoDriver.prototype.getElementText = async function (cmd) {
1862 lazy.assert.open(this.getBrowsingContext());
1863 await this._handleUserPrompts();
1865 let id = lazy.assert.string(cmd.parameters.id);
1866 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1868 return this.getActor().getElementText(webEl);
1872 * Get the tag name of the element.
1874 * @param {object} cmd
1875 * @param {string} cmd.parameters.id
1876 * Reference ID to the element that will be inspected.
1879 * Local tag name of element.
1881 * @throws {InvalidArgumentError}
1882 * If <var>id</var> is not a string.
1883 * @throws {NoSuchElementError}
1884 * If element represented by reference <var>id</var> is unknown.
1885 * @throws {NoSuchWindowError}
1886 * Browsing context has been discarded.
1887 * @throws {StaleElementReferenceError}
1888 * If element represented by reference <var>id</var> has gone stale.
1889 * @throws {UnexpectedAlertOpenError}
1890 * A modal dialog is open, blocking this operation.
1892 GeckoDriver.prototype.getElementTagName = async function (cmd) {
1893 lazy.assert.open(this.getBrowsingContext());
1894 await this._handleUserPrompts();
1896 let id = lazy.assert.string(cmd.parameters.id);
1897 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1899 return this.getActor().getElementTagName(webEl);
1903 * Check if element is displayed.
1905 * @param {object} cmd
1906 * @param {string} cmd.parameters.id
1907 * Reference ID to the element that will be inspected.
1909 * @returns {boolean}
1910 * True if displayed, false otherwise.
1912 * @throws {InvalidArgumentError}
1913 * If <var>id</var> is not a string.
1914 * @throws {NoSuchElementError}
1915 * If element represented by reference <var>id</var> is unknown.
1916 * @throws {NoSuchWindowError}
1917 * Browsing context has been discarded.
1918 * @throws {UnexpectedAlertOpenError}
1919 * A modal dialog is open, blocking this operation.
1921 GeckoDriver.prototype.isElementDisplayed = async function (cmd) {
1922 lazy.assert.open(this.getBrowsingContext());
1923 await this._handleUserPrompts();
1925 let id = lazy.assert.string(cmd.parameters.id);
1926 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1928 return this.getActor().isElementDisplayed(
1930 this.currentSession.capabilities
1935 * Return the property of the computed style of an element.
1937 * @param {object} cmd
1938 * @param {string} cmd.parameters.id
1939 * Reference ID to the element that will be checked.
1940 * @param {string} cmd.parameters.propertyName
1941 * CSS rule that is being requested.
1944 * Value of |propertyName|.
1946 * @throws {InvalidArgumentError}
1947 * If <var>id</var> or <var>propertyName</var> are not strings.
1948 * @throws {NoSuchElementError}
1949 * If element represented by reference <var>id</var> is unknown.
1950 * @throws {NoSuchWindowError}
1951 * Browsing context has been discarded.
1952 * @throws {StaleElementReferenceError}
1953 * If element represented by reference <var>id</var> has gone stale.
1954 * @throws {UnexpectedAlertOpenError}
1955 * A modal dialog is open, blocking this operation.
1957 GeckoDriver.prototype.getElementValueOfCssProperty = async function (cmd) {
1958 lazy.assert.open(this.getBrowsingContext());
1959 await this._handleUserPrompts();
1961 let id = lazy.assert.string(cmd.parameters.id);
1962 let prop = lazy.assert.string(cmd.parameters.propertyName);
1963 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1965 return this.getActor().getElementValueOfCssProperty(webEl, prop);
1969 * Check if element is enabled.
1971 * @param {object} cmd
1972 * @param {string} cmd.parameters.id
1973 * Reference ID to the element that will be checked.
1975 * @returns {boolean}
1976 * True if enabled, false if disabled.
1978 * @throws {InvalidArgumentError}
1979 * If <var>id</var> is not a string.
1980 * @throws {NoSuchElementError}
1981 * If element represented by reference <var>id</var> is unknown.
1982 * @throws {NoSuchWindowError}
1983 * Browsing context has been discarded.
1984 * @throws {StaleElementReferenceError}
1985 * If element represented by reference <var>id</var> has gone stale.
1986 * @throws {UnexpectedAlertOpenError}
1987 * A modal dialog is open, blocking this operation.
1989 GeckoDriver.prototype.isElementEnabled = async function (cmd) {
1990 lazy.assert.open(this.getBrowsingContext());
1991 await this._handleUserPrompts();
1993 let id = lazy.assert.string(cmd.parameters.id);
1994 let webEl = lazy.WebElement.fromUUID(id).toJSON();
1996 return this.getActor().isElementEnabled(
1998 this.currentSession.capabilities
2003 * Check if element is selected.
2005 * @param {object} cmd
2006 * @param {string} cmd.parameters.id
2007 * Reference ID to the element that will be checked.
2009 * @returns {boolean}
2010 * True if selected, false if unselected.
2012 * @throws {InvalidArgumentError}
2013 * If <var>id</var> is not a string.
2014 * @throws {NoSuchElementError}
2015 * If element represented by reference <var>id</var> is unknown.
2016 * @throws {NoSuchWindowError}
2017 * Browsing context has been discarded.
2018 * @throws {UnexpectedAlertOpenError}
2019 * A modal dialog is open, blocking this operation.
2021 GeckoDriver.prototype.isElementSelected = async function (cmd) {
2022 lazy.assert.open(this.getBrowsingContext());
2023 await this._handleUserPrompts();
2025 let id = lazy.assert.string(cmd.parameters.id);
2026 let webEl = lazy.WebElement.fromUUID(id).toJSON();
2028 return this.getActor().isElementSelected(
2030 this.currentSession.capabilities
2035 * @throws {InvalidArgumentError}
2036 * If <var>id</var> is not a string.
2037 * @throws {NoSuchElementError}
2038 * If element represented by reference <var>id</var> is unknown.
2039 * @throws {NoSuchWindowError}
2040 * Browsing context has been discarded.
2041 * @throws {StaleElementReferenceError}
2042 * If element represented by reference <var>id</var> has gone stale.
2043 * @throws {UnexpectedAlertOpenError}
2044 * A modal dialog is open, blocking this operation.
2046 GeckoDriver.prototype.getElementRect = async function (cmd) {
2047 lazy.assert.open(this.getBrowsingContext());
2048 await this._handleUserPrompts();
2050 let id = lazy.assert.string(cmd.parameters.id);
2051 let webEl = lazy.WebElement.fromUUID(id).toJSON();
2053 return this.getActor().getElementRect(webEl);
2057 * Send key presses to element after focusing on it.
2059 * @param {object} cmd
2060 * @param {string} cmd.parameters.id
2061 * Reference ID to the element that will be checked.
2062 * @param {string} cmd.parameters.text
2063 * Value to send to the element.
2065 * @throws {InvalidArgumentError}
2066 * If <var>id</var> or <var>text</var> are not strings.
2067 * @throws {NoSuchElementError}
2068 * If element represented by reference <var>id</var> is unknown.
2069 * @throws {NoSuchWindowError}
2070 * Browsing context has been discarded.
2071 * @throws {StaleElementReferenceError}
2072 * If element represented by reference <var>id</var> has gone stale.
2073 * @throws {UnexpectedAlertOpenError}
2074 * A modal dialog is open, blocking this operation.
2076 GeckoDriver.prototype.sendKeysToElement = async function (cmd) {
2077 lazy.assert.open(this.getBrowsingContext());
2078 await this._handleUserPrompts();
2080 let id = lazy.assert.string(cmd.parameters.id);
2081 let text = lazy.assert.string(cmd.parameters.text);
2082 let webEl = lazy.WebElement.fromUUID(id).toJSON();
2084 return this.getActor().sendKeysToElement(
2087 this.currentSession.capabilities
2092 * Clear the text of an element.
2094 * @param {object} cmd
2095 * @param {string} cmd.parameters.id
2096 * Reference ID to the element that will be cleared.
2098 * @throws {InvalidArgumentError}
2099 * If <var>id</var> is not a string.
2100 * @throws {NoSuchElementError}
2101 * If element represented by reference <var>id</var> is unknown.
2102 * @throws {NoSuchWindowError}
2103 * Browsing context has been discarded.
2104 * @throws {StaleElementReferenceError}
2105 * If element represented by reference <var>id</var> has gone stale.
2106 * @throws {UnexpectedAlertOpenError}
2107 * A modal dialog is open, blocking this operation.
2109 GeckoDriver.prototype.clearElement = async function (cmd) {
2110 lazy.assert.open(this.getBrowsingContext());
2111 await this._handleUserPrompts();
2113 let id = lazy.assert.string(cmd.parameters.id);
2114 let webEl = lazy.WebElement.fromUUID(id).toJSON();
2116 await this.getActor().clearElement(webEl);
2120 * Add a single cookie to the cookie store associated with the active
2121 * document's address.
2123 * @param {object} cmd
2124 * @param {Map.<string, (string|number|boolean)>} cmd.parameters.cookie
2127 * @throws {InvalidCookieDomainError}
2128 * If <var>cookie</var> is for a different domain than the active
2130 * @throws {NoSuchWindowError}
2131 * Bbrowsing context has been discarded.
2132 * @throws {UnexpectedAlertOpenError}
2133 * A modal dialog is open, blocking this operation.
2134 * @throws {UnsupportedOperationError}
2135 * Not available in current context.
2137 GeckoDriver.prototype.addCookie = async function (cmd) {
2138 lazy.assert.content(this.context);
2139 lazy.assert.open(this.getBrowsingContext());
2140 await this._handleUserPrompts();
2142 let { protocol, hostname } = this._getCurrentURL();
2144 const networkSchemes = ["http:", "https:"];
2145 if (!networkSchemes.includes(protocol)) {
2146 throw new lazy.error.InvalidCookieDomainError("Document is cookie-averse");
2149 let newCookie = lazy.cookie.fromJSON(cmd.parameters.cookie);
2151 lazy.cookie.add(newCookie, { restrictToHost: hostname, protocol });
2155 * Get all the cookies for the current domain.
2157 * This is the equivalent of calling <code>document.cookie</code> and
2158 * parsing the result.
2160 * @throws {NoSuchWindowError}
2161 * Browsing context has been discarded.
2162 * @throws {UnexpectedAlertOpenError}
2163 * A modal dialog is open, blocking this operation.
2164 * @throws {UnsupportedOperationError}
2165 * Not available in current context.
2167 GeckoDriver.prototype.getCookies = async function () {
2168 lazy.assert.content(this.context);
2169 lazy.assert.open(this.getBrowsingContext());
2170 await this._handleUserPrompts();
2172 let { hostname, pathname } = this._getCurrentURL();
2173 return [...lazy.cookie.iter(hostname, pathname)];
2177 * Delete all cookies that are visible to a document.
2179 * @throws {NoSuchWindowError}
2180 * Browsing context has been discarded.
2181 * @throws {UnexpectedAlertOpenError}
2182 * A modal dialog is open, blocking this operation.
2183 * @throws {UnsupportedOperationError}
2184 * Not available in current context.
2186 GeckoDriver.prototype.deleteAllCookies = async function () {
2187 lazy.assert.content(this.context);
2188 lazy.assert.open(this.getBrowsingContext());
2189 await this._handleUserPrompts();
2191 let { hostname, pathname } = this._getCurrentURL();
2192 for (let toDelete of lazy.cookie.iter(hostname, pathname)) {
2193 lazy.cookie.remove(toDelete);
2198 * Delete a cookie by name.
2200 * @throws {NoSuchWindowError}
2201 * Browsing context has been discarded.
2202 * @throws {UnexpectedAlertOpenError}
2203 * A modal dialog is open, blocking this operation.
2204 * @throws {UnsupportedOperationError}
2205 * Not available in current context.
2207 GeckoDriver.prototype.deleteCookie = async function (cmd) {
2208 lazy.assert.content(this.context);
2209 lazy.assert.open(this.getBrowsingContext());
2210 await this._handleUserPrompts();
2212 let { hostname, pathname } = this._getCurrentURL();
2213 let name = lazy.assert.string(cmd.parameters.name);
2214 for (let c of lazy.cookie.iter(hostname, pathname)) {
2215 if (c.name === name) {
2216 lazy.cookie.remove(c);
2222 * Open a new top-level browsing context.
2224 * @param {object} cmd
2225 * @param {string=} cmd.parameters.type
2226 * Optional type of the new top-level browsing context. Can be one of
2227 * `tab` or `window`. Defaults to `tab`.
2228 * @param {boolean=} cmd.parameters.focus
2229 * Optional flag if the new top-level browsing context should be opened
2230 * in foreground (focused) or background (not focused). Defaults to false.
2231 * @param {boolean=} cmd.parameters.private
2232 * Optional flag, which gets only evaluated for type `window`. True if the
2233 * new top-level browsing context should be a private window.
2234 * Defaults to false.
2236 * @returns {Object<string, string>}
2237 * Handle and type of the new browsing context.
2239 * @throws {NoSuchWindowError}
2240 * Top-level browsing context has been discarded.
2241 * @throws {UnexpectedAlertOpenError}
2242 * A modal dialog is open, blocking this operation.
2244 GeckoDriver.prototype.newWindow = async function (cmd) {
2245 lazy.assert.open(this.getBrowsingContext({ top: true }));
2246 await this._handleUserPrompts();
2249 if (typeof cmd.parameters.focus != "undefined") {
2250 focus = lazy.assert.boolean(
2251 cmd.parameters.focus,
2252 lazy.pprint`Expected "focus" to be a boolean, got ${cmd.parameters.focus}`
2256 let isPrivate = false;
2257 if (typeof cmd.parameters.private != "undefined") {
2258 isPrivate = lazy.assert.boolean(
2259 cmd.parameters.private,
2260 lazy.pprint`Expected "private" to be a boolean, got ${cmd.parameters.private}`
2265 if (typeof cmd.parameters.type != "undefined") {
2266 type = lazy.assert.string(
2267 cmd.parameters.type,
2268 lazy.pprint`Expected "type" to be a string, got ${cmd.parameters.type}`
2272 // If an invalid or no type has been specified default to a tab.
2273 if (typeof type == "undefined" || !["tab", "window"].includes(type)) {
2281 let win = await this.curBrowser.openBrowserWindow(focus, isPrivate);
2282 contentBrowser = lazy.TabManager.getTabBrowser(win).selectedBrowser;
2286 // To not fail if a new type gets added in the future, make opening
2287 // a new tab the default action.
2288 let tab = await this.curBrowser.openTab(focus);
2289 contentBrowser = lazy.TabManager.getBrowserForTab(tab);
2292 // Actors need the new window to be loaded to safely execute queries.
2293 // Wait until the initial page load has been finished.
2294 await lazy.waitForInitialNavigationCompleted(
2295 contentBrowser.browsingContext.webProgress,
2297 unloadTimeout: 5000,
2301 const id = lazy.TabManager.getIdForBrowser(contentBrowser);
2303 return { handle: id.toString(), type };
2307 * Close the currently selected tab/window.
2309 * With multiple open tabs present the currently selected tab will
2310 * be closed. Otherwise the window itself will be closed. If it is the
2311 * last window currently open, the window will not be closed to prevent
2312 * a shutdown of the application. Instead the returned list of window
2315 * @returns {Array.<string>}
2316 * Unique window handles of remaining windows.
2318 * @throws {NoSuchWindowError}
2319 * Top-level browsing context has been discarded.
2320 * @throws {UnexpectedAlertOpenError}
2321 * A modal dialog is open, blocking this operation.
2323 GeckoDriver.prototype.close = async function () {
2325 this.getBrowsingContext({ context: lazy.Context.Content, top: true })
2327 await this._handleUserPrompts();
2329 // If there is only one window left, do not close unless windowless mode is
2330 // enabled. Instead return a faked empty array of window handles.
2331 // This will instruct geckodriver to terminate the application.
2333 lazy.TabManager.getTabCount() === 1 &&
2334 !this.currentSession.capabilities.get("moz:windowless")
2339 await this.curBrowser.closeTab();
2340 this.currentSession.contentBrowsingContext = null;
2342 return lazy.TabManager.allBrowserUniqueIds.map(String);
2346 * Close the currently selected chrome window.
2348 * If it is the last window currently open, the chrome window will not be
2349 * closed to prevent a shutdown of the application. Instead the returned
2350 * list of chrome window handles is empty.
2352 * @returns {Array.<string>}
2353 * Unique chrome window handles of remaining chrome windows.
2355 * @throws {NoSuchWindowError}
2356 * Top-level browsing context has been discarded.
2358 GeckoDriver.prototype.closeChromeWindow = async function () {
2359 lazy.assert.desktop();
2361 this.getBrowsingContext({ context: lazy.Context.Chrome, top: true })
2366 // eslint-disable-next-line
2367 for (let _ of lazy.windowManager.windows) {
2371 // If there is only one window left, do not close unless windowless mode is
2372 // enabled. Instead return a faked empty array of window handles.
2373 // This will instruct geckodriver to terminate the application.
2374 if (nwins == 1 && !this.currentSession.capabilities.get("moz:windowless")) {
2378 await this.curBrowser.closeWindow();
2379 this.currentSession.chromeBrowsingContext = null;
2380 this.currentSession.contentBrowsingContext = null;
2382 return lazy.windowManager.chromeWindowHandles.map(String);
2385 /** Delete Marionette session. */
2386 GeckoDriver.prototype.deleteSession = function () {
2387 if (!this.currentSession) {
2391 for (let win of lazy.windowManager.windows) {
2392 this.stopObservingWindow(win);
2395 // reset to the top-most frame
2396 this.mainFrame = null;
2398 if (this.promptListener) {
2399 this.promptListener.stopListening();
2400 this.promptListener = null;
2404 Services.obs.removeObserver(this, TOPIC_BROWSER_READY);
2406 lazy.logger.debug(`Failed to remove observer "${TOPIC_BROWSER_READY}"`);
2409 // Always unregister actors after all other observers
2410 // and listeners have been removed.
2411 lazy.unregisterCommandsActor();
2412 // MarionetteEvents actors are only disabled to avoid IPC errors if there are
2413 // in flight events being forwarded from the content process to the parent
2415 lazy.disableEventsActor();
2417 if (lazy.RemoteAgent.webDriverBiDi) {
2418 lazy.RemoteAgent.webDriverBiDi.deleteSession();
2420 this.currentSession.destroy();
2421 this._currentSession = null;
2426 * Takes a screenshot of a web element, current frame, or viewport.
2428 * The screen capture is returned as a lossless PNG image encoded as
2431 * If called in the content context, the |id| argument is not null and
2432 * refers to a present and visible web element's ID, the capture area will
2433 * be limited to the bounding box of that element. Otherwise, the capture
2434 * area will be the bounding box of the current frame.
2436 * If called in the chrome context, the screenshot will always represent
2437 * the entire viewport.
2439 * @param {object} cmd
2440 * @param {string=} cmd.parameters.id
2441 * Optional web element reference to take a screenshot of.
2442 * If undefined, a screenshot will be taken of the document element.
2443 * @param {boolean=} cmd.parameters.full
2444 * True to take a screenshot of the entire document element. Is only
2445 * considered if <var>id</var> is not defined. Defaults to true.
2446 * @param {boolean=} cmd.parameters.hash
2447 * True if the user requests a hash of the image data. Defaults to false.
2448 * @param {boolean=} cmd.parameters.scroll
2449 * Scroll to element if |id| is provided. Defaults to true.
2452 * If <var>hash</var> is false, PNG image encoded as Base64 encoded
2453 * string. If <var>hash</var> is true, hex digest of the SHA-256
2454 * hash of the Base64 encoded string.
2456 * @throws {NoSuchElementError}
2457 * If element represented by reference <var>id</var> is unknown.
2458 * @throws {NoSuchWindowError}
2459 * Browsing context has been discarded.
2460 * @throws {StaleElementReferenceError}
2461 * If element represented by reference <var>id</var> has gone stale.
2463 GeckoDriver.prototype.takeScreenshot = async function (cmd) {
2464 lazy.assert.open(this.getBrowsingContext({ top: true }));
2465 await this._handleUserPrompts();
2467 let { id, full, hash, scroll } = cmd.parameters;
2468 let format = hash ? lazy.capture.Format.Hash : lazy.capture.Format.Base64;
2470 full = typeof full == "undefined" ? true : full;
2471 scroll = typeof scroll == "undefined" ? true : scroll;
2473 let webEl = id ? lazy.WebElement.fromUUID(id).toJSON() : null;
2475 // Only consider full screenshot if no element has been specified
2476 full = webEl ? false : full;
2478 return this.getActor().takeScreenshot(webEl, format, full, scroll);
2482 * Get the current browser orientation.
2484 * Will return one of the valid primary orientation values
2485 * portrait-primary, landscape-primary, portrait-secondary, or
2486 * landscape-secondary.
2488 * @throws {NoSuchWindowError}
2489 * Top-level browsing context has been discarded.
2491 GeckoDriver.prototype.getScreenOrientation = function () {
2492 lazy.assert.mobile();
2493 lazy.assert.open(this.getBrowsingContext({ top: true }));
2495 const win = this.getCurrentWindow();
2497 return win.screen.orientation.type;
2501 * Set the current browser orientation.
2503 * The supplied orientation should be given as one of the valid
2504 * orientation values. If the orientation is unknown, an error will
2507 * Valid orientations are "portrait" and "landscape", which fall
2508 * back to "portrait-primary" and "landscape-primary" respectively,
2509 * and "portrait-secondary" as well as "landscape-secondary".
2511 * @throws {NoSuchWindowError}
2512 * Top-level browsing context has been discarded.
2514 GeckoDriver.prototype.setScreenOrientation = async function (cmd) {
2515 lazy.assert.mobile();
2516 lazy.assert.open(this.getBrowsingContext({ top: true }));
2522 "landscape-primary",
2523 "portrait-secondary",
2524 "landscape-secondary",
2527 let or = String(cmd.parameters.orientation);
2528 lazy.assert.string(or);
2529 let mozOr = or.toLowerCase();
2530 if (!ors.includes(mozOr)) {
2531 throw new lazy.error.InvalidArgumentError(
2532 `Unknown screen orientation: ${or}`
2536 const win = this.getCurrentWindow();
2539 await win.screen.orientation.lock(mozOr);
2541 throw new lazy.error.WebDriverError(
2542 `Unable to set screen orientation: ${or}`
2548 * Synchronously minimizes the user agent window as if the user pressed
2549 * the minimize button.
2551 * No action is taken if the window is already minimized.
2553 * Not supported on Fennec.
2555 * @returns {Object<string, number>}
2556 * Window rect and window state.
2558 * @throws {NoSuchWindowError}
2559 * Top-level browsing context has been discarded.
2560 * @throws {UnexpectedAlertOpenError}
2561 * A modal dialog is open, blocking this operation.
2562 * @throws {UnsupportedOperationError}
2563 * Not available for current application.
2565 GeckoDriver.prototype.minimizeWindow = async function () {
2566 lazy.assert.desktop();
2567 lazy.assert.open(this.getBrowsingContext({ top: true }));
2568 await this._handleUserPrompts();
2570 const win = this.getCurrentWindow();
2571 switch (lazy.WindowState.from(win.windowState)) {
2572 case lazy.WindowState.Fullscreen:
2573 await exitFullscreen(win);
2576 case lazy.WindowState.Maximized:
2577 await restoreWindow(win);
2581 if (lazy.WindowState.from(win.windowState) != lazy.WindowState.Minimized) {
2583 // Use a timed promise to abort if no window manager is present
2584 await new lazy.TimedPromise(
2586 cb = new lazy.DebounceCallback(resolve);
2587 win.addEventListener("sizemodechange", cb);
2590 { throws: null, timeout: TIMEOUT_NO_WINDOW_MANAGER }
2592 win.removeEventListener("sizemodechange", cb);
2593 await new lazy.IdlePromise(win);
2596 return this.curBrowser.rect;
2600 * Synchronously maximizes the user agent window as if the user pressed
2601 * the maximize button.
2603 * No action is taken if the window is already maximized.
2605 * Not supported on Fennec.
2607 * @returns {Object<string, number>}
2610 * @throws {NoSuchWindowError}
2611 * Top-level browsing context has been discarded.
2612 * @throws {UnexpectedAlertOpenError}
2613 * A modal dialog is open, blocking this operation.
2614 * @throws {UnsupportedOperationError}
2615 * Not available for current application.
2617 GeckoDriver.prototype.maximizeWindow = async function () {
2618 lazy.assert.desktop();
2619 lazy.assert.open(this.getBrowsingContext({ top: true }));
2620 await this._handleUserPrompts();
2622 const win = this.getCurrentWindow();
2623 switch (lazy.WindowState.from(win.windowState)) {
2624 case lazy.WindowState.Fullscreen:
2625 await exitFullscreen(win);
2628 case lazy.WindowState.Minimized:
2629 await restoreWindow(win);
2633 if (lazy.WindowState.from(win.windowState) != lazy.WindowState.Maximized) {
2635 // Use a timed promise to abort if no window manager is present
2636 await new lazy.TimedPromise(
2638 cb = new lazy.DebounceCallback(resolve);
2639 win.addEventListener("sizemodechange", cb);
2642 { throws: null, timeout: TIMEOUT_NO_WINDOW_MANAGER }
2644 win.removeEventListener("sizemodechange", cb);
2645 await new lazy.IdlePromise(win);
2648 return this.curBrowser.rect;
2652 * Synchronously sets the user agent window to full screen as if the user
2653 * had done "View > Enter Full Screen".
2655 * No action is taken if the window is already in full screen mode.
2657 * Not supported on Fennec.
2659 * @returns {Map.<string, number>}
2662 * @throws {NoSuchWindowError}
2663 * Top-level browsing context has been discarded.
2664 * @throws {UnexpectedAlertOpenError}
2665 * A modal dialog is open, blocking this operation.
2666 * @throws {UnsupportedOperationError}
2667 * Not available for current application.
2669 GeckoDriver.prototype.fullscreenWindow = async function () {
2670 lazy.assert.desktop();
2671 lazy.assert.open(this.getBrowsingContext({ top: true }));
2672 await this._handleUserPrompts();
2674 const win = this.getCurrentWindow();
2675 switch (lazy.WindowState.from(win.windowState)) {
2676 case lazy.WindowState.Maximized:
2677 case lazy.WindowState.Minimized:
2678 await restoreWindow(win);
2682 if (lazy.WindowState.from(win.windowState) != lazy.WindowState.Fullscreen) {
2684 // Use a timed promise to abort if no window manager is present
2685 await new lazy.TimedPromise(
2687 cb = new lazy.DebounceCallback(resolve);
2688 win.addEventListener("sizemodechange", cb);
2689 win.fullScreen = true;
2691 { throws: null, timeout: TIMEOUT_NO_WINDOW_MANAGER }
2693 win.removeEventListener("sizemodechange", cb);
2695 await new lazy.IdlePromise(win);
2697 return this.curBrowser.rect;
2701 * Dismisses a currently displayed modal dialogs, or returns no such alert if
2702 * no modal is displayed.
2704 * @throws {NoSuchAlertError}
2705 * If there is no current user prompt.
2706 * @throws {NoSuchWindowError}
2707 * Top-level browsing context has been discarded.
2709 GeckoDriver.prototype.dismissDialog = async function () {
2710 lazy.assert.open(this.getBrowsingContext({ top: true }));
2711 this._checkIfAlertIsPresent();
2713 const dialogClosed = this.promptListener.dialogClosed();
2714 this.dialog.dismiss();
2717 const win = this.getCurrentWindow();
2718 await new lazy.IdlePromise(win);
2722 * Accepts a currently displayed dialog modal, or returns no such alert if
2723 * no modal is displayed.
2725 * @throws {NoSuchAlertError}
2726 * If there is no current user prompt.
2727 * @throws {NoSuchWindowError}
2728 * Top-level browsing context has been discarded.
2730 GeckoDriver.prototype.acceptDialog = async function () {
2731 lazy.assert.open(this.getBrowsingContext({ top: true }));
2732 this._checkIfAlertIsPresent();
2734 const dialogClosed = this.promptListener.dialogClosed();
2735 this.dialog.accept();
2738 const win = this.getCurrentWindow();
2739 await new lazy.IdlePromise(win);
2743 * Returns the message shown in a currently displayed modal, or returns
2744 * a no such alert error if no modal is currently displayed.
2746 * @throws {NoSuchAlertError}
2747 * If there is no current user prompt.
2748 * @throws {NoSuchWindowError}
2749 * Top-level browsing context has been discarded.
2751 GeckoDriver.prototype.getTextFromDialog = async function () {
2752 lazy.assert.open(this.getBrowsingContext({ top: true }));
2753 this._checkIfAlertIsPresent();
2754 const text = await this.dialog.getText();
2759 * Set the user prompt's value field.
2761 * Sends keys to the input field of a currently displayed modal, or
2762 * returns a no such alert error if no modal is currently displayed. If
2763 * a modal dialog is currently displayed but has no means for text input,
2764 * an element not visible error is returned.
2766 * @param {object} cmd
2767 * @param {string} cmd.parameters.text
2768 * Input to the user prompt's value field.
2770 * @throws {ElementNotInteractableError}
2771 * If the current user prompt is an alert or confirm.
2772 * @throws {NoSuchAlertError}
2773 * If there is no current user prompt.
2774 * @throws {NoSuchWindowError}
2775 * Top-level browsing context has been discarded.
2776 * @throws {UnsupportedOperationError}
2777 * If the current user prompt is something other than an alert,
2778 * confirm, or a prompt.
2780 GeckoDriver.prototype.sendKeysToDialog = async function (cmd) {
2781 lazy.assert.open(this.getBrowsingContext({ top: true }));
2782 this._checkIfAlertIsPresent();
2784 let text = lazy.assert.string(cmd.parameters.text);
2785 let promptType = this.dialog.args.promptType;
2787 switch (promptType) {
2790 throw new lazy.error.ElementNotInteractableError(
2791 `User prompt of type ${promptType} is not interactable`
2796 await this.dismissDialog();
2797 throw new lazy.error.UnsupportedOperationError(
2798 `User prompt of type ${promptType} is not supported`
2801 this.dialog.text = text;
2804 GeckoDriver.prototype._checkIfAlertIsPresent = function () {
2805 if (!this.dialog || !this.dialog.isOpen) {
2806 throw new lazy.error.NoSuchAlertError();
2810 GeckoDriver.prototype._handleUserPrompts = async function () {
2811 if (!this.dialog || !this.dialog.isOpen) {
2815 const textContent = await this.dialog.getText();
2817 const behavior = this.currentSession.unhandledPromptBehavior;
2819 case lazy.UnhandledPromptBehavior.Accept:
2820 await this.acceptDialog();
2823 case lazy.UnhandledPromptBehavior.AcceptAndNotify:
2824 await this.acceptDialog();
2825 throw new lazy.error.UnexpectedAlertOpenError(
2826 `Accepted user prompt dialog: ${textContent}`
2829 case lazy.UnhandledPromptBehavior.Dismiss:
2830 await this.dismissDialog();
2833 case lazy.UnhandledPromptBehavior.DismissAndNotify:
2834 await this.dismissDialog();
2835 throw new lazy.error.UnexpectedAlertOpenError(
2836 `Dismissed user prompt dialog: ${textContent}`
2839 case lazy.UnhandledPromptBehavior.Ignore:
2840 throw new lazy.error.UnexpectedAlertOpenError(
2841 "Encountered unhandled user prompt dialog"
2845 throw new TypeError(`Unknown unhandledPromptBehavior "${behavior}"`);
2850 * Enables or disables accepting new socket connections.
2852 * By calling this method with `false` the server will not accept any
2853 * further connections, but existing connections will not be forcible
2854 * closed. Use `true` to re-enable accepting connections.
2856 * Please note that when closing the connection via the client you can
2857 * end-up in a non-recoverable state if it hasn't been enabled before.
2859 * This method is used for custom in application shutdowns via
2860 * marionette.quit() or marionette.restart(), like File -> Quit.
2862 * @param {object} cmd
2863 * @param {boolean} cmd.parameters.value
2864 * True if the server should accept new socket connections.
2866 GeckoDriver.prototype.acceptConnections = async function (cmd) {
2867 lazy.assert.boolean(cmd.parameters.value);
2868 await this._server.setAcceptConnections(cmd.parameters.value);
2872 * Quits the application with the provided flags.
2874 * Marionette will stop accepting new connections before ending the
2875 * current session, and finally attempting to quit the application.
2877 * Optional {@link nsIAppStartup} flags may be provided as
2878 * an array of masks, and these will be combined by ORing
2879 * them with a bitmask. The available masks are defined in
2880 * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIAppStartup.
2882 * Crucially, only one of the *Quit flags can be specified. The |eRestart|
2883 * flag may be bit-wise combined with one of the *Quit flags to cause
2884 * the application to restart after it quits.
2886 * @param {object} cmd
2887 * @param {Array.<string>=} cmd.parameters.flags
2888 * Constant name of masks to pass to |Services.startup.quit|.
2889 * If empty or undefined, |nsIAppStartup.eAttemptQuit| is used.
2890 * @param {boolean=} cmd.parameters.safeMode
2891 * Optional flag to indicate that the application has to
2892 * be restarted in safe mode.
2894 * @returns {Object<string,boolean>}
2895 * Dictionary containing information that explains the shutdown reason.
2896 * The value for `cause` contains the shutdown kind like "shutdown" or
2897 * "restart", while `forced` will indicate if it was a normal or forced
2898 * shutdown of the application. "in_app" is always set to indicate that
2899 * it is a shutdown triggered from within the application.
2901 * @throws {InvalidArgumentError}
2902 * If <var>flags</var> contains unknown or incompatible flags,
2903 * for example multiple Quit flags.
2905 GeckoDriver.prototype.quit = async function (cmd) {
2906 const { flags = [], safeMode = false } = cmd.parameters;
2908 lazy.assert.array(flags, `Expected "flags" to be an array`);
2909 lazy.assert.boolean(safeMode, `Expected "safeMode" to be a boolean`);
2911 if (safeMode && !flags.includes("eRestart")) {
2912 throw new lazy.error.InvalidArgumentError(
2913 `"safeMode" only works with restart flag`
2917 // Register handler to run Marionette specific shutdown code.
2918 Services.obs.addObserver(this, TOPIC_QUIT_APPLICATION_REQUESTED);
2920 let quitApplicationResponse;
2922 quitApplicationResponse = await lazy.quit(
2925 this.currentSession.capabilities.get("moz:windowless")
2928 if (e instanceof TypeError) {
2929 throw new lazy.error.InvalidArgumentError(e.message);
2932 throw new lazy.error.UnsupportedOperationError(e.message);
2934 Services.obs.removeObserver(this, TOPIC_QUIT_APPLICATION_REQUESTED);
2937 return quitApplicationResponse;
2940 GeckoDriver.prototype.installAddon = function (cmd) {
2941 lazy.assert.desktop();
2943 let path = cmd.parameters.path;
2944 let temp = cmd.parameters.temporary || false;
2946 typeof path == "undefined" ||
2947 typeof path != "string" ||
2948 typeof temp != "boolean"
2950 throw new lazy.error.InvalidArgumentError();
2953 return lazy.Addon.install(path, temp);
2956 GeckoDriver.prototype.uninstallAddon = function (cmd) {
2957 lazy.assert.desktop();
2959 let id = cmd.parameters.id;
2960 if (typeof id == "undefined" || typeof id != "string") {
2961 throw new lazy.error.InvalidArgumentError();
2964 return lazy.Addon.uninstall(id);
2968 * Retrieve the localized string for the specified entity id.
2971 * localizeEntity(["chrome://branding/locale/brand.dtd"], "brandShortName")
2973 * @param {object} cmd
2974 * @param {Array.<string>} cmd.parameters.urls
2975 * Array of .dtd URLs.
2976 * @param {string} cmd.parameters.id
2977 * The ID of the entity to retrieve the localized string for.
2980 * The localized string for the requested entity.
2982 GeckoDriver.prototype.localizeEntity = function (cmd) {
2983 let { urls, id } = cmd.parameters;
2985 if (!Array.isArray(urls)) {
2986 throw new lazy.error.InvalidArgumentError(
2987 "Value of `urls` should be of type 'Array'"
2990 if (typeof id != "string") {
2991 throw new lazy.error.InvalidArgumentError(
2992 "Value of `id` should be of type 'string'"
2996 return lazy.l10n.localizeEntity(urls, id);
3000 * Retrieve the localized string for the specified property id.
3005 * ["chrome://global/locale/findbar.properties"], "FastFind");
3007 * @param {object} cmd
3008 * @param {Array.<string>} cmd.parameters.urls
3009 * Array of .properties URLs.
3010 * @param {string} cmd.parameters.id
3011 * The ID of the property to retrieve the localized string for.
3014 * The localized string for the requested property.
3016 GeckoDriver.prototype.localizeProperty = function (cmd) {
3017 let { urls, id } = cmd.parameters;
3019 if (!Array.isArray(urls)) {
3020 throw new lazy.error.InvalidArgumentError(
3021 "Value of `urls` should be of type 'Array'"
3024 if (typeof id != "string") {
3025 throw new lazy.error.InvalidArgumentError(
3026 "Value of `id` should be of type 'string'"
3030 return lazy.l10n.localizeProperty(urls, id);
3034 * Initialize the reftest mode
3036 GeckoDriver.prototype.setupReftest = async function (cmd) {
3037 if (this._reftest) {
3038 throw new lazy.error.UnsupportedOperationError(
3039 "Called reftest:setup with a reftest session already active"
3045 screenshot = "unexpected",
3048 if (!["always", "fail", "unexpected"].includes(screenshot)) {
3049 throw new lazy.error.InvalidArgumentError(
3050 "Value of `screenshot` should be 'always', 'fail' or 'unexpected'"
3054 this._reftest = new lazy.reftest.Runner(this);
3055 this._reftest.setup(urlCount, screenshot, isPrint);
3058 /** Run a reftest. */
3059 GeckoDriver.prototype.runReftest = function (cmd) {
3060 let { test, references, expected, timeout, width, height, pageRanges } =
3063 if (!this._reftest) {
3064 throw new lazy.error.UnsupportedOperationError(
3065 "Called reftest:run before reftest:start"
3069 lazy.assert.string(test);
3070 lazy.assert.string(expected);
3071 lazy.assert.array(references);
3073 return this._reftest.run(
3085 * End a reftest run.
3087 * Closes the reftest window (without changing the current window handle),
3088 * and removes cached canvases.
3090 GeckoDriver.prototype.teardownReftest = function () {
3091 if (!this._reftest) {
3092 throw new lazy.error.UnsupportedOperationError(
3093 "Called reftest:teardown before reftest:start"
3097 this._reftest.teardown();
3098 this._reftest = null;
3102 * Print page as PDF.
3104 * @param {object} cmd
3105 * @param {boolean=} cmd.parameters.background
3106 * Whether or not to print background colors and images.
3107 * Defaults to false, which prints without background graphics.
3108 * @param {number=} cmd.parameters.margin.bottom
3109 * Bottom margin in cm. Defaults to 1cm (~0.4 inches).
3110 * @param {number=} cmd.parameters.margin.left
3111 * Left margin in cm. Defaults to 1cm (~0.4 inches).
3112 * @param {number=} cmd.parameters.margin.right
3113 * Right margin in cm. Defaults to 1cm (~0.4 inches).
3114 * @param {number=} cmd.parameters.margin.top
3115 * Top margin in cm. Defaults to 1cm (~0.4 inches).
3116 * @param {('landscape'|'portrait')=} cmd.parameters.options.orientation
3117 * Paper orientation. Defaults to 'portrait'.
3118 * @param {Array.<string|number>=} cmd.parameters.pageRanges
3119 * Paper ranges to print, e.g., ['1-5', 8, '11-13'].
3120 * Defaults to the empty array, which means print all pages.
3121 * @param {number=} cmd.parameters.page.height
3122 * Paper height in cm. Defaults to US letter height (27.94cm / 11 inches)
3123 * @param {number=} cmd.parameters.page.width
3124 * Paper width in cm. Defaults to US letter width (21.59cm / 8.5 inches)
3125 * @param {number=} cmd.parameters.scale
3126 * Scale of the webpage rendering. Defaults to 1.0.
3127 * @param {boolean=} cmd.parameters.shrinkToFit
3128 * Whether or not to override page size as defined by CSS.
3129 * Defaults to true, in which case the content will be scaled
3130 * to fit the paper size.
3133 * Base64 encoded PDF representing printed document
3135 * @throws {NoSuchWindowError}
3136 * Top-level browsing context has been discarded.
3137 * @throws {UnexpectedAlertOpenError}
3138 * A modal dialog is open, blocking this operation.
3139 * @throws {UnsupportedOperationError}
3140 * Not available in chrome context.
3142 GeckoDriver.prototype.print = async function (cmd) {
3143 lazy.assert.content(this.context);
3144 lazy.assert.open(this.getBrowsingContext({ top: true }));
3145 await this._handleUserPrompts();
3147 const settings = lazy.print.addDefaultSettings(cmd.parameters);
3148 for (const prop of ["top", "bottom", "left", "right"]) {
3149 lazy.assert.positiveNumber(
3150 settings.margin[prop],
3151 lazy.pprint`margin.${prop} is not a positive number`
3154 for (const prop of ["width", "height"]) {
3155 lazy.assert.positiveNumber(
3156 settings.page[prop],
3157 lazy.pprint`page.${prop} is not a positive number`
3160 lazy.assert.positiveNumber(
3162 `scale ${settings.scale} is not a positive number`
3166 s >= lazy.print.minScaleValue &&
3167 settings.scale <= lazy.print.maxScaleValue,
3168 `scale ${settings.scale} is outside the range ${lazy.print.minScaleValue}-${lazy.print.maxScaleValue}`
3170 lazy.assert.boolean(settings.shrinkToFit);
3172 orientation => lazy.print.defaults.orientationValue.includes(orientation),
3174 settings.orientation
3175 } doesn't match allowed values "${lazy.print.defaults.orientationValue.join(
3178 )(settings.orientation);
3179 lazy.assert.boolean(settings.background);
3180 lazy.assert.array(settings.pageRanges);
3182 const browsingContext = this.curBrowser.tab.linkedBrowser.browsingContext;
3183 const printSettings = await lazy.print.getPrintSettings(settings);
3184 const binaryString = await lazy.print.printToBinaryString(
3189 return btoa(binaryString);
3192 GeckoDriver.prototype.addVirtualAuthenticator = function (cmd) {
3197 hasUserVerification,
3204 "addVirtualAuthenticator: protocol must be a string"
3208 "addVirtualAuthenticator: transport must be a string"
3210 lazy.assert.boolean(
3212 "addVirtualAuthenticator: hasResidentKey must be a boolean"
3214 lazy.assert.boolean(
3215 hasUserVerification,
3216 "addVirtualAuthenticator: hasUserVerification must be a boolean"
3218 lazy.assert.boolean(
3220 "addVirtualAuthenticator: isUserConsenting must be a boolean"
3222 lazy.assert.boolean(
3224 "addVirtualAuthenticator: isUserVerified must be a boolean"
3227 return lazy.webauthn.addVirtualAuthenticator(
3231 hasUserVerification,
3237 GeckoDriver.prototype.removeVirtualAuthenticator = function (cmd) {
3238 const { authenticatorId } = cmd.parameters;
3240 lazy.assert.positiveInteger(
3242 "removeVirtualAuthenticator: authenticatorId must be a positiveInteger"
3245 lazy.webauthn.removeVirtualAuthenticator(authenticatorId);
3248 GeckoDriver.prototype.addCredential = function (cmd) {
3252 isResidentCredential,
3259 lazy.assert.positiveInteger(
3261 "addCredential: authenticatorId must be a positiveInteger"
3265 "addCredential: credentialId must be a string"
3267 lazy.assert.boolean(
3268 isResidentCredential,
3269 "addCredential: isResidentCredential must be a boolean"
3271 lazy.assert.string(rpId, "addCredential: rpId must be a string");
3272 lazy.assert.string(privateKey, "addCredential: privateKey must be a string");
3276 "addCredential: userHandle must be a string if present"
3279 lazy.assert.number(signCount, "addCredential: signCount must be a number");
3281 lazy.webauthn.addCredential(
3284 isResidentCredential,
3292 GeckoDriver.prototype.getCredentials = function (cmd) {
3293 const { authenticatorId } = cmd.parameters;
3295 lazy.assert.positiveInteger(
3297 "getCredentials: authenticatorId must be a positiveInteger"
3300 return lazy.webauthn.getCredentials(authenticatorId);
3303 GeckoDriver.prototype.removeCredential = function (cmd) {
3304 const { authenticatorId, credentialId } = cmd.parameters;
3306 lazy.assert.positiveInteger(
3308 "removeCredential: authenticatorId must be a positiveInteger"
3312 "removeCredential: credentialId must be a string"
3315 lazy.webauthn.removeCredential(authenticatorId, credentialId);
3318 GeckoDriver.prototype.removeAllCredentials = function (cmd) {
3319 const { authenticatorId } = cmd.parameters;
3321 lazy.assert.positiveInteger(
3323 "removeAllCredentials: authenticatorId must be a positiveInteger"
3326 lazy.webauthn.removeAllCredentials(authenticatorId);
3329 GeckoDriver.prototype.setUserVerified = function (cmd) {
3330 const { authenticatorId, isUserVerified } = cmd.parameters;
3332 lazy.assert.positiveInteger(
3334 "setUserVerified: authenticatorId must be a positiveInteger"
3336 lazy.assert.boolean(
3338 "setUserVerified: isUserVerified must be a boolean"
3341 lazy.webauthn.setUserVerified(authenticatorId, isUserVerified);
3344 GeckoDriver.prototype.setPermission = async function (cmd) {
3345 const { descriptor, state, oneRealm = false } = cmd.parameters;
3347 lazy.assert.boolean(oneRealm);
3349 state => ["granted", "denied", "prompt"].includes(state),
3350 `state is ${state}, expected "granted", "denied", or "prompt"`
3353 lazy.permissions.set(descriptor, state, oneRealm);
3357 * Determines the Accessibility label for this element.
3359 * @param {object} cmd
3360 * @param {string} cmd.parameters.id
3361 * Web element reference ID to the element for which the accessibility label
3365 * The Accessibility label for this element
3367 GeckoDriver.prototype.getComputedLabel = async function (cmd) {
3368 lazy.assert.open(this.getBrowsingContext());
3369 await this._handleUserPrompts();
3371 let id = lazy.assert.string(cmd.parameters.id);
3372 let webEl = lazy.WebElement.fromUUID(id).toJSON();
3374 return this.getActor().getComputedLabel(webEl);
3378 * Determines the Accessibility role for this element.
3380 * @param {object} cmd
3381 * @param {string} cmd.parameters.id
3382 * Web element reference ID to the element for which the accessibility role
3386 * The Accessibility role for this element
3388 GeckoDriver.prototype.getComputedRole = async function (cmd) {
3389 lazy.assert.open(this.getBrowsingContext());
3390 await this._handleUserPrompts();
3392 let id = lazy.assert.string(cmd.parameters.id);
3393 let webEl = lazy.WebElement.fromUUID(id).toJSON();
3394 return this.getActor().getComputedRole(webEl);
3397 GeckoDriver.prototype.commands = {
3398 // Marionette service
3399 "Marionette:AcceptConnections": GeckoDriver.prototype.acceptConnections,
3400 "Marionette:GetContext": GeckoDriver.prototype.getContext,
3401 "Marionette:GetScreenOrientation": GeckoDriver.prototype.getScreenOrientation,
3402 "Marionette:GetWindowType": GeckoDriver.prototype.getWindowType,
3403 "Marionette:Quit": GeckoDriver.prototype.quit,
3404 "Marionette:SetContext": GeckoDriver.prototype.setContext,
3405 "Marionette:SetScreenOrientation": GeckoDriver.prototype.setScreenOrientation,
3408 "Addon:Install": GeckoDriver.prototype.installAddon,
3409 "Addon:Uninstall": GeckoDriver.prototype.uninstallAddon,
3412 "L10n:LocalizeEntity": GeckoDriver.prototype.localizeEntity,
3413 "L10n:LocalizeProperty": GeckoDriver.prototype.localizeProperty,
3416 "reftest:setup": GeckoDriver.prototype.setupReftest,
3417 "reftest:run": GeckoDriver.prototype.runReftest,
3418 "reftest:teardown": GeckoDriver.prototype.teardownReftest,
3420 // WebDriver service
3421 "WebDriver:AcceptAlert": GeckoDriver.prototype.acceptDialog,
3422 // deprecated, no longer used since the geckodriver 0.30.0 release
3423 "WebDriver:AcceptDialog": GeckoDriver.prototype.acceptDialog,
3424 "WebDriver:AddCookie": GeckoDriver.prototype.addCookie,
3425 "WebDriver:Back": GeckoDriver.prototype.goBack,
3426 "WebDriver:CloseChromeWindow": GeckoDriver.prototype.closeChromeWindow,
3427 "WebDriver:CloseWindow": GeckoDriver.prototype.close,
3428 "WebDriver:DeleteAllCookies": GeckoDriver.prototype.deleteAllCookies,
3429 "WebDriver:DeleteCookie": GeckoDriver.prototype.deleteCookie,
3430 "WebDriver:DeleteSession": GeckoDriver.prototype.deleteSession,
3431 "WebDriver:DismissAlert": GeckoDriver.prototype.dismissDialog,
3432 "WebDriver:ElementClear": GeckoDriver.prototype.clearElement,
3433 "WebDriver:ElementClick": GeckoDriver.prototype.clickElement,
3434 "WebDriver:ElementSendKeys": GeckoDriver.prototype.sendKeysToElement,
3435 "WebDriver:ExecuteAsyncScript": GeckoDriver.prototype.executeAsyncScript,
3436 "WebDriver:ExecuteScript": GeckoDriver.prototype.executeScript,
3437 "WebDriver:FindElement": GeckoDriver.prototype.findElement,
3438 "WebDriver:FindElementFromShadowRoot":
3439 GeckoDriver.prototype.findElementFromShadowRoot,
3440 "WebDriver:FindElements": GeckoDriver.prototype.findElements,
3441 "WebDriver:FindElementsFromShadowRoot":
3442 GeckoDriver.prototype.findElementsFromShadowRoot,
3443 "WebDriver:Forward": GeckoDriver.prototype.goForward,
3444 "WebDriver:FullscreenWindow": GeckoDriver.prototype.fullscreenWindow,
3445 "WebDriver:GetActiveElement": GeckoDriver.prototype.getActiveElement,
3446 "WebDriver:GetAlertText": GeckoDriver.prototype.getTextFromDialog,
3447 "WebDriver:GetCapabilities": GeckoDriver.prototype.getSessionCapabilities,
3448 "WebDriver:GetComputedLabel": GeckoDriver.prototype.getComputedLabel,
3449 "WebDriver:GetComputedRole": GeckoDriver.prototype.getComputedRole,
3450 "WebDriver:GetCookies": GeckoDriver.prototype.getCookies,
3451 "WebDriver:GetCurrentURL": GeckoDriver.prototype.getCurrentUrl,
3452 "WebDriver:GetElementAttribute": GeckoDriver.prototype.getElementAttribute,
3453 "WebDriver:GetElementCSSValue":
3454 GeckoDriver.prototype.getElementValueOfCssProperty,
3455 "WebDriver:GetElementProperty": GeckoDriver.prototype.getElementProperty,
3456 "WebDriver:GetElementRect": GeckoDriver.prototype.getElementRect,
3457 "WebDriver:GetElementTagName": GeckoDriver.prototype.getElementTagName,
3458 "WebDriver:GetElementText": GeckoDriver.prototype.getElementText,
3459 "WebDriver:GetPageSource": GeckoDriver.prototype.getPageSource,
3460 "WebDriver:GetShadowRoot": GeckoDriver.prototype.getShadowRoot,
3461 "WebDriver:GetTimeouts": GeckoDriver.prototype.getTimeouts,
3462 "WebDriver:GetTitle": GeckoDriver.prototype.getTitle,
3463 "WebDriver:GetWindowHandle": GeckoDriver.prototype.getWindowHandle,
3464 "WebDriver:GetWindowHandles": GeckoDriver.prototype.getWindowHandles,
3465 "WebDriver:GetWindowRect": GeckoDriver.prototype.getWindowRect,
3466 "WebDriver:IsElementDisplayed": GeckoDriver.prototype.isElementDisplayed,
3467 "WebDriver:IsElementEnabled": GeckoDriver.prototype.isElementEnabled,
3468 "WebDriver:IsElementSelected": GeckoDriver.prototype.isElementSelected,
3469 "WebDriver:MinimizeWindow": GeckoDriver.prototype.minimizeWindow,
3470 "WebDriver:MaximizeWindow": GeckoDriver.prototype.maximizeWindow,
3471 "WebDriver:Navigate": GeckoDriver.prototype.navigateTo,
3472 "WebDriver:NewSession": GeckoDriver.prototype.newSession,
3473 "WebDriver:NewWindow": GeckoDriver.prototype.newWindow,
3474 "WebDriver:PerformActions": GeckoDriver.prototype.performActions,
3475 "WebDriver:Print": GeckoDriver.prototype.print,
3476 "WebDriver:Refresh": GeckoDriver.prototype.refresh,
3477 "WebDriver:ReleaseActions": GeckoDriver.prototype.releaseActions,
3478 "WebDriver:SendAlertText": GeckoDriver.prototype.sendKeysToDialog,
3479 "WebDriver:SetPermission": GeckoDriver.prototype.setPermission,
3480 "WebDriver:SetTimeouts": GeckoDriver.prototype.setTimeouts,
3481 "WebDriver:SetWindowRect": GeckoDriver.prototype.setWindowRect,
3482 "WebDriver:SwitchToFrame": GeckoDriver.prototype.switchToFrame,
3483 "WebDriver:SwitchToParentFrame": GeckoDriver.prototype.switchToParentFrame,
3484 "WebDriver:SwitchToWindow": GeckoDriver.prototype.switchToWindow,
3485 "WebDriver:TakeScreenshot": GeckoDriver.prototype.takeScreenshot,
3488 "WebAuthn:AddVirtualAuthenticator":
3489 GeckoDriver.prototype.addVirtualAuthenticator,
3490 "WebAuthn:RemoveVirtualAuthenticator":
3491 GeckoDriver.prototype.removeVirtualAuthenticator,
3492 "WebAuthn:AddCredential": GeckoDriver.prototype.addCredential,
3493 "WebAuthn:GetCredentials": GeckoDriver.prototype.getCredentials,
3494 "WebAuthn:RemoveCredential": GeckoDriver.prototype.removeCredential,
3495 "WebAuthn:RemoveAllCredentials": GeckoDriver.prototype.removeAllCredentials,
3496 "WebAuthn:SetUserVerified": GeckoDriver.prototype.setUserVerified,
3499 async function exitFullscreen(win) {
3501 // Use a timed promise to abort if no window manager is present
3502 await new lazy.TimedPromise(
3504 cb = new lazy.DebounceCallback(resolve);
3505 win.addEventListener("sizemodechange", cb);
3506 win.fullScreen = false;
3508 { throws: null, timeout: TIMEOUT_NO_WINDOW_MANAGER }
3510 win.removeEventListener("sizemodechange", cb);
3511 await new lazy.IdlePromise(win);
3514 async function restoreWindow(win) {
3516 if (lazy.WindowState.from(win.windowState) == lazy.WindowState.Normal) {
3519 // Use a timed promise to abort if no window manager is present
3520 await new lazy.TimedPromise(
3522 cb = new lazy.DebounceCallback(resolve);
3523 win.addEventListener("sizemodechange", cb);
3526 { throws: null, timeout: TIMEOUT_NO_WINDOW_MANAGER }
3528 win.removeEventListener("sizemodechange", cb);
3529 await new lazy.IdlePromise(win);