From 59203c63bb777a3bacec32fb8830fba33540e809 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 12 Jun 2024 07:35:29 +0200 Subject: Adding upstream version 127.0. Signed-off-by: Daniel Baumann --- remote/doc/marionette/SeleniumAtoms.md | 2 - remote/jar.mn | 4 + remote/marionette/accessibility.sys.mjs | 479 --------- .../actors/MarionetteCommandsChild.sys.mjs | 23 +- remote/marionette/atom.sys.mjs | 9 +- remote/marionette/driver.sys.mjs | 52 +- remote/marionette/interaction.sys.mjs | 11 +- remote/marionette/jar.mn | 2 - remote/marionette/permissions.sys.mjs | 95 -- remote/shared/Capture.sys.mjs | 2 +- remote/shared/DOM.sys.mjs | 34 +- remote/shared/Navigate.sys.mjs | 7 +- remote/shared/NetworkRequest.sys.mjs | 254 +++++ remote/shared/NetworkResponse.sys.mjs | 131 +++ remote/shared/Permissions.sys.mjs | 90 ++ remote/shared/listeners/NetworkEventRecord.sys.mjs | 290 +---- remote/shared/listeners/NetworkListener.sys.mjs | 11 +- .../test/browser/browser_NetworkListener.js | 31 +- remote/shared/test/xpcshell/test_DOM.js | 114 +- remote/shared/webdriver/Accessibility.sys.mjs | 519 +++++++++ remote/shared/webdriver/Actions.sys.mjs | 27 +- remote/shared/webdriver/Assert.sys.mjs | 30 + remote/shared/webdriver/Session.sys.mjs | 3 +- .../shared/webdriver/test/xpcshell/test_Assert.js | 14 + .../test/puppeteer/.release-please-manifest.json | 6 +- remote/test/puppeteer/.vscode/launch.template.json | 46 + remote/test/puppeteer/Herebyfile.mjs | 33 +- remote/test/puppeteer/README.md | 216 +--- remote/test/puppeteer/examples/README.md | 14 +- remote/test/puppeteer/moz.yaml | 4 +- remote/test/puppeteer/package-lock.json | 1126 ++++++++++---------- remote/test/puppeteer/package.json | 16 +- .../test/puppeteer/packages/browsers/CHANGELOG.md | 27 + remote/test/puppeteer/packages/browsers/README.md | 24 +- .../test/puppeteer/packages/browsers/package.json | 2 +- remote/test/puppeteer/packages/browsers/src/CLI.ts | 28 +- .../browsers/src/browser-data/browser-data.ts | 111 +- .../packages/browsers/src/browser-data/firefox.ts | 132 ++- .../packages/browsers/src/browser-data/types.ts | 3 + .../puppeteer/packages/browsers/src/fileUtil.ts | 14 +- .../puppeteer/packages/browsers/src/httpUtil.ts | 3 + .../puppeteer/packages/browsers/src/install.ts | 9 + .../test/puppeteer/packages/browsers/src/launch.ts | 69 +- .../browsers/test/src/chrome/install.spec.ts | 2 + .../browsers/test/src/chrome/launch.spec.ts | 1 - .../browsers/test/src/chromium/launch.spec.ts | 1 - .../browsers/test/src/firefox/firefox-data.spec.ts | 71 +- .../packages/browsers/test/src/versions.ts | 2 +- .../puppeteer/packages/ng-schematics/.eslintignore | 2 +- .../puppeteer/packages/ng-schematics/README.md | 2 +- .../puppeteer/packages/puppeteer-core/CHANGELOG.md | 98 ++ .../packages/puppeteer-core/Herebyfile.mjs | 6 +- .../puppeteer/packages/puppeteer-core/package.json | 9 +- .../packages/puppeteer-core/src/api/Browser.ts | 2 +- .../puppeteer-core/src/api/BrowserContext.ts | 2 +- .../packages/puppeteer-core/src/api/CDPSession.ts | 2 +- .../puppeteer-core/src/api/ElementHandle.ts | 22 +- .../packages/puppeteer-core/src/api/Frame.ts | 143 +-- .../packages/puppeteer-core/src/api/HTTPRequest.ts | 226 +++- .../puppeteer-core/src/api/HTTPResponse.ts | 9 +- .../packages/puppeteer-core/src/api/Page.ts | 29 +- .../puppeteer-core/src/api/locators/locators.ts | 23 +- .../puppeteer-core/src/bidi/BidiOverCdp.ts | 4 +- .../packages/puppeteer-core/src/bidi/Browser.ts | 7 +- .../packages/puppeteer-core/src/bidi/CDPSession.ts | 18 +- .../packages/puppeteer-core/src/bidi/Connection.ts | 5 +- .../packages/puppeteer-core/src/bidi/Frame.ts | 34 +- .../puppeteer-core/src/bidi/HTTPRequest.ts | 236 +++- .../puppeteer-core/src/bidi/HTTPResponse.ts | 6 + .../packages/puppeteer-core/src/bidi/Page.ts | 136 ++- .../puppeteer-core/src/bidi/core/Browser.ts | 17 +- .../src/bidi/core/BrowsingContext.ts | 48 +- .../puppeteer-core/src/bidi/core/Connection.ts | 25 + .../puppeteer-core/src/bidi/core/Navigation.ts | 24 +- .../packages/puppeteer-core/src/bidi/core/Realm.ts | 32 +- .../puppeteer-core/src/bidi/core/Request.ts | 90 +- .../puppeteer-core/src/bidi/core/Session.ts | 30 +- .../puppeteer-core/src/bidi/core/UserContext.ts | 10 +- .../puppeteer-core/src/bidi/core/UserPrompt.ts | 7 +- .../puppeteer-core/src/cdp/AriaQueryHandler.ts | 11 +- .../packages/puppeteer-core/src/cdp/Browser.ts | 93 +- .../puppeteer-core/src/cdp/BrowserContext.ts | 104 ++ .../packages/puppeteer-core/src/cdp/Frame.ts | 13 +- .../packages/puppeteer-core/src/cdp/FrameTree.ts | 6 +- .../packages/puppeteer-core/src/cdp/HTTPRequest.ts | 217 +--- .../puppeteer-core/src/cdp/NetworkManager.ts | 21 +- .../packages/puppeteer-core/src/cdp/Page.ts | 18 +- .../puppeteer-core/src/common/CallbackRegistry.ts | 15 +- .../puppeteer-core/src/common/Configuration.ts | 2 +- .../packages/puppeteer-core/src/common/Errors.ts | 4 +- .../puppeteer-core/src/common/PDFOptions.ts | 2 +- .../packages/puppeteer-core/src/common/util.ts | 4 +- .../puppeteer-core/src/node/ChromeLauncher.ts | 25 +- .../src/node/NodeWebSocketTransport.ts | 16 +- .../puppeteer-core/src/node/ProductLauncher.ts | 6 + .../puppeteer-core/src/node/PuppeteerNode.ts | 6 +- .../packages/puppeteer-core/src/revisions.ts | 4 +- .../packages/puppeteer-core/src/util/Function.ts | 2 +- .../test/puppeteer/packages/puppeteer/CHANGELOG.md | 115 ++ .../test/puppeteer/packages/puppeteer/package.json | 7 +- .../packages/puppeteer/src/getConfiguration.ts | 11 + remote/test/puppeteer/test/.eslintrc.js | 6 + remote/test/puppeteer/test/TestExpectations.json | 893 ++++++++++------ remote/test/puppeteer/test/TestSuites.json | 2 +- .../test/golden-chrome/screenshot-element-clip.png | Bin 0 -> 104 bytes .../golden-firefox/screenshot-element-clip.png | Bin 0 -> 104 bytes .../installation/assets/puppeteer-core/launch.js | 6 +- .../test/puppeteer/test/installation/package.json | 4 +- .../installation/src/puppeteer-firefox.spec.ts | 35 + .../puppeteer/test/src/ariaqueryhandler.spec.ts | 11 +- remote/test/puppeteer/test/src/browser.spec.ts | 19 +- .../test/puppeteer/test/src/cdp/CDPSession.spec.ts | 2 +- .../test/puppeteer/test/src/cdp/devtools.spec.ts | 7 +- .../test/puppeteer/test/src/elementhandle.spec.ts | 22 +- remote/test/puppeteer/test/src/frame.spec.ts | 51 +- remote/test/puppeteer/test/src/jshandle.spec.ts | 10 + remote/test/puppeteer/test/src/keyboard.spec.ts | 11 +- remote/test/puppeteer/test/src/launcher.spec.ts | 44 +- remote/test/puppeteer/test/src/navigation.spec.ts | 21 + remote/test/puppeteer/test/src/network.spec.ts | 4 +- remote/test/puppeteer/test/src/page.spec.ts | 21 +- .../src/requestinterception-experimental.spec.ts | 93 +- .../puppeteer/test/src/requestinterception.spec.ts | 61 +- remote/test/puppeteer/test/src/screenshot.spec.ts | 27 + remote/test/puppeteer/test/src/utils.ts | 17 +- remote/test/puppeteer/tools/analyze_issue.mjs | 12 +- remote/test/puppeteer/tools/docgen/package.json | 8 +- .../tools/docgen/src/custom_markdown_documenter.ts | 306 +++--- remote/test/puppeteer/tools/doctest/package.json | 4 +- remote/test/puppeteer/tools/eslint/package.json | 2 +- .../test/puppeteer/tools/mocha-runner/package.json | 2 +- .../puppeteer/tools/sort-test-expectations.mjs | 12 +- .../puppeteer/tools/update_chrome_revision.mjs | 6 + remote/test/puppeteer/versions.js | 8 +- remote/webdriver-bidi/jar.mn | 1 + .../webdriver-bidi/modules/ModuleRegistry.sys.mjs | 2 + .../modules/root/browsingContext.sys.mjs | 128 ++- remote/webdriver-bidi/modules/root/network.sys.mjs | 457 ++++---- .../modules/root/permissions.sys.mjs | 140 +++ .../modules/windowglobal/browsingContext.sys.mjs | 228 ++-- .../modules/windowglobal/input.sys.mjs | 4 + 141 files changed, 5508 insertions(+), 3379 deletions(-) delete mode 100644 remote/marionette/accessibility.sys.mjs delete mode 100644 remote/marionette/permissions.sys.mjs create mode 100644 remote/shared/NetworkRequest.sys.mjs create mode 100644 remote/shared/NetworkResponse.sys.mjs create mode 100644 remote/shared/Permissions.sys.mjs create mode 100644 remote/shared/webdriver/Accessibility.sys.mjs create mode 100644 remote/test/puppeteer/.vscode/launch.template.json create mode 100644 remote/test/puppeteer/packages/puppeteer-core/src/cdp/BrowserContext.ts create mode 100644 remote/test/puppeteer/test/golden-chrome/screenshot-element-clip.png create mode 100644 remote/test/puppeteer/test/golden-firefox/screenshot-element-clip.png create mode 100644 remote/webdriver-bidi/modules/root/permissions.sys.mjs (limited to 'remote') diff --git a/remote/doc/marionette/SeleniumAtoms.md b/remote/doc/marionette/SeleniumAtoms.md index 9f25af46cc..be65cb315e 100644 --- a/remote/doc/marionette/SeleniumAtoms.md +++ b/remote/doc/marionette/SeleniumAtoms.md @@ -10,7 +10,6 @@ Currently the following atoms are in use: - `getElementText` - `isElementDisplayed` -- `isElementEnabled` To use one of those atoms Javascript modules will have to import [atom.sys.mjs]. @@ -56,7 +55,6 @@ commands. Make sure to [install bazelisk] first. ```bash bazel build //javascript/atoms/fragments:get-text bazel build //javascript/atoms/fragments:is-displayed -bazel build //javascript/atoms/fragments:is-enabled ``` For each of the exported atoms a file can now be found in the folder diff --git a/remote/jar.mn b/remote/jar.mn index 7d08451a6f..7431951644 100644 --- a/remote/jar.mn +++ b/remote/jar.mn @@ -23,7 +23,10 @@ remote.jar: content/shared/MobileTabBrowser.sys.mjs (shared/MobileTabBrowser.sys.mjs) content/shared/Navigate.sys.mjs (shared/Navigate.sys.mjs) content/shared/NavigationManager.sys.mjs (shared/NavigationManager.sys.mjs) + content/shared/NetworkRequest.sys.mjs (shared/NetworkRequest.sys.mjs) + content/shared/NetworkResponse.sys.mjs (shared/NetworkResponse.sys.mjs) content/shared/PDF.sys.mjs (shared/PDF.sys.mjs) + content/shared/Permissions.sys.mjs (shared/Permissions.sys.mjs) content/shared/Prompt.sys.mjs (shared/Prompt.sys.mjs) content/shared/Realm.sys.mjs (shared/Realm.sys.mjs) content/shared/RecommendedPreferences.sys.mjs (shared/RecommendedPreferences.sys.mjs) @@ -69,6 +72,7 @@ remote.jar: content/shared/messagehandler/transports/RootTransport.sys.mjs (shared/messagehandler/transports/RootTransport.sys.mjs) # shared modules (WebDriver HTTP / BiDi only) + content/shared/webdriver/Accessibility.sys.mjs (shared/webdriver/Accessibility.sys.mjs) content/shared/webdriver/Actions.sys.mjs (shared/webdriver/Actions.sys.mjs) content/shared/webdriver/Assert.sys.mjs (shared/webdriver/Assert.sys.mjs) content/shared/webdriver/Capabilities.sys.mjs (shared/webdriver/Capabilities.sys.mjs) diff --git a/remote/marionette/accessibility.sys.mjs b/remote/marionette/accessibility.sys.mjs deleted file mode 100644 index c500f2121e..0000000000 --- a/remote/marionette/accessibility.sys.mjs +++ /dev/null @@ -1,479 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const lazy = {}; - -ChromeUtils.defineESModuleGetters(lazy, { - error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", - Log: "chrome://remote/content/shared/Log.sys.mjs", - waitForObserverTopic: "chrome://remote/content/marionette/sync.sys.mjs", -}); - -ChromeUtils.defineLazyGetter(lazy, "logger", () => - lazy.Log.get(lazy.Log.TYPES.MARIONETTE) -); - -ChromeUtils.defineLazyGetter(lazy, "service", () => { - try { - return Cc["@mozilla.org/accessibilityService;1"].getService( - Ci.nsIAccessibilityService - ); - } catch (e) { - lazy.logger.warn("Accessibility module is not present"); - return undefined; - } -}); - -/** @namespace */ -export const accessibility = { - get service() { - return lazy.service; - }, -}; - -/** - * Accessible states used to check element"s state from the accessiblity API - * perspective. - * - * Note: if gecko is built with --disable-accessibility, the interfaces - * are not defined. This is why we use getters instead to be able to use - * these statically. - */ -accessibility.State = { - get Unavailable() { - return Ci.nsIAccessibleStates.STATE_UNAVAILABLE; - }, - get Focusable() { - return Ci.nsIAccessibleStates.STATE_FOCUSABLE; - }, - get Selectable() { - return Ci.nsIAccessibleStates.STATE_SELECTABLE; - }, - get Selected() { - return Ci.nsIAccessibleStates.STATE_SELECTED; - }, -}; - -/** - * Accessible object roles that support some action. - */ -accessibility.ActionableRoles = new Set([ - "checkbutton", - "check menu item", - "check rich option", - "combobox", - "combobox option", - "entry", - "key", - "link", - "listbox option", - "listbox rich option", - "menuitem", - "option", - "outlineitem", - "pagetab", - "pushbutton", - "radiobutton", - "radio menu item", - "rowheader", - "slider", - "spinbutton", - "switch", -]); - -/** - * Factory function that constructs a new {@code accessibility.Checks} - * object with enforced strictness or not. - */ -accessibility.get = function (strict = false) { - return new accessibility.Checks(!!strict); -}; - -/** - * Wait for the document accessibility state to be different from STATE_BUSY. - * - * @param {Document} doc - * The document to wait for. - * @returns {Promise} - * A promise which resolves when the document's accessibility state is no - * longer busy. - */ -function waitForDocumentAccessibility(doc) { - const documentAccessible = accessibility.service.getAccessibleFor(doc); - const state = {}; - documentAccessible.getState(state, {}); - if ((state.value & Ci.nsIAccessibleStates.STATE_BUSY) == 0) { - return Promise.resolve(); - } - - // Accessibility for the doc is busy, so wait for the state to change. - return lazy.waitForObserverTopic("accessible-event", { - checkFn: subject => { - // If event type does not match expected type, skip the event. - // If event's accessible does not match expected accessible, - // skip the event. - const event = subject.QueryInterface(Ci.nsIAccessibleEvent); - return ( - event.eventType === Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE && - event.accessible === documentAccessible - ); - }, - }); -} - -/** - * Retrieve the Accessible for the provided element. - * - * @param {Element} element - * The element for which we need to retrieve the accessible. - * - * @returns {nsIAccessible|null} - * The Accessible object corresponding to the provided element or null if - * the accessibility service is not available. - */ -accessibility.getAccessible = async function (element) { - if (!accessibility.service) { - return null; - } - - // First, wait for accessibility to be ready for the element's document. - await waitForDocumentAccessibility(element.ownerDocument); - - const acc = accessibility.service.getAccessibleFor(element); - if (acc) { - return acc; - } - - // The Accessible doesn't exist yet. This can happen because a11y tree - // mutations happen during refresh driver ticks. Stop the refresh driver from - // doing its regular ticks and force two refresh driver ticks: the first to - // let layout update and notify a11y, and the second to let a11y process - // updates. - const windowUtils = element.ownerGlobal.windowUtils; - windowUtils.advanceTimeAndRefresh(0); - windowUtils.advanceTimeAndRefresh(0); - // Go back to normal refresh driver ticks. - windowUtils.restoreNormalRefresh(); - return accessibility.service.getAccessibleFor(element); -}; - -/** - * Component responsible for interacting with platform accessibility - * API. - * - * Its methods serve as wrappers for testing content and chrome - * accessibility as well as accessibility of user interactions. - */ -accessibility.Checks = class { - /** - * @param {boolean} strict - * Flag indicating whether the accessibility issue should be logged - * or cause an error to be thrown. Default is to log to stdout. - */ - constructor(strict) { - this.strict = strict; - } - - /** - * Assert that the element has a corresponding accessible object, and retrieve - * this accessible. Note that if the accessibility.Checks component was - * created in non-strict mode, this helper will not attempt to resolve the - * accessible at all and will simply return null. - * - * @param {DOMElement|XULElement} element - * Element to get the accessible object for. - * @param {boolean=} mustHaveAccessible - * Flag indicating that the element must have an accessible object. - * Defaults to not require this. - * - * @returns {Promise.} - * Promise with an accessibility object for the given element. - */ - async assertAccessible(element, mustHaveAccessible = false) { - if (!this.strict) { - return null; - } - - const accessible = await accessibility.getAccessible(element); - if (!accessible && mustHaveAccessible) { - this.error("Element does not have an accessible object", element); - } - - return accessible; - } - - /** - * Test if the accessible has a role that supports some arbitrary - * action. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @returns {boolean} - * True if an actionable role is found on the accessible, false - * otherwise. - */ - isActionableRole(accessible) { - return accessibility.ActionableRoles.has( - accessibility.service.getStringRole(accessible.role) - ); - } - - /** - * Test if an accessible has at least one action that it supports. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @returns {boolean} - * True if the accessible has at least one supported action, - * false otherwise. - */ - hasActionCount(accessible) { - return accessible.actionCount > 0; - } - - /** - * Test if an accessible has a valid name. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @returns {boolean} - * True if the accessible has a non-empty valid name, or false if - * this is not the case. - */ - hasValidName(accessible) { - return accessible.name && accessible.name.trim(); - } - - /** - * Test if an accessible has a {@code hidden} attribute. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @returns {boolean} - * True if the accessible object has a {@code hidden} attribute, - * false otherwise. - */ - hasHiddenAttribute(accessible) { - let hidden = false; - try { - hidden = accessible.attributes.getStringProperty("hidden"); - } catch (e) {} - // if the property is missing, error will be thrown - return hidden && hidden === "true"; - } - - /** - * Verify if an accessible has a given state. - * Test if an accessible has a given state. - * - * @param {nsIAccessible} accessible - * Accessible object to test. - * @param {number} stateToMatch - * State to match. - * - * @returns {boolean} - * True if |accessible| has |stateToMatch|, false otherwise. - */ - matchState(accessible, stateToMatch) { - let state = {}; - accessible.getState(state, {}); - return !!(state.value & stateToMatch); - } - - /** - * Test if an accessible is hidden from the user. - * - * @param {nsIAccessible} accessible - * Accessible object. - * - * @returns {boolean} - * True if element is hidden from user, false otherwise. - */ - isHidden(accessible) { - if (!accessible) { - return true; - } - - while (accessible) { - if (this.hasHiddenAttribute(accessible)) { - return true; - } - accessible = accessible.parent; - } - return false; - } - - /** - * Test if the element's visible state corresponds to its accessibility - * API visibility. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * @param {boolean} visible - * Visibility state of |element|. - * - * @throws ElementNotAccessibleError - * If |element|'s visibility state does not correspond to - * |accessible|'s. - */ - assertVisible(accessible, element, visible) { - let hiddenAccessibility = this.isHidden(accessible); - - let message; - if (visible && hiddenAccessibility) { - message = - "Element is not currently visible via the accessibility API " + - "and may not be manipulated by it"; - } else if (!visible && !hiddenAccessibility) { - message = - "Element is currently only visible via the accessibility API " + - "and can be manipulated by it"; - } - this.error(message, element); - } - - /** - * Test if the element's unavailable accessibility state matches the - * enabled state. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * @param {boolean} enabled - * Enabled state of |element|. - * - * @throws ElementNotAccessibleError - * If |element|'s enabled state does not match |accessible|'s. - */ - assertEnabled(accessible, element, enabled) { - if (!accessible) { - return; - } - - let win = element.ownerGlobal; - let disabledAccessibility = this.matchState( - accessible, - accessibility.State.Unavailable - ); - let explorable = - win.getComputedStyle(element).getPropertyValue("pointer-events") !== - "none"; - - let message; - if (!explorable && !disabledAccessibility) { - message = - "Element is enabled but is not explorable via the " + - "accessibility API"; - } else if (enabled && disabledAccessibility) { - message = "Element is enabled but disabled via the accessibility API"; - } else if (!enabled && !disabledAccessibility) { - message = "Element is disabled but enabled via the accessibility API"; - } - this.error(message, element); - } - - /** - * Test if it is possible to activate an element with the accessibility - * API. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * - * @throws ElementNotAccessibleError - * If it is impossible to activate |element| with |accessible|. - */ - assertActionable(accessible, element) { - if (!accessible) { - return; - } - - let message; - if (!this.hasActionCount(accessible)) { - message = "Element does not support any accessible actions"; - } else if (!this.isActionableRole(accessible)) { - message = - "Element does not have a correct accessibility role " + - "and may not be manipulated via the accessibility API"; - } else if (!this.hasValidName(accessible)) { - message = "Element is missing an accessible name"; - } else if (!this.matchState(accessible, accessibility.State.Focusable)) { - message = "Element is not focusable via the accessibility API"; - } - - this.error(message, element); - } - - /** - * Test that an element's selected state corresponds to its - * accessibility API selected state. - * - * @param {nsIAccessible} accessible - * Accessible object. - * @param {DOMElement|XULElement} element - * Element associated with |accessible|. - * @param {boolean} selected - * The |element|s selected state. - * - * @throws ElementNotAccessibleError - * If |element|'s selected state does not correspond to - * |accessible|'s. - */ - assertSelected(accessible, element, selected) { - if (!accessible) { - return; - } - - // element is not selectable via the accessibility API - if (!this.matchState(accessible, accessibility.State.Selectable)) { - return; - } - - let selectedAccessibility = this.matchState( - accessible, - accessibility.State.Selected - ); - - let message; - if (selected && !selectedAccessibility) { - message = - "Element is selected but not selected via the accessibility API"; - } else if (!selected && selectedAccessibility) { - message = - "Element is not selected but selected via the accessibility API"; - } - this.error(message, element); - } - - /** - * Throw an error if strict accessibility checks are enforced and log - * the error to the log. - * - * @param {string} message - * @param {DOMElement|XULElement} element - * Element that caused an error. - * - * @throws ElementNotAccessibleError - * If |strict| is true. - */ - error(message, element) { - if (!message || !this.strict) { - return; - } - if (element) { - let { id, tagName, className } = element; - message += `: id: ${id}, tagName: ${tagName}, className: ${className}`; - } - - throw new lazy.error.ElementNotAccessibleError(message); - } -}; diff --git a/remote/marionette/actors/MarionetteCommandsChild.sys.mjs b/remote/marionette/actors/MarionetteCommandsChild.sys.mjs index d454e03fb0..e4db24b1e6 100644 --- a/remote/marionette/actors/MarionetteCommandsChild.sys.mjs +++ b/remote/marionette/actors/MarionetteCommandsChild.sys.mjs @@ -7,7 +7,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - accessibility: "chrome://remote/content/marionette/accessibility.sys.mjs", + accessibility: + "chrome://remote/content/shared/webdriver/Accessibility.sys.mjs", action: "chrome://remote/content/shared/webdriver/Actions.sys.mjs", atom: "chrome://remote/content/marionette/atom.sys.mjs", dom: "chrome://remote/content/shared/DOM.sys.mjs", @@ -282,17 +283,7 @@ export class MarionetteCommandsChild extends JSWindowActorChild { async getComputedLabel(options = {}) { const { elem } = options; - const accessible = await lazy.accessibility.getAccessible(elem); - if (!accessible) { - return ""; - } - - // If name is null (absent), expose the empty string. - if (accessible.name === null) { - return ""; - } - - return accessible.name; + return lazy.accessibility.getAccessibleName(elem); } /** @@ -301,13 +292,7 @@ export class MarionetteCommandsChild extends JSWindowActorChild { async getComputedRole(options = {}) { const { elem } = options; - const accessible = await lazy.accessibility.getAccessible(elem); - if (!accessible) { - // If it's not in the a11y tree, it's probably presentational. - return "none"; - } - - return accessible.computedARIARole; + return lazy.accessibility.getComputedRole(elem); } /** diff --git a/remote/marionette/atom.sys.mjs b/remote/marionette/atom.sys.mjs index d065849d43..35840cac24 100644 --- a/remote/marionette/atom.sys.mjs +++ b/remote/marionette/atom.sys.mjs @@ -25,11 +25,10 @@ export const atom = {}; // Follow the instructions to export all the atoms: // https://firefox-source-docs.mozilla.org/testing/marionette/SeleniumAtoms.html // -// Built from SHA1: bd5cbe5b3a3e60b5970d8168474dd69a996c392c +// Built from SHA1: 33c6b7841a59aaaad55744909c0600f066fd5593 const ATOMS = { - getVisibleText: "function(){return (function(){var k=this||self;function aa(a){return\"string\"==typeof a}function ba(a,b){a=a.split(\".\");var c=k;a[0]in c||\"undefined\"==typeof c.execScript||c.execScript(\"var \"+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}\nfunction ca(a){var b=typeof a;if(\"object\"==b)if(a){if(a instanceof Array)return\"array\";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(\"[object Window]\"==c)return\"object\";if(\"[object Array]\"==c||\"number\"==typeof a.length&&\"undefined\"!=typeof a.splice&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"splice\"))return\"array\";if(\"[object Function]\"==c||\"undefined\"!=typeof a.call&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"call\"))return\"function\"}else return\"null\";\nelse if(\"function\"==b&&\"undefined\"==typeof a.call)return\"object\";return b}function da(a,b,c){return a.call.apply(a.bind,arguments)}function ea(a,b,c){if(!a)throw Error();if(2b?null:\"string\"===typeof a?a.charAt(b):a[b]}function qa(a){return Array.prototype.concat.apply([],arguments)}function ra(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};function sa(a){var b=a.length-1;return 0<=b&&a.indexOf(\" \",b)==b}var ta=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\\s\\xa0]*([\\s\\S]*?)[\\s\\xa0]*$/.exec(a)[1]};function ua(a,b){return ab?1:0};var r;a:{var va=k.navigator;if(va){var wa=va.userAgent;if(wa){r=wa;break a}}r=\"\"}function u(a){return-1!=r.indexOf(a)};function xa(){return u(\"Firefox\")||u(\"FxiOS\")}function ya(){return(u(\"Chrome\")||u(\"CriOS\"))&&!u(\"Edge\")};function za(a){return String(a).replace(/\\-([a-z])/g,function(b,c){return c.toUpperCase()})};function Aa(){return u(\"iPhone\")&&!u(\"iPod\")&&!u(\"iPad\")};function Ba(a,b){var c=Ca;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var Da=u(\"Opera\"),w=u(\"Trident\")||u(\"MSIE\"),Ea=u(\"Edge\"),Fa=u(\"Gecko\")&&!(-1!=r.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\"))&&!(u(\"Trident\")||u(\"MSIE\"))&&!u(\"Edge\"),Ga=-1!=r.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\");function Ha(){var a=k.document;return a?a.documentMode:void 0}var Ia;\na:{var Ja=\"\",Ka=function(){var a=r;if(Fa)return/rv:([^\\);]+)(\\)|;)/.exec(a);if(Ea)return/Edge\\/([\\d\\.]+)/.exec(a);if(w)return/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(a);if(Ga)return/WebKit\\/(\\S+)/.exec(a);if(Da)return/(?:Version)[ \\/]?(\\S+)/.exec(a)}();Ka&&(Ja=Ka?Ka[1]:\"\");if(w){var La=Ha();if(null!=La&&La>parseFloat(Ja)){Ia=String(La);break a}}Ia=Ja}var Ca={};\nfunction Ma(a){return Ba(a,function(){for(var b=0,c=ta(String(Ia)).split(\".\"),d=ta(String(a)).split(\".\"),e=Math.max(c.length,d.length),f=0;0==b&&f]=|\\s+|./g,Ua=/^\\s/;function y(a,b){return a.b[a.a+(b||0)]}function z(a){return a.b[a.a++]}function Va(a){return a.b.length<=a.a};function Wa(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}Wa.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};Wa.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};Wa.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function Xa(a,b){this.width=a;this.height=b}Xa.prototype.aspectRatio=function(){return this.width/this.height};Xa.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};Xa.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};Xa.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function Ya(a){for(;a&&1!=a.nodeType;)a=a.previousSibling;return a}function Za(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if(\"undefined\"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}\nfunction $a(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(w&&!(9<=Number(Na))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if(\"sourceIndex\"in a||a.parentNode&&\"sourceIndex\"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?ab(a,b):!c&&Za(e,b)?-1*bb(a,b):!d&&Za(f,a)?bb(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=A(a);c=d.createRange();\nc.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(k.Range.START_TO_END,a)}function bb(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return ab(b,a)}function ab(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function A(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function cb(a,b){a&&(a=a.parentNode);for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}\nfunction db(a){this.a=a||k.document||document}db.prototype.getElementsByTagName=function(a,b){return(b||this.a).getElementsByTagName(String(a))};function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?\"\":b);if(\"string\"!=typeof b)if(x&&\"title\"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b=\"\";a;){do 1!=a.nodeType&&(b+=a.nodeValue),x&&\"title\"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}\nfunction C(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Oa&&\"class\"==b&&(b=\"className\");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function eb(a,b,c,d,e){return(x?fb:gb).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new E)}\nfunction fb(a,b,c,d,e){if(a instanceof F||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=ib(a);if(\"*\"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],h=0;b=f[h++];)C(b,c,d)&&g.push(b);f=g}for(h=0;b=f[h++];)\"*\"==a&&\"!\"==b.tagName||e.add(b);return e}jb(a,b,c,d,e);return e}\nfunction gb(a,b,c,d,e){b.getElementsByName&&d&&\"name\"==c&&!w?(b=b.getElementsByName(d),p(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&\"class\"==c?(b=b.getElementsByClassName(d),p(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof G?jb(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),p(b,function(f){C(f,c,d)&&e.add(f)}));return e}\nfunction kb(a,b,c,d,e){var f;if((a instanceof F||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=ib(a);if(\"*\"!=g&&(f=ka(f,function(h){return h.tagName&&h.tagName.toLowerCase()==g}),!f))return e;c&&(f=ka(f,function(h){return C(h,c,d)}));p(f,function(h){\"*\"==g&&(\"!\"==h.tagName||\"*\"==g&&1!=h.nodeType)||e.add(h)});return e}return lb(a,b,c,d,e)}function lb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b);return e}\nfunction jb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b),jb(a,b,c,d,e)}function ib(a){if(a instanceof G){if(8==a.b)return\"!\";if(null===a.b)return\"*\"}return a.f()};function E(){this.b=this.a=null;this.l=0}function mb(a){this.f=a;this.a=this.b=null}function nb(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof Pa&&g instanceof Pa&&e.a==g.a?(e=c,c=c.a,b=b.a):0<$a(c.f,b.f)?(e=b,b=b.a):(e=c,c=c.a);(e.b=d)?d.a=e:a.a=e;d=e;f++}for(e=c||b;e;)e.b=d,d=d.a=e,f++,e=e.a;a.b=d;a.l=f;return a}function ob(a,b){b=new mb(b);b.a=a.a;a.b?a.a.b=b:a.a=a.b=b;a.a=b;a.l++}\nE.prototype.add=function(a){a=new mb(a);a.b=this.b;this.a?this.b.a=a:this.a=this.b=a;this.b=a;this.l++};function pb(a){return(a=a.a)?a.f:null}function qb(a){return(a=pb(a))?B(a):\"\"}function H(a,b){return new rb(a,!!b)}function rb(a,b){this.f=a;this.b=(this.s=b)?a.b:a.a;this.a=null}function I(a){var b=a.b;if(null==b)return null;var c=a.a=b;a.b=a.s?b.b:b.a;return c.f};function J(a){this.i=a;this.b=this.g=!1;this.f=null}function K(a){return\"\\n \"+a.toString().split(\"\\n\").join(\"\\n \")}function sb(a,b){a.g=b}function tb(a,b){a.b=b}function L(a,b){a=a.a(b);return a instanceof E?+qb(a):+a}function O(a,b){a=a.a(b);return a instanceof E?qb(a):\"\"+a}function ub(a,b){a=a.a(b);return a instanceof E?!!a.l:!!a};function vb(a,b,c){J.call(this,a.i);this.c=a;this.h=b;this.o=c;this.g=b.g||c.g;this.b=b.b||c.b;this.c==wb&&(c.b||c.g||4==c.i||0==c.i||!b.f?b.b||b.g||4==b.i||0==b.i||!c.f||(this.f={name:c.f.name,u:b}):this.f={name:b.f.name,u:c})}m(vb,J);\nfunction xb(a,b,c,d,e){b=b.a(d);c=c.a(d);var f;if(b instanceof E&&c instanceof E){b=H(b);for(d=I(b);d;d=I(b))for(e=H(c),f=I(e);f;f=I(e))if(a(B(d),B(f)))return!0;return!1}if(b instanceof E||c instanceof E){b instanceof E?(e=b,d=c):(e=c,d=b);f=H(e);for(var g=typeof d,h=I(f);h;h=I(f)){switch(g){case \"number\":h=+B(h);break;case \"boolean\":h=!!B(h);break;case \"string\":h=B(h);break;default:throw Error(\"Illegal primitive type for comparison.\");}if(e==b&&a(h,d)||e==c&&a(d,h))return!0}return!1}return e?\"boolean\"==\ntypeof b||\"boolean\"==typeof c?a(!!b,!!c):\"number\"==typeof b||\"number\"==typeof c?a(+b,+c):a(b,c):a(+b,+c)}vb.prototype.a=function(a){return this.c.m(this.h,this.o,a)};vb.prototype.toString=function(){var a=\"Binary Expression: \"+this.c;a+=K(this.h);return a+=K(this.o)};function yb(a,b,c,d){this.I=a;this.D=b;this.i=c;this.m=d}yb.prototype.toString=function(){return this.I};var zb={};\nfunction P(a,b,c,d){if(zb.hasOwnProperty(a))throw Error(\"Binary operator already created: \"+a);a=new yb(a,b,c,d);return zb[a.toString()]=a}P(\"div\",6,1,function(a,b,c){return L(a,c)/L(b,c)});P(\"mod\",6,1,function(a,b,c){return L(a,c)%L(b,c)});P(\"*\",6,1,function(a,b,c){return L(a,c)*L(b,c)});P(\"+\",5,1,function(a,b,c){return L(a,c)+L(b,c)});P(\"-\",5,1,function(a,b,c){return L(a,c)-L(b,c)});P(\"<\",4,2,function(a,b,c){return xb(function(d,e){return d\",4,2,function(a,b,c){return xb(function(d,e){return d>e},a,b,c)});P(\"<=\",4,2,function(a,b,c){return xb(function(d,e){return d<=e},a,b,c)});P(\">=\",4,2,function(a,b,c){return xb(function(d,e){return d>=e},a,b,c)});var wb=P(\"=\",3,2,function(a,b,c){return xb(function(d,e){return d==e},a,b,c,!0)});P(\"!=\",3,2,function(a,b,c){return xb(function(d,e){return d!=e},a,b,c,!0)});P(\"and\",2,2,function(a,b,c){return ub(a,c)&&ub(b,c)});P(\"or\",1,2,function(a,b,c){return ub(a,c)||ub(b,c)});function Ab(a,b){if(b.a.length&&4!=a.i)throw Error(\"Primary expression must evaluate to nodeset if filter has predicate(s).\");J.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}m(Ab,J);Ab.prototype.a=function(a){a=this.c.a(a);return Bb(this.h,a)};Ab.prototype.toString=function(){var a=\"Filter:\"+K(this.c);return a+=K(this.h)};function Cb(a,b){if(b.lengtha.B)throw Error(\"Function \"+a.j+\" expects at most \"+a.B+\" arguments, \"+b.length+\" given\");a.H&&p(b,function(c,d){if(4!=c.i)throw Error(\"Argument \"+d+\" to function \"+a.j+\" is not of type Nodeset: \"+c);});J.call(this,a.i);this.v=a;this.c=b;sb(this,a.g||na(b,function(c){return c.g}));tb(this,a.G&&!b.length||a.F&&!!b.length||na(b,function(c){return c.b}))}\nm(Cb,J);Cb.prototype.a=function(a){return this.v.m.apply(null,qa(a,this.c))};Cb.prototype.toString=function(){var a=\"Function: \"+this.v;if(this.c.length){var b=ma(this.c,function(c,d){return c+K(d)},\"Arguments:\");a+=K(b)}return a};function Db(a,b,c,d,e,f,g,h){this.j=a;this.i=b;this.g=c;this.G=d;this.F=!1;this.m=e;this.C=f;this.B=void 0!==g?g:f;this.H=!!h}Db.prototype.toString=function(){return this.j};var Eb={};\nfunction Q(a,b,c,d,e,f,g,h){if(Eb.hasOwnProperty(a))throw Error(\"Function already created: \"+a+\".\");Eb[a]=new Db(a,b,c,d,e,f,g,h)}Q(\"boolean\",2,!1,!1,function(a,b){return ub(b,a)},1);Q(\"ceiling\",1,!1,!1,function(a,b){return Math.ceil(L(b,a))},1);Q(\"concat\",3,!1,!1,function(a,b){return ma(ra(arguments,1),function(c,d){return c+O(d,a)},\"\")},2,null);Q(\"contains\",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return-1!=b.indexOf(a)},2);Q(\"count\",1,!1,!1,function(a,b){return b.a(a).l},1,1,!0);\nQ(\"false\",2,!1,!1,function(){return!1},0);Q(\"floor\",1,!1,!1,function(a,b){return Math.floor(L(b,a))},1);Q(\"id\",4,!1,!1,function(a,b){function c(h){if(x){var l=e.all[h];if(l){if(l.nodeType&&h==l.id)return l;if(l.length)return pa(l,function(v){return h==v.id})}return null}return e.getElementById(h)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=O(b,a).split(/\\s+/);var f=[];p(a,function(h){h=c(h);!h||0<=ja(f,h)||f.push(h)});f.sort($a);var g=new E;p(f,function(h){g.add(h)});return g},1);\nQ(\"lang\",2,!1,!1,function(){return!1},1);Q(\"last\",1,!0,!1,function(a){if(1!=arguments.length)throw Error(\"Function last expects ()\");return a.f},0);Q(\"local-name\",3,!1,!0,function(a,b){return(a=b?pb(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():\"\"},0,1,!0);Q(\"name\",3,!1,!0,function(a,b){return(a=b?pb(b.a(a)):a.a)?a.nodeName.toLowerCase():\"\"},0,1,!0);Q(\"namespace-uri\",3,!0,!1,function(){return\"\"},0,1,!0);\nQ(\"normalize-space\",3,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).replace(/[\\s\\xa0]+/g,\" \").replace(/^\\s+|\\s+$/g,\"\")},0,1);Q(\"not\",2,!1,!1,function(a,b){return!ub(b,a)},1);Q(\"number\",1,!1,!0,function(a,b){return b?L(b,a):+B(a.a)},0,1);Q(\"position\",1,!0,!1,function(a){return a.b},0);Q(\"round\",1,!1,!1,function(a,b){return Math.round(L(b,a))},1);Q(\"starts-with\",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return 0==b.lastIndexOf(a,0)},2);Q(\"string\",3,!1,!0,function(a,b){return b?O(b,a):B(a.a)},0,1);\nQ(\"string-length\",1,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).length},0,1);Q(\"substring\",3,!1,!1,function(a,b,c,d){c=L(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return\"\";d=d?L(d,a):Infinity;if(isNaN(d)||-Infinity===d)return\"\";c=Math.round(c)-1;var e=Math.max(c,0);a=O(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);Q(\"substring-after\",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);c=b.indexOf(a);return-1==c?\"\":b.substring(c+a.length)},2);\nQ(\"substring-before\",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);a=b.indexOf(a);return-1==a?\"\":b.substring(0,a)},2);Q(\"sum\",1,!1,!1,function(a,b){a=H(b.a(a));b=0;for(var c=I(a);c;c=I(a))b+=+B(c);return b},1,1,!0);Q(\"translate\",3,!1,!1,function(a,b,c,d){b=O(b,a);c=O(c,a);var e=O(d,a);a={};for(d=0;da.length)throw Error(\"Unclosed literal string\");return new Gb(a)}\nfunction fc(a){var b=[];if(Mb(y(a.a))){var c=z(a.a);var d=y(a.a);if(\"/\"==c&&(Va(a.a)||\".\"!=d&&\"..\"!=d&&\"@\"!=d&&\"*\"!=d&&!/(?![0-9])[\\w]/.test(d)))return new Kb;d=new Kb;T(a,\"Missing next location step.\");c=gc(a,c);b.push(c)}else{a:{c=y(a.a);d=c.charAt(0);switch(d){case \"$\":throw Error(\"Variable reference not allowed in HTML XPath\");case \"(\":z(a.a);c=Zb(a);T(a,'unclosed \"(\"');cc(a,\")\");break;case '\"':case \"'\":c=ec(a);break;default:if(isNaN(+c))if(!Fb(c)&&/(?![0-9])[\\w]/.test(d)&&\"(\"==y(a.a,1)){c=z(a.a);\nc=Eb[c]||null;z(a.a);for(d=[];\")\"!=y(a.a);){T(a,\"Missing function argument list.\");d.push(Zb(a));if(\",\"!=y(a.a))break;z(a.a)}T(a,\"Unclosed function argument list.\");dc(a);c=new Cb(c,d)}else{c=null;break a}else c=new Hb(+z(a.a))}\"[\"==y(a.a)&&(d=new Pb(hc(a)),c=new Ab(c,d))}if(c)if(Mb(y(a.a)))d=c;else return c;else c=gc(a,\"/\"),d=new Lb,b.push(c)}for(;Mb(y(a.a));)c=z(a.a),T(a,\"Missing next location step.\"),c=gc(a,c),b.push(c);return new Ib(d,b)}\nfunction gc(a,b){if(\"/\"!=b&&\"//\"!=b)throw Error('Step op should be \"/\" or \"//\"');if(\".\"==y(a.a)){var c=new R(Vb,new G(\"node\"));z(a.a);return c}if(\"..\"==y(a.a))return c=new R(Ub,new G(\"node\")),z(a.a),c;if(\"@\"==y(a.a)){var d=Jb;z(a.a);T(a,\"Missing attribute name\")}else if(\"::\"==y(a.a,1)){if(!/(?![0-9])[\\w]/.test(y(a.a).charAt(0)))throw Error(\"Bad token: \"+z(a.a));var e=z(a.a);d=Tb[e]||null;if(!d)throw Error(\"No axis with name: \"+e);z(a.a);T(a,\"Missing node name\")}else d=Qb;e=y(a.a);if(/(?![0-9])[\\w\\*]/.test(e.charAt(0)))if(\"(\"==\ny(a.a,1)){if(!Fb(e))throw Error(\"Invalid node type: \"+e);e=z(a.a);if(!Fb(e))throw Error(\"Invalid type name: \"+e);cc(a,\"(\");T(a,\"Bad nodetype\");var f=y(a.a).charAt(0),g=null;if('\"'==f||\"'\"==f)g=ec(a);T(a,\"Bad nodetype\");dc(a);e=new G(e,g)}else if(e=z(a.a),f=e.indexOf(\":\"),-1==f)e=new F(e);else{g=e.substring(0,f);if(\"*\"==g)var h=\"*\";else if(h=a.b(g),!h)throw Error(\"Namespace prefix not declared: \"+g);e=e.substr(f+1);e=new F(e,h)}else throw Error(\"Bad token: \"+z(a.a));a=new Pb(hc(a),d.s);return c||new R(d,\ne,a,\"//\"==b)}function hc(a){for(var b=[];\"[\"==y(a.a);){z(a.a);T(a,\"Missing predicate expression.\");var c=Zb(a);b.push(c);T(a,\"Unclosed predicate expression.\");cc(a,\"]\")}return b}function bc(a){if(\"-\"==y(a.a))return z(a.a),new Wb(bc(a));var b=fc(a);if(\"|\"!=y(a.a))a=b;else{for(b=[b];\"|\"==z(a.a);)T(a,\"Missing next union location path.\"),b.push(fc(a));a.a.a--;a=new Xb(b)}return a};function ic(a){switch(a.nodeType){case 1:return ha(jc,a);case 9:return ic(a.documentElement);case 11:case 10:case 6:case 12:return kc;default:return a.parentNode?ic(a.parentNode):kc}}function kc(){return null}function jc(a,b){if(a.prefix==b)return a.namespaceURI||\"http://www.w3.org/1999/xhtml\";var c=a.getAttributeNode(\"xmlns:\"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?jc(a.parentNode,b):null};function lc(a,b){if(!a.length)throw Error(\"Empty XPath expression.\");a=Sa(a);if(Va(a))throw Error(\"Invalid XPath expression.\");b?\"function\"==ca(b)||(b=fa(b.lookupNamespaceURI,b)):b=function(){return null};var c=Zb(new Yb(a,b));if(!Va(a))throw Error(\"Bad token: \"+z(a));this.evaluate=function(d,e){d=c.a(new ia(d));return new U(d,e)}}\nfunction U(a,b){if(0==b)if(a instanceof E)b=4;else if(\"string\"==typeof a)b=2;else if(\"number\"==typeof a)b=1;else if(\"boolean\"==typeof a)b=3;else throw Error(\"Unexpected evaluation result.\");if(2!=b&&1!=b&&3!=b&&!(a instanceof E))throw Error(\"value could not be converted to the specified type\");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof E?qb(a):\"\"+a;break;case 1:this.numberValue=a instanceof E?+qb(a):+a;break;case 3:this.booleanValue=a instanceof E?0=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error(\"snapshotItem called with wrong result type\");return g>=d.length||\n0>g?null:d[g]}}U.ANY_TYPE=0;U.NUMBER_TYPE=1;U.STRING_TYPE=2;U.BOOLEAN_TYPE=3;U.UNORDERED_NODE_ITERATOR_TYPE=4;U.ORDERED_NODE_ITERATOR_TYPE=5;U.UNORDERED_NODE_SNAPSHOT_TYPE=6;U.ORDERED_NODE_SNAPSHOT_TYPE=7;U.ANY_UNORDERED_NODE_TYPE=8;U.FIRST_ORDERED_NODE_TYPE=9;function mc(a){this.lookupNamespaceURI=ic(a)}\nfunction nc(a,b){a=a||k;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=U,c.evaluate=function(d,e,f,g){return(new lc(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new lc(d,e)},c.createNSResolver=function(d){return new mc(d)}}ba(\"wgxpath.install\",nc);ba(\"wgxpath.install\",nc);var oc={aliceblue:\"#f0f8ff\",antiquewhite:\"#faebd7\",aqua:\"#00ffff\",aquamarine:\"#7fffd4\",azure:\"#f0ffff\",beige:\"#f5f5dc\",bisque:\"#ffe4c4\",black:\"#000000\",blanchedalmond:\"#ffebcd\",blue:\"#0000ff\",blueviolet:\"#8a2be2\",brown:\"#a52a2a\",burlywood:\"#deb887\",cadetblue:\"#5f9ea0\",chartreuse:\"#7fff00\",chocolate:\"#d2691e\",coral:\"#ff7f50\",cornflowerblue:\"#6495ed\",cornsilk:\"#fff8dc\",crimson:\"#dc143c\",cyan:\"#00ffff\",darkblue:\"#00008b\",darkcyan:\"#008b8b\",darkgoldenrod:\"#b8860b\",darkgray:\"#a9a9a9\",darkgreen:\"#006400\",\ndarkgrey:\"#a9a9a9\",darkkhaki:\"#bdb76b\",darkmagenta:\"#8b008b\",darkolivegreen:\"#556b2f\",darkorange:\"#ff8c00\",darkorchid:\"#9932cc\",darkred:\"#8b0000\",darksalmon:\"#e9967a\",darkseagreen:\"#8fbc8f\",darkslateblue:\"#483d8b\",darkslategray:\"#2f4f4f\",darkslategrey:\"#2f4f4f\",darkturquoise:\"#00ced1\",darkviolet:\"#9400d3\",deeppink:\"#ff1493\",deepskyblue:\"#00bfff\",dimgray:\"#696969\",dimgrey:\"#696969\",dodgerblue:\"#1e90ff\",firebrick:\"#b22222\",floralwhite:\"#fffaf0\",forestgreen:\"#228b22\",fuchsia:\"#ff00ff\",gainsboro:\"#dcdcdc\",\nghostwhite:\"#f8f8ff\",gold:\"#ffd700\",goldenrod:\"#daa520\",gray:\"#808080\",green:\"#008000\",greenyellow:\"#adff2f\",grey:\"#808080\",honeydew:\"#f0fff0\",hotpink:\"#ff69b4\",indianred:\"#cd5c5c\",indigo:\"#4b0082\",ivory:\"#fffff0\",khaki:\"#f0e68c\",lavender:\"#e6e6fa\",lavenderblush:\"#fff0f5\",lawngreen:\"#7cfc00\",lemonchiffon:\"#fffacd\",lightblue:\"#add8e6\",lightcoral:\"#f08080\",lightcyan:\"#e0ffff\",lightgoldenrodyellow:\"#fafad2\",lightgray:\"#d3d3d3\",lightgreen:\"#90ee90\",lightgrey:\"#d3d3d3\",lightpink:\"#ffb6c1\",lightsalmon:\"#ffa07a\",\nlightseagreen:\"#20b2aa\",lightskyblue:\"#87cefa\",lightslategray:\"#778899\",lightslategrey:\"#778899\",lightsteelblue:\"#b0c4de\",lightyellow:\"#ffffe0\",lime:\"#00ff00\",limegreen:\"#32cd32\",linen:\"#faf0e6\",magenta:\"#ff00ff\",maroon:\"#800000\",mediumaquamarine:\"#66cdaa\",mediumblue:\"#0000cd\",mediumorchid:\"#ba55d3\",mediumpurple:\"#9370db\",mediumseagreen:\"#3cb371\",mediumslateblue:\"#7b68ee\",mediumspringgreen:\"#00fa9a\",mediumturquoise:\"#48d1cc\",mediumvioletred:\"#c71585\",midnightblue:\"#191970\",mintcream:\"#f5fffa\",mistyrose:\"#ffe4e1\",\nmoccasin:\"#ffe4b5\",navajowhite:\"#ffdead\",navy:\"#000080\",oldlace:\"#fdf5e6\",olive:\"#808000\",olivedrab:\"#6b8e23\",orange:\"#ffa500\",orangered:\"#ff4500\",orchid:\"#da70d6\",palegoldenrod:\"#eee8aa\",palegreen:\"#98fb98\",paleturquoise:\"#afeeee\",palevioletred:\"#db7093\",papayawhip:\"#ffefd5\",peachpuff:\"#ffdab9\",peru:\"#cd853f\",pink:\"#ffc0cb\",plum:\"#dda0dd\",powderblue:\"#b0e0e6\",purple:\"#800080\",red:\"#ff0000\",rosybrown:\"#bc8f8f\",royalblue:\"#4169e1\",saddlebrown:\"#8b4513\",salmon:\"#fa8072\",sandybrown:\"#f4a460\",seagreen:\"#2e8b57\",\nseashell:\"#fff5ee\",sienna:\"#a0522d\",silver:\"#c0c0c0\",skyblue:\"#87ceeb\",slateblue:\"#6a5acd\",slategray:\"#708090\",slategrey:\"#708090\",snow:\"#fffafa\",springgreen:\"#00ff7f\",steelblue:\"#4682b4\",tan:\"#d2b48c\",teal:\"#008080\",thistle:\"#d8bfd8\",tomato:\"#ff6347\",turquoise:\"#40e0d0\",violet:\"#ee82ee\",wheat:\"#f5deb3\",white:\"#ffffff\",whitesmoke:\"#f5f5f5\",yellow:\"#ffff00\",yellowgreen:\"#9acd32\"};var pc=\"backgroundColor borderTopColor borderRightColor borderBottomColor borderLeftColor color outlineColor\".split(\" \"),qc=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/,rc=/^#(?:[0-9a-f]{3}){1,2}$/i,sc=/^(?:rgba)?\\((\\d{1,3}),\\s?(\\d{1,3}),\\s?(\\d{1,3}),\\s?(0|1|0\\.\\d*)\\)$/i,tc=/^(?:rgb)?\\((0|[1-9]\\d{0,2}),\\s?(0|[1-9]\\d{0,2}),\\s?(0|[1-9]\\d{0,2})\\)$/i;function uc(a,b){this.code=a;this.a=V[a]||vc;this.message=b||\"\";a=this.a.replace(/((?:^|\\s+)[a-z])/g,function(c){return c.toUpperCase().replace(/^[\\s\\xa0]+/g,\"\")});b=a.length-5;if(0>b||a.indexOf(\"Error\",b)!=b)a+=\"Error\";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||\"\"}m(uc,Error);var vc=\"unknown error\",V={15:\"element not selectable\",11:\"element not visible\"};V[31]=vc;V[30]=vc;V[24]=\"invalid cookie domain\";V[29]=\"invalid element coordinates\";V[12]=\"invalid element state\";\nV[32]=\"invalid selector\";V[51]=\"invalid selector\";V[52]=\"invalid selector\";V[17]=\"javascript error\";V[405]=\"unsupported operation\";V[34]=\"move target out of bounds\";V[27]=\"no such alert\";V[7]=\"no such element\";V[8]=\"no such frame\";V[23]=\"no such window\";V[28]=\"script timeout\";V[33]=\"session not created\";V[10]=\"stale element reference\";V[21]=\"timeout\";V[25]=\"unable to set cookie\";V[26]=\"unexpected alert open\";V[13]=vc;V[9]=\"unknown command\";var wc=xa(),xc=Aa()||u(\"iPod\"),yc=u(\"iPad\"),zc=u(\"Android\")&&!(ya()||xa()||u(\"Opera\")||u(\"Silk\")),Ac=ya(),Bc=u(\"Safari\")&&!(ya()||u(\"Coast\")||u(\"Opera\")||u(\"Edge\")||u(\"Edg/\")||u(\"OPR\")||xa()||u(\"Silk\")||u(\"Android\"))&&!(Aa()||u(\"iPad\")||u(\"iPod\"));function Cc(a){return(a=a.exec(r))?a[1]:\"\"}(function(){if(wc)return Cc(/Firefox\\/([0-9.]+)/);if(w||Ea||Da)return Ia;if(Ac)return Aa()||u(\"iPad\")||u(\"iPod\")?Cc(/CriOS\\/([0-9.]+)/):Cc(/Chrome\\/([0-9.]+)/);if(Bc&&!(Aa()||u(\"iPad\")||u(\"iPod\")))return Cc(/Version\\/([0-9.]+)/);if(xc||yc){var a=/Version\\/(\\S+).*Mobile\\/(\\S+)/.exec(r);if(a)return a[1]+\".\"+a[2]}else if(zc)return(a=Cc(/Android\\s+([0-9.]+)/))?a:Cc(/Version\\/([0-9.]+)/);return\"\"})();var Dc=w&&!(9<=Number(Na));function W(a,b){b&&\"string\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};var Ec=function(){var a={K:\"http://www.w3.org/2000/svg\"};return function(b){return a[b]||null}}();\nfunction Fc(a,b){var c=A(a);if(!c.documentElement)return null;(w||zc)&&nc(c?c.parentWindow||c.defaultView:window);try{var d=c.createNSResolver?c.createNSResolver(c.documentElement):Ec;if(w&&!Ma(7))return c.evaluate.call(c,b,a,d,9,null);if(!w||9<=Number(Na)){for(var e={},f=c.getElementsByTagName(\"*\"),g=0;g=b&&0<=c&&255>=c&&0<=d&&255>=d&&0<=e&&1>=e)){b=[b,c,d,e];break b}b=null}if(!b)b:{if(d=a.match(tc))if(b=\nNumber(d[1]),c=Number(d[2]),d=Number(d[3]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d){b=[b,c,d,1];break b}b=null}if(!b)b:{b=a.toLowerCase();c=oc[b.toLowerCase()];if(!c&&(c=\"#\"==b.charAt(0)?b:\"#\"+b,4==c.length&&(c=c.replace(qc,\"#$1$1$2$2$3$3\")),!rc.test(c))){b=null;break b}b=[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),1]}a=b?\"rgba(\"+b.join(\", \")+\")\":a}return a}\nfunction Kc(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&\"function\"==ca(c.getPropertyValue)&&(d=c.getPropertyValue(b));return\"inherit\"!=d?void 0!==d?d:null:(a=Jc(a))?Kc(a,b):null}\nfunction Lc(a,b,c){function d(g){var h=Mc(g);return 0=D.a+D.width;D=e.c>=D.b+D.height;if(M&&\"hidden\"==n.x||D&&\"hidden\"==n.y)return Z;if(M&&\"visible\"!=n.x||D&&\"visible\"!=n.y){if(v&&(n=d(a),e.f>=g.scrollWidth-n.x||e.a>=g.scrollHeight-n.y))return Z;e=Nc(a);return e==Z?Z:\"scroll\"}}}return\"none\"}\nfunction Mc(a){var b=Oc(a);if(b)return b.rect;if(W(a,\"HTML\"))return a=A(a),a=((a?a.parentWindow||a.defaultView:window)||window).document,a=\"CSS1Compat\"==a.compatMode?a.documentElement:a.body,a=new Xa(a.clientWidth,a.clientHeight),new X(0,0,a.width,a.height);try{var c=a.getBoundingClientRect()}catch(d){return new X(0,0,0,0)}b=new X(c.left,c.top,c.right-c.left,c.bottom-c.top);w&&a.ownerDocument.body&&(a=A(a),b.a-=a.documentElement.clientLeft+a.body.clientLeft,b.b-=a.documentElement.clientTop+a.body.clientTop);\nreturn b}function Oc(a){var b=W(a,\"MAP\");if(!b&&!W(a,\"AREA\"))return null;var c=b?a:W(a.parentNode,\"MAP\")?a.parentNode:null,d=null,e=null;c&&c.name&&(d=Gc('/descendant::*[@usemap = \"#'+c.name+'\"]',A(c)))&&(e=Mc(d),b||\"default\"==a.shape.toLowerCase()||(a=Sc(a),b=Math.min(Math.max(a.a,0),e.width),c=Math.min(Math.max(a.b,0),e.height),e=new X(b+e.a,c+e.b,Math.min(a.width,e.width-b),Math.min(a.height,e.height-c))));return{image:d,rect:e||new X(0,0,0,0)}}\nfunction Sc(a){var b=a.shape.toLowerCase();a=a.coords.split(\",\");if(\"rect\"==b&&4==a.length){b=a[0];var c=a[1];return new X(b,c,a[2]-b,a[3]-c)}if(\"circle\"==b&&3==a.length)return b=a[2],new X(a[0]-b,a[1]-b,2*b,2*b);if(\"poly\"==b&&2b||a.indexOf(\"Error\",b)!=b)a+=\"Error\";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||\"\"}ba(p,Error);var u=\"unknown error\",q={15:\"element not selectable\",11:\"element not visible\"};q[31]=u;q[30]=u;q[24]=\"invalid cookie domain\";q[29]=\"invalid element coordinates\";q[12]=\"invalid element state\";\nq[32]=\"invalid selector\";q[51]=\"invalid selector\";q[52]=\"invalid selector\";q[17]=\"javascript error\";q[405]=\"unsupported operation\";q[34]=\"move target out of bounds\";q[27]=\"no such alert\";q[7]=\"no such element\";q[8]=\"no such frame\";q[23]=\"no such window\";q[28]=\"script timeout\";q[33]=\"session not created\";q[10]=\"stale element reference\";q[21]=\"timeout\";q[25]=\"unable to set cookie\";q[26]=\"unexpected alert open\";q[13]=u;q[9]=\"unknown command\";function oa(a){var b=a.length-1;return 0<=b&&a.indexOf(\" \",b)==b}var v=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\\s\\xa0]*([\\s\\S]*?)[\\s\\xa0]*$/.exec(a)[1]};\nfunction pa(a,b){var c=0;a=v(String(a)).split(\".\");b=v(String(b)).split(\".\");for(var d=Math.max(a.length,b.length),e=0;0==c&&eb?1:0};var x;a:{var qa=h.navigator;if(qa){var ra=qa.userAgent;if(ra){x=ra;break a}}x=\"\"}function z(a){return-1!=x.indexOf(a)};function B(){return z(\"Firefox\")||z(\"FxiOS\")}function C(){return(z(\"Chrome\")||z(\"CriOS\"))&&!z(\"Edge\")};function sa(a){return String(a).replace(/\\-([a-z])/g,function(b,c){return c.toUpperCase()})};function D(){return z(\"iPhone\")&&!z(\"iPod\")&&!z(\"iPad\")};function ta(a,b){var c=ua;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var va=z(\"Opera\"),E=z(\"Trident\")||z(\"MSIE\"),ya=z(\"Edge\"),za=z(\"Gecko\")&&!(-1!=x.toLowerCase().indexOf(\"webkit\")&&!z(\"Edge\"))&&!(z(\"Trident\")||z(\"MSIE\"))&&!z(\"Edge\"),Aa=-1!=x.toLowerCase().indexOf(\"webkit\")&&!z(\"Edge\");function Ba(){var a=h.document;return a?a.documentMode:void 0}var F;\na:{var G=\"\",I=function(){var a=x;if(za)return/rv:([^\\);]+)(\\)|;)/.exec(a);if(ya)return/Edge\\/([\\d\\.]+)/.exec(a);if(E)return/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(a);if(Aa)return/WebKit\\/(\\S+)/.exec(a);if(va)return/(?:Version)[ \\/]?(\\S+)/.exec(a)}();I&&(G=I?I[1]:\"\");if(E){var J=Ba();if(null!=J&&J>parseFloat(G)){F=String(J);break a}}F=G}var ua={};function Ca(a){return ta(a,function(){return 0<=pa(F,a)})}var Da;Da=h.document&&E?Ba():void 0;var Ea=B(),Fa=D()||z(\"iPod\"),Ga=z(\"iPad\"),Ha=z(\"Android\")&&!(C()||B()||z(\"Opera\")||z(\"Silk\")),Ia=C(),Ja=z(\"Safari\")&&!(C()||z(\"Coast\")||z(\"Opera\")||z(\"Edge\")||z(\"Edg/\")||z(\"OPR\")||B()||z(\"Silk\")||z(\"Android\"))&&!(D()||z(\"iPad\")||z(\"iPod\"));function K(a){return(a=a.exec(x))?a[1]:\"\"}(function(){if(Ea)return K(/Firefox\\/([0-9.]+)/);if(E||ya||va)return F;if(Ia)return D()||z(\"iPad\")||z(\"iPod\")?K(/CriOS\\/([0-9.]+)/):K(/Chrome\\/([0-9.]+)/);if(Ja&&!(D()||z(\"iPad\")||z(\"iPod\")))return K(/Version\\/([0-9.]+)/);if(Fa||Ga){var a=/Version\\/(\\S+).*Mobile\\/(\\S+)/.exec(x);if(a)return a[1]+\".\"+a[2]}else if(Ha)return(a=K(/Android\\s+([0-9.]+)/))?a:K(/Version\\/([0-9.]+)/);return\"\"})();var Ka;if(Ka=E)Ka=!(9<=Number(Da));var La=Ka;function L(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}L.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};L.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};L.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function M(a,b){this.width=a;this.height=b}M.prototype.aspectRatio=function(){return this.width/this.height};M.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};M.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};M.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function Ma(a){for(;a&&1!=a.nodeType;)a=a.previousSibling;return a}function N(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function Na(a,b){a&&(a=a.parentNode);for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}function Oa(a){this.a=a||h.document||document};function O(a,b){b&&\"string\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};function P(a,b,c,d){this.f=a;this.a=b;this.b=c;this.c=d}P.prototype.ceil=function(){this.f=Math.ceil(this.f);this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.c=Math.ceil(this.c);return this};P.prototype.floor=function(){this.f=Math.floor(this.f);this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.c=Math.floor(this.c);return this};P.prototype.round=function(){this.f=Math.round(this.f);this.a=Math.round(this.a);this.b=Math.round(this.b);this.c=Math.round(this.c);return this};function R(a,b,c,d){this.a=a;this.b=b;this.width=c;this.height=d}R.prototype.ceil=function(){this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};R.prototype.floor=function(){this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};\nR.prototype.round=function(){this.a=Math.round(this.a);this.b=Math.round(this.b);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};var Pa=\"function\"===typeof ShadowRoot;function S(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return O(a)?a:null}\nfunction T(a,b){b=sa(b);if(\"float\"==b||\"cssFloat\"==b||\"styleFloat\"==b)b=La?\"styleFloat\":\"cssFloat\";a:{var c=b;var d=N(a);if(d.defaultView&&d.defaultView.getComputedStyle&&(d=d.defaultView.getComputedStyle(a,null))){c=d[c]||d.getPropertyValue(c)||\"\";break a}c=\"\"}a=c||Qa(a,b);if(null===a)a=null;else if(0<=ca(ja,b)){b:{var e=a.match(ma);if(e&&(b=Number(e[1]),c=Number(e[2]),d=Number(e[3]),e=Number(e[4]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d&&0<=e&&1>=e)){b=[b,c,d,e];break b}b=null}if(!b)b:{if(d=a.match(na))if(b=\nNumber(d[1]),c=Number(d[2]),d=Number(d[3]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d){b=[b,c,d,1];break b}b=null}if(!b)b:{b=a.toLowerCase();c=ia[b.toLowerCase()];if(!c&&(c=\"#\"==b.charAt(0)?b:\"#\"+b,4==c.length&&(c=c.replace(ka,\"#$1$1$2$2$3$3\")),!la.test(c))){b=null;break b}b=[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),1]}a=b?\"rgba(\"+b.join(\", \")+\")\":a}return a}\nfunction Qa(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&\"function\"==aa(c.getPropertyValue)&&(d=c.getPropertyValue(b));return\"inherit\"!=d?void 0!==d?d:null:(a=S(a))?Qa(a,b):null}\nfunction Ra(a,b,c){function d(g){var k=U(g);return 0=t.a+t.width;t=e.f>=t.b+t.height;if(A&&\"hidden\"==r.x||t&&\"hidden\"==r.y)return V;if(A&&\"visible\"!=r.x||t&&\"visible\"!=r.y){if(H&&(r=d(a),e.c>=g.scrollWidth-r.x||e.a>=g.scrollHeight-r.y))return V;e=Sa(a);return e==V?V:\"scroll\"}}}return\"none\"}\nfunction U(a){var b=Ta(a);if(b)return b.rect;if(O(a,\"HTML\"))return a=N(a),a=((a?a.parentWindow||a.defaultView:window)||window).document,a=\"CSS1Compat\"==a.compatMode?a.documentElement:a.body,a=new M(a.clientWidth,a.clientHeight),new R(0,0,a.width,a.height);try{var c=a.getBoundingClientRect()}catch(d){return new R(0,0,0,0)}b=new R(c.left,c.top,c.right-c.left,c.bottom-c.top);E&&a.ownerDocument.body&&(a=N(a),b.a-=a.documentElement.clientLeft+a.body.clientLeft,b.b-=a.documentElement.clientTop+a.body.clientTop);\nreturn b}\nfunction Ta(a){var b=O(a,\"MAP\");if(!b&&!O(a,\"AREA\"))return null;var c=b?a:O(a.parentNode,\"MAP\")?a.parentNode:null,d=null,e=null;if(c&&c.name){d='*[usemap=\"#'+c.name+'\"]';c=N(c);var f;if(f=\"function\"!=aa(c.querySelector)&&E&&(E?0<=pa(Da,8):Ca(8))){f=c.querySelector;var g=typeof f;f=!(\"object\"==g&&null!=f||\"function\"==g)}if(f)throw Error(\"CSS selection is not supported\");if(!d)throw new p(32,\"No selector specified\");d=v(d);try{var k=c.querySelector(d)}catch(y){throw new p(32,\"An invalid or illegal selector was specified\");}if(d=\nk&&1==k.nodeType?k:null)e=U(d),b||\"default\"==a.shape.toLowerCase()||(a=Xa(a),b=Math.min(Math.max(a.a,0),e.width),k=Math.min(Math.max(a.b,0),e.height),e=new R(b+e.a,k+e.b,Math.min(a.width,e.width-b),Math.min(a.height,e.height-k)))}return{image:d,rect:e||new R(0,0,0,0)}}\nfunction Xa(a){var b=a.shape.toLowerCase();a=a.coords.split(\",\");if(\"rect\"==b&&4==a.length){b=a[0];var c=a[1];return new R(b,c,a[2]-b,a[3]-c)}if(\"circle\"==b&&3==a.length)return b=a[2],new R(a[0]-b,a[1]-b,2*b,2*b);if(\"poly\"==b&&2b?null:\"string\"===typeof a?a.charAt(b):a[b]}\nfunction pa(a){return Array.prototype.concat.apply([],arguments)}function qa(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var ra=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\\s\\xa0]*([\\s\\S]*?)[\\s\\xa0]*$/.exec(a)[1]};function sa(a,b){return ab?1:0};var t;a:{var ta=k.navigator;if(ta){var ua=ta.userAgent;if(ua){t=ua;break a}}t=\"\"}function u(a){return-1!=t.indexOf(a)};function va(){return u(\"Firefox\")||u(\"FxiOS\")}function wa(){return(u(\"Chrome\")||u(\"CriOS\"))&&!u(\"Edge\")};function xa(a){return String(a).replace(/\\-([a-z])/g,function(b,c){return c.toUpperCase()})};function ya(){return u(\"iPhone\")&&!u(\"iPod\")&&!u(\"iPad\")};function za(a,b){var c=Aa;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var Ba=u(\"Opera\"),v=u(\"Trident\")||u(\"MSIE\"),Ca=u(\"Edge\"),Da=u(\"Gecko\")&&!(-1!=t.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\"))&&!(u(\"Trident\")||u(\"MSIE\"))&&!u(\"Edge\"),Ea=-1!=t.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\");function Fa(){var a=k.document;return a?a.documentMode:void 0}var Ga;\na:{var Ha=\"\",Ia=function(){var a=t;if(Da)return/rv:([^\\);]+)(\\)|;)/.exec(a);if(Ca)return/Edge\\/([\\d\\.]+)/.exec(a);if(v)return/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(a);if(Ea)return/WebKit\\/(\\S+)/.exec(a);if(Ba)return/(?:Version)[ \\/]?(\\S+)/.exec(a)}();Ia&&(Ha=Ia?Ia[1]:\"\");if(v){var Ja=Fa();if(null!=Ja&&Ja>parseFloat(Ha)){Ga=String(Ja);break a}}Ga=Ha}var Aa={};\nfunction Ka(a){return za(a,function(){for(var b=0,c=ra(String(Ga)).split(\".\"),d=ra(String(a)).split(\".\"),e=Math.max(c.length,d.length),f=0;0==b&&f]=|\\s+|./g,Sa=/^\\s/;function y(a,b){return a.b[a.a+(b||0)]}function z(a){return a.b[a.a++]}function Ta(a){return a.b.length<=a.a};function Ua(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}Ua.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};Ua.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};Ua.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function Va(a,b){this.width=a;this.height=b}Va.prototype.aspectRatio=function(){return this.width/this.height};Va.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};Va.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};Va.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function Wa(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if(\"undefined\"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}\nfunction Xa(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(v&&!(9<=Number(La))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if(\"sourceIndex\"in a||a.parentNode&&\"sourceIndex\"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?Ya(a,b):!c&&Wa(e,b)?-1*Za(a,b):!d&&Wa(f,a)?Za(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=A(a);c=d.createRange();\nc.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(k.Range.START_TO_END,a)}function Za(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return Ya(b,a)}function Ya(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function A(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function $a(a,b){a&&(a=a.parentNode);for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}\nfunction ab(a){this.a=a||k.document||document}ab.prototype.getElementsByTagName=function(a,b){return(b||this.a).getElementsByTagName(String(a))};function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?\"\":b);if(\"string\"!=typeof b)if(x&&\"title\"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b=\"\";a;){do 1!=a.nodeType&&(b+=a.nodeValue),x&&\"title\"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}\nfunction C(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Ma&&\"class\"==b&&(b=\"className\");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function bb(a,b,c,d,e){return(x?cb:db).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new E)}\nfunction cb(a,b,c,d,e){if(a instanceof F||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=eb(a);if(\"*\"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],h=0;b=f[h++];)C(b,c,d)&&g.push(b);f=g}for(h=0;b=f[h++];)\"*\"==a&&\"!\"==b.tagName||e.add(b);return e}gb(a,b,c,d,e);return e}\nfunction db(a,b,c,d,e){b.getElementsByName&&d&&\"name\"==c&&!v?(b=b.getElementsByName(d),n(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&\"class\"==c?(b=b.getElementsByClassName(d),n(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof G?gb(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),n(b,function(f){C(f,c,d)&&e.add(f)}));return e}\nfunction hb(a,b,c,d,e){var f;if((a instanceof F||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=eb(a);if(\"*\"!=g&&(f=ka(f,function(h){return h.tagName&&h.tagName.toLowerCase()==g}),!f))return e;c&&(f=ka(f,function(h){return C(h,c,d)}));n(f,function(h){\"*\"==g&&(\"!\"==h.tagName||\"*\"==g&&1!=h.nodeType)||e.add(h)});return e}return ib(a,b,c,d,e)}function ib(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b);return e}\nfunction gb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b),gb(a,b,c,d,e)}function eb(a){if(a instanceof G){if(8==a.b)return\"!\";if(null===a.b)return\"*\"}return a.f()};function E(){this.b=this.a=null;this.l=0}function jb(a){this.f=a;this.a=this.b=null}function kb(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof Na&&g instanceof Na&&e.a==g.a?(e=c,c=c.a,b=b.a):0\",4,2,function(a,b,c){return ub(function(d,e){return d>e},a,b,c)});P(\"<=\",4,2,function(a,b,c){return ub(function(d,e){return d<=e},a,b,c)});P(\">=\",4,2,function(a,b,c){return ub(function(d,e){return d>=e},a,b,c)});var tb=P(\"=\",3,2,function(a,b,c){return ub(function(d,e){return d==e},a,b,c,!0)});P(\"!=\",3,2,function(a,b,c){return ub(function(d,e){return d!=e},a,b,c,!0)});P(\"and\",2,2,function(a,b,c){return rb(a,c)&&rb(b,c)});P(\"or\",1,2,function(a,b,c){return rb(a,c)||rb(b,c)});function xb(a,b){if(b.a.length&&4!=a.i)throw Error(\"Primary expression must evaluate to nodeset if filter has predicate(s).\");J.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}l(xb,J);xb.prototype.a=function(a){a=this.c.a(a);return yb(this.h,a)};xb.prototype.toString=function(){var a=\"Filter:\"+K(this.c);return a+=K(this.h)};function zb(a,b){if(b.lengtha.B)throw Error(\"Function \"+a.j+\" expects at most \"+a.B+\" arguments, \"+b.length+\" given\");a.H&&n(b,function(c,d){if(4!=c.i)throw Error(\"Argument \"+d+\" to function \"+a.j+\" is not of type Nodeset: \"+c);});J.call(this,a.i);this.v=a;this.c=b;pb(this,a.g||ma(b,function(c){return c.g}));qb(this,a.G&&!b.length||a.F&&!!b.length||ma(b,function(c){return c.b}))}\nl(zb,J);zb.prototype.a=function(a){return this.v.m.apply(null,pa(a,this.c))};zb.prototype.toString=function(){var a=\"Function: \"+this.v;if(this.c.length){var b=la(this.c,function(c,d){return c+K(d)},\"Arguments:\");a+=K(b)}return a};function Ab(a,b,c,d,e,f,g,h){this.j=a;this.i=b;this.g=c;this.G=d;this.F=!1;this.m=e;this.C=f;this.B=void 0!==g?g:f;this.H=!!h}Ab.prototype.toString=function(){return this.j};var Bb={};\nfunction Q(a,b,c,d,e,f,g,h){if(Bb.hasOwnProperty(a))throw Error(\"Function already created: \"+a+\".\");Bb[a]=new Ab(a,b,c,d,e,f,g,h)}Q(\"boolean\",2,!1,!1,function(a,b){return rb(b,a)},1);Q(\"ceiling\",1,!1,!1,function(a,b){return Math.ceil(N(b,a))},1);Q(\"concat\",3,!1,!1,function(a,b){return la(qa(arguments,1),function(c,d){return c+O(d,a)},\"\")},2,null);Q(\"contains\",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return-1!=b.indexOf(a)},2);Q(\"count\",1,!1,!1,function(a,b){return b.a(a).l},1,1,!0);\nQ(\"false\",2,!1,!1,function(){return!1},0);Q(\"floor\",1,!1,!1,function(a,b){return Math.floor(N(b,a))},1);Q(\"id\",4,!1,!1,function(a,b){function c(h){if(x){var m=e.all[h];if(m){if(m.nodeType&&h==m.id)return m;if(m.length)return oa(m,function(w){return h==w.id})}return null}return e.getElementById(h)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=O(b,a).split(/\\s+/);var f=[];n(a,function(h){h=c(h);!h||0<=ja(f,h)||f.push(h)});f.sort(Xa);var g=new E;n(f,function(h){g.add(h)});return g},1);\nQ(\"lang\",2,!1,!1,function(){return!1},1);Q(\"last\",1,!0,!1,function(a){if(1!=arguments.length)throw Error(\"Function last expects ()\");return a.f},0);Q(\"local-name\",3,!1,!0,function(a,b){return(a=b?mb(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():\"\"},0,1,!0);Q(\"name\",3,!1,!0,function(a,b){return(a=b?mb(b.a(a)):a.a)?a.nodeName.toLowerCase():\"\"},0,1,!0);Q(\"namespace-uri\",3,!0,!1,function(){return\"\"},0,1,!0);\nQ(\"normalize-space\",3,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).replace(/[\\s\\xa0]+/g,\" \").replace(/^\\s+|\\s+$/g,\"\")},0,1);Q(\"not\",2,!1,!1,function(a,b){return!rb(b,a)},1);Q(\"number\",1,!1,!0,function(a,b){return b?N(b,a):+B(a.a)},0,1);Q(\"position\",1,!0,!1,function(a){return a.b},0);Q(\"round\",1,!1,!1,function(a,b){return Math.round(N(b,a))},1);Q(\"starts-with\",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return 0==b.lastIndexOf(a,0)},2);Q(\"string\",3,!1,!0,function(a,b){return b?O(b,a):B(a.a)},0,1);\nQ(\"string-length\",1,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).length},0,1);Q(\"substring\",3,!1,!1,function(a,b,c,d){c=N(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return\"\";d=d?N(d,a):Infinity;if(isNaN(d)||-Infinity===d)return\"\";c=Math.round(c)-1;var e=Math.max(c,0);a=O(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);Q(\"substring-after\",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);c=b.indexOf(a);return-1==c?\"\":b.substring(c+a.length)},2);\nQ(\"substring-before\",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);a=b.indexOf(a);return-1==a?\"\":b.substring(0,a)},2);Q(\"sum\",1,!1,!1,function(a,b){a=H(b.a(a));b=0;for(var c=I(a);c;c=I(a))b+=+B(c);return b},1,1,!0);Q(\"translate\",3,!1,!1,function(a,b,c,d){b=O(b,a);c=O(c,a);var e=O(d,a);a={};for(d=0;da.length)throw Error(\"Unclosed literal string\");return new Db(a)}\nfunction cc(a){var b=[];if(Jb(y(a.a))){var c=z(a.a);var d=y(a.a);if(\"/\"==c&&(Ta(a.a)||\".\"!=d&&\"..\"!=d&&\"@\"!=d&&\"*\"!=d&&!/(?![0-9])[\\w]/.test(d)))return new Hb;d=new Hb;T(a,\"Missing next location step.\");c=dc(a,c);b.push(c)}else{a:{c=y(a.a);d=c.charAt(0);switch(d){case \"$\":throw Error(\"Variable reference not allowed in HTML XPath\");case \"(\":z(a.a);c=Yb(a);T(a,'unclosed \"(\"');$b(a,\")\");break;case '\"':case \"'\":c=bc(a);break;default:if(isNaN(+c))if(!Cb(c)&&/(?![0-9])[\\w]/.test(d)&&\"(\"==y(a.a,1)){c=z(a.a);\nc=Bb[c]||null;z(a.a);for(d=[];\")\"!=y(a.a);){T(a,\"Missing function argument list.\");d.push(Yb(a));if(\",\"!=y(a.a))break;z(a.a)}T(a,\"Unclosed function argument list.\");ac(a);c=new zb(c,d)}else{c=null;break a}else c=new Eb(+z(a.a))}\"[\"==y(a.a)&&(d=new Mb(ec(a)),c=new xb(c,d))}if(c)if(Jb(y(a.a)))d=c;else return c;else c=dc(a,\"/\"),d=new Ib,b.push(c)}for(;Jb(y(a.a));)c=z(a.a),T(a,\"Missing next location step.\"),c=dc(a,c),b.push(c);return new Fb(d,b)}\nfunction dc(a,b){if(\"/\"!=b&&\"//\"!=b)throw Error('Step op should be \"/\" or \"//\"');if(\".\"==y(a.a)){var c=new R(Sb,new G(\"node\"));z(a.a);return c}if(\"..\"==y(a.a))return c=new R(Rb,new G(\"node\")),z(a.a),c;if(\"@\"==y(a.a)){var d=Gb;z(a.a);T(a,\"Missing attribute name\")}else if(\"::\"==y(a.a,1)){if(!/(?![0-9])[\\w]/.test(y(a.a).charAt(0)))throw Error(\"Bad token: \"+z(a.a));var e=z(a.a);d=Qb[e]||null;if(!d)throw Error(\"No axis with name: \"+e);z(a.a);T(a,\"Missing node name\")}else d=Nb;e=y(a.a);if(/(?![0-9])[\\w\\*]/.test(e.charAt(0)))if(\"(\"==\ny(a.a,1)){if(!Cb(e))throw Error(\"Invalid node type: \"+e);e=z(a.a);if(!Cb(e))throw Error(\"Invalid type name: \"+e);$b(a,\"(\");T(a,\"Bad nodetype\");var f=y(a.a).charAt(0),g=null;if('\"'==f||\"'\"==f)g=bc(a);T(a,\"Bad nodetype\");ac(a);e=new G(e,g)}else if(e=z(a.a),f=e.indexOf(\":\"),-1==f)e=new F(e);else{g=e.substring(0,f);if(\"*\"==g)var h=\"*\";else if(h=a.b(g),!h)throw Error(\"Namespace prefix not declared: \"+g);e=e.substr(f+1);e=new F(e,h)}else throw Error(\"Bad token: \"+z(a.a));a=new Mb(ec(a),d.s);return c||new R(d,\ne,a,\"//\"==b)}function ec(a){for(var b=[];\"[\"==y(a.a);){z(a.a);T(a,\"Missing predicate expression.\");var c=Yb(a);b.push(c);T(a,\"Unclosed predicate expression.\");$b(a,\"]\")}return b}function Zb(a){if(\"-\"==y(a.a))return z(a.a),new Tb(Zb(a));var b=cc(a);if(\"|\"!=y(a.a))a=b;else{for(b=[b];\"|\"==z(a.a);)T(a,\"Missing next union location path.\"),b.push(cc(a));a.a.a--;a=new Ub(b)}return a};function fc(a){switch(a.nodeType){case 1:return ha(gc,a);case 9:return fc(a.documentElement);case 11:case 10:case 6:case 12:return hc;default:return a.parentNode?fc(a.parentNode):hc}}function hc(){return null}function gc(a,b){if(a.prefix==b)return a.namespaceURI||\"http://www.w3.org/1999/xhtml\";var c=a.getAttributeNode(\"xmlns:\"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?gc(a.parentNode,b):null};function ic(a,b){if(!a.length)throw Error(\"Empty XPath expression.\");a=Qa(a);if(Ta(a))throw Error(\"Invalid XPath expression.\");b?\"function\"==ca(b)||(b=fa(b.lookupNamespaceURI,b)):b=function(){return null};var c=Yb(new Vb(a,b));if(!Ta(a))throw Error(\"Bad token: \"+z(a));this.evaluate=function(d,e){d=c.a(new ia(d));return new U(d,e)}}\nfunction U(a,b){if(0==b)if(a instanceof E)b=4;else if(\"string\"==typeof a)b=2;else if(\"number\"==typeof a)b=1;else if(\"boolean\"==typeof a)b=3;else throw Error(\"Unexpected evaluation result.\");if(2!=b&&1!=b&&3!=b&&!(a instanceof E))throw Error(\"value could not be converted to the specified type\");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof E?nb(a):\"\"+a;break;case 1:this.numberValue=a instanceof E?+nb(a):+a;break;case 3:this.booleanValue=a instanceof E?0=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error(\"snapshotItem called with wrong result type\");return g>=d.length||\n0>g?null:d[g]}}U.ANY_TYPE=0;U.NUMBER_TYPE=1;U.STRING_TYPE=2;U.BOOLEAN_TYPE=3;U.UNORDERED_NODE_ITERATOR_TYPE=4;U.ORDERED_NODE_ITERATOR_TYPE=5;U.UNORDERED_NODE_SNAPSHOT_TYPE=6;U.ORDERED_NODE_SNAPSHOT_TYPE=7;U.ANY_UNORDERED_NODE_TYPE=8;U.FIRST_ORDERED_NODE_TYPE=9;function jc(a){this.lookupNamespaceURI=fc(a)}\nfunction kc(a,b){a=a||k;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=U,c.evaluate=function(d,e,f,g){return(new ic(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new ic(d,e)},c.createNSResolver=function(d){return new jc(d)}}ba(\"wgxpath.install\",kc);ba(\"wgxpath.install\",kc);var lc={aliceblue:\"#f0f8ff\",antiquewhite:\"#faebd7\",aqua:\"#00ffff\",aquamarine:\"#7fffd4\",azure:\"#f0ffff\",beige:\"#f5f5dc\",bisque:\"#ffe4c4\",black:\"#000000\",blanchedalmond:\"#ffebcd\",blue:\"#0000ff\",blueviolet:\"#8a2be2\",brown:\"#a52a2a\",burlywood:\"#deb887\",cadetblue:\"#5f9ea0\",chartreuse:\"#7fff00\",chocolate:\"#d2691e\",coral:\"#ff7f50\",cornflowerblue:\"#6495ed\",cornsilk:\"#fff8dc\",crimson:\"#dc143c\",cyan:\"#00ffff\",darkblue:\"#00008b\",darkcyan:\"#008b8b\",darkgoldenrod:\"#b8860b\",darkgray:\"#a9a9a9\",darkgreen:\"#006400\",\ndarkgrey:\"#a9a9a9\",darkkhaki:\"#bdb76b\",darkmagenta:\"#8b008b\",darkolivegreen:\"#556b2f\",darkorange:\"#ff8c00\",darkorchid:\"#9932cc\",darkred:\"#8b0000\",darksalmon:\"#e9967a\",darkseagreen:\"#8fbc8f\",darkslateblue:\"#483d8b\",darkslategray:\"#2f4f4f\",darkslategrey:\"#2f4f4f\",darkturquoise:\"#00ced1\",darkviolet:\"#9400d3\",deeppink:\"#ff1493\",deepskyblue:\"#00bfff\",dimgray:\"#696969\",dimgrey:\"#696969\",dodgerblue:\"#1e90ff\",firebrick:\"#b22222\",floralwhite:\"#fffaf0\",forestgreen:\"#228b22\",fuchsia:\"#ff00ff\",gainsboro:\"#dcdcdc\",\nghostwhite:\"#f8f8ff\",gold:\"#ffd700\",goldenrod:\"#daa520\",gray:\"#808080\",green:\"#008000\",greenyellow:\"#adff2f\",grey:\"#808080\",honeydew:\"#f0fff0\",hotpink:\"#ff69b4\",indianred:\"#cd5c5c\",indigo:\"#4b0082\",ivory:\"#fffff0\",khaki:\"#f0e68c\",lavender:\"#e6e6fa\",lavenderblush:\"#fff0f5\",lawngreen:\"#7cfc00\",lemonchiffon:\"#fffacd\",lightblue:\"#add8e6\",lightcoral:\"#f08080\",lightcyan:\"#e0ffff\",lightgoldenrodyellow:\"#fafad2\",lightgray:\"#d3d3d3\",lightgreen:\"#90ee90\",lightgrey:\"#d3d3d3\",lightpink:\"#ffb6c1\",lightsalmon:\"#ffa07a\",\nlightseagreen:\"#20b2aa\",lightskyblue:\"#87cefa\",lightslategray:\"#778899\",lightslategrey:\"#778899\",lightsteelblue:\"#b0c4de\",lightyellow:\"#ffffe0\",lime:\"#00ff00\",limegreen:\"#32cd32\",linen:\"#faf0e6\",magenta:\"#ff00ff\",maroon:\"#800000\",mediumaquamarine:\"#66cdaa\",mediumblue:\"#0000cd\",mediumorchid:\"#ba55d3\",mediumpurple:\"#9370db\",mediumseagreen:\"#3cb371\",mediumslateblue:\"#7b68ee\",mediumspringgreen:\"#00fa9a\",mediumturquoise:\"#48d1cc\",mediumvioletred:\"#c71585\",midnightblue:\"#191970\",mintcream:\"#f5fffa\",mistyrose:\"#ffe4e1\",\nmoccasin:\"#ffe4b5\",navajowhite:\"#ffdead\",navy:\"#000080\",oldlace:\"#fdf5e6\",olive:\"#808000\",olivedrab:\"#6b8e23\",orange:\"#ffa500\",orangered:\"#ff4500\",orchid:\"#da70d6\",palegoldenrod:\"#eee8aa\",palegreen:\"#98fb98\",paleturquoise:\"#afeeee\",palevioletred:\"#db7093\",papayawhip:\"#ffefd5\",peachpuff:\"#ffdab9\",peru:\"#cd853f\",pink:\"#ffc0cb\",plum:\"#dda0dd\",powderblue:\"#b0e0e6\",purple:\"#800080\",red:\"#ff0000\",rosybrown:\"#bc8f8f\",royalblue:\"#4169e1\",saddlebrown:\"#8b4513\",salmon:\"#fa8072\",sandybrown:\"#f4a460\",seagreen:\"#2e8b57\",\nseashell:\"#fff5ee\",sienna:\"#a0522d\",silver:\"#c0c0c0\",skyblue:\"#87ceeb\",slateblue:\"#6a5acd\",slategray:\"#708090\",slategrey:\"#708090\",snow:\"#fffafa\",springgreen:\"#00ff7f\",steelblue:\"#4682b4\",tan:\"#d2b48c\",teal:\"#008080\",thistle:\"#d8bfd8\",tomato:\"#ff6347\",turquoise:\"#40e0d0\",violet:\"#ee82ee\",wheat:\"#f5deb3\",white:\"#ffffff\",whitesmoke:\"#f5f5f5\",yellow:\"#ffff00\",yellowgreen:\"#9acd32\"};var mc=\"backgroundColor borderTopColor borderRightColor borderBottomColor borderLeftColor color outlineColor\".split(\" \"),nc=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/,oc=/^#(?:[0-9a-f]{3}){1,2}$/i,pc=/^(?:rgba)?\\((\\d{1,3}),\\s?(\\d{1,3}),\\s?(\\d{1,3}),\\s?(0|1|0\\.\\d*)\\)$/i,qc=/^(?:rgb)?\\((0|[1-9]\\d{0,2}),\\s?(0|[1-9]\\d{0,2}),\\s?(0|[1-9]\\d{0,2})\\)$/i;function rc(a,b){this.code=a;this.a=V[a]||sc;this.message=b||\"\";a=this.a.replace(/((?:^|\\s+)[a-z])/g,function(c){return c.toUpperCase().replace(/^[\\s\\xa0]+/g,\"\")});b=a.length-5;if(0>b||a.indexOf(\"Error\",b)!=b)a+=\"Error\";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||\"\"}l(rc,Error);var sc=\"unknown error\",V={15:\"element not selectable\",11:\"element not visible\"};V[31]=sc;V[30]=sc;V[24]=\"invalid cookie domain\";V[29]=\"invalid element coordinates\";V[12]=\"invalid element state\";\nV[32]=\"invalid selector\";V[51]=\"invalid selector\";V[52]=\"invalid selector\";V[17]=\"javascript error\";V[405]=\"unsupported operation\";V[34]=\"move target out of bounds\";V[27]=\"no such alert\";V[7]=\"no such element\";V[8]=\"no such frame\";V[23]=\"no such window\";V[28]=\"script timeout\";V[33]=\"session not created\";V[10]=\"stale element reference\";V[21]=\"timeout\";V[25]=\"unable to set cookie\";V[26]=\"unexpected alert open\";V[13]=sc;V[9]=\"unknown command\";var tc=va(),uc=ya()||u(\"iPod\"),vc=u(\"iPad\"),wc=u(\"Android\")&&!(wa()||va()||u(\"Opera\")||u(\"Silk\")),xc=wa(),yc=u(\"Safari\")&&!(wa()||u(\"Coast\")||u(\"Opera\")||u(\"Edge\")||u(\"Edg/\")||u(\"OPR\")||va()||u(\"Silk\")||u(\"Android\"))&&!(ya()||u(\"iPad\")||u(\"iPod\"));function zc(a){return(a=a.exec(t))?a[1]:\"\"}(function(){if(tc)return zc(/Firefox\\/([0-9.]+)/);if(v||Ca||Ba)return Ga;if(xc)return ya()||u(\"iPad\")||u(\"iPod\")?zc(/CriOS\\/([0-9.]+)/):zc(/Chrome\\/([0-9.]+)/);if(yc&&!(ya()||u(\"iPad\")||u(\"iPod\")))return zc(/Version\\/([0-9.]+)/);if(uc||vc){var a=/Version\\/(\\S+).*Mobile\\/(\\S+)/.exec(t);if(a)return a[1]+\".\"+a[2]}else if(wc)return(a=zc(/Android\\s+([0-9.]+)/))?a:zc(/Version\\/([0-9.]+)/);return\"\"})();var Ac=v&&!(9<=Number(La));function W(a,b){b&&\"string\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};var Bc=function(){var a={K:\"http://www.w3.org/2000/svg\"};return function(b){return a[b]||null}}();\nfunction Cc(a,b){var c=A(a);if(!c.documentElement)return null;(v||wc)&&kc(c?c.parentWindow||c.defaultView:window);try{var d=c.createNSResolver?c.createNSResolver(c.documentElement):Bc;if(v&&!Ka(7))return c.evaluate.call(c,b,a,d,9,null);if(!v||9<=Number(La)){for(var e={},f=c.getElementsByTagName(\"*\"),g=0;g=b&&0<=c&&255>=c&&0<=d&&255>=d&&0<=e&&1>=e)){b=[b,c,d,e];break b}b=null}if(!b)b:{if(d=a.match(qc))if(b=\nNumber(d[1]),c=Number(d[2]),d=Number(d[3]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d){b=[b,c,d,1];break b}b=null}if(!b)b:{b=a.toLowerCase();c=lc[b.toLowerCase()];if(!c&&(c=\"#\"==b.charAt(0)?b:\"#\"+b,4==c.length&&(c=c.replace(nc,\"#$1$1$2$2$3$3\")),!oc.test(c))){b=null;break b}b=[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),1]}a=b?\"rgba(\"+b.join(\", \")+\")\":a}return a}\nfunction Hc(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&\"function\"==ca(c.getPropertyValue)&&(d=c.getPropertyValue(b));return\"inherit\"!=d?void 0!==d?d:null:(a=Gc(a))?Hc(a,b):null}\nfunction Ic(a,b,c){function d(g){var h=Jc(g);return 0=D.a+D.width;D=e.c>=D.b+D.height;if(L&&\"hidden\"==r.x||D&&\"hidden\"==r.y)return Z;if(L&&\"visible\"!=r.x||D&&\"visible\"!=r.y){if(w&&(r=d(a),e.f>=g.scrollWidth-r.x||e.a>=g.scrollHeight-r.y))return Z;e=Kc(a);return e==Z?Z:\"scroll\"}}}return\"none\"}\nfunction Jc(a){var b=Lc(a);if(b)return b.rect;if(W(a,\"HTML\"))return a=A(a),a=((a?a.parentWindow||a.defaultView:window)||window).document,a=\"CSS1Compat\"==a.compatMode?a.documentElement:a.body,a=new Va(a.clientWidth,a.clientHeight),new X(0,0,a.width,a.height);try{var c=a.getBoundingClientRect()}catch(d){return new X(0,0,0,0)}b=new X(c.left,c.top,c.right-c.left,c.bottom-c.top);v&&a.ownerDocument.body&&(a=A(a),b.a-=a.documentElement.clientLeft+a.body.clientLeft,b.b-=a.documentElement.clientTop+a.body.clientTop);\nreturn b}function Lc(a){var b=W(a,\"MAP\");if(!b&&!W(a,\"AREA\"))return null;var c=b?a:W(a.parentNode,\"MAP\")?a.parentNode:null,d=null,e=null;c&&c.name&&(d=Dc('/descendant::*[@usemap = \"#'+c.name+'\"]',A(c)))&&(e=Jc(d),b||\"default\"==a.shape.toLowerCase()||(a=Oc(a),b=Math.min(Math.max(a.a,0),e.width),c=Math.min(Math.max(a.b,0),e.height),e=new X(b+e.a,c+e.b,Math.min(a.width,e.width-b),Math.min(a.height,e.height-c))));return{image:d,rect:e||new X(0,0,0,0)}}\nfunction Oc(a){var b=a.shape.toLowerCase();a=a.coords.split(\",\");if(\"rect\"==b&&4==a.length){b=a[0];var c=a[1];return new X(b,c,a[2]-b,a[3]-c)}if(\"circle\"==b&&3==a.length)return b=a[2],new X(a[0]-b,a[1]-b,2*b,2*b);if(\"poly\"==b&&2b?null:\"string\"===typeof a?a.charAt(b):a[b]}function la(a){return Array.prototype.concat.apply([],arguments)}function ma(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var t;a:{var na=k.navigator;if(na){var oa=na.userAgent;if(oa){t=oa;break a}}t=\"\"}function u(a){return-1!=t.indexOf(a)};function pa(){return u(\"Firefox\")||u(\"FxiOS\")}function qa(){return(u(\"Chrome\")||u(\"CriOS\"))&&!u(\"Edge\")};function ra(){return u(\"iPhone\")&&!u(\"iPod\")&&!u(\"iPad\")};var sa=u(\"Opera\"),v=u(\"Trident\")||u(\"MSIE\"),ta=u(\"Edge\"),ua=u(\"Gecko\")&&!(-1!=t.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\"))&&!(u(\"Trident\")||u(\"MSIE\"))&&!u(\"Edge\"),va=-1!=t.toLowerCase().indexOf(\"webkit\")&&!u(\"Edge\");function wa(){var a=k.document;return a?a.documentMode:void 0}var xa;\na:{var ya=\"\",za=function(){var a=t;if(ua)return/rv:([^\\);]+)(\\)|;)/.exec(a);if(ta)return/Edge\\/([\\d\\.]+)/.exec(a);if(v)return/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/.exec(a);if(va)return/WebKit\\/(\\S+)/.exec(a);if(sa)return/(?:Version)[ \\/]?(\\S+)/.exec(a)}();za&&(ya=za?za[1]:\"\");if(v){var Aa=wa();if(null!=Aa&&Aa>parseFloat(ya)){xa=String(Aa);break a}}xa=ya}var Ba;Ba=k.document&&v?wa():void 0;var w=v&&!(9<=Number(Ba)),Ca=v&&!(8<=Number(Ba));function y(a,b,c,d){this.a=a;this.nodeName=c;this.nodeValue=d;this.nodeType=2;this.parentNode=this.ownerElement=b}function Da(a,b){var c=Ca&&\"href\"==b.nodeName?a.getAttribute(b.nodeName,2):b.nodeValue;return new y(b,a,b.nodeName,c)};function Ea(a){this.b=a;this.a=0}function Fa(a){a=a.match(Ga);for(var b=0;b]=|\\s+|./g,Ha=/^\\s/;function z(a,b){return a.b[a.a+(b||0)]}function A(a){return a.b[a.a++]}function Ia(a){return a.b.length<=a.a};function Ja(a){for(;a&&1!=a.nodeType;)a=a.previousSibling;return a}function Ka(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if(\"undefined\"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}\nfunction La(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(v&&!(9<=Number(Ba))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if(\"sourceIndex\"in a||a.parentNode&&\"sourceIndex\"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?Ma(a,b):!c&&Ka(e,b)?-1*Na(a,b):!d&&Ka(f,a)?Na(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=9==a.nodeType?\na:a.ownerDocument||a.document;c=d.createRange();c.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(k.Range.START_TO_END,a)}function Na(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return Ma(b,a)}function Ma(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function Oa(a,b){for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null};function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?\"\":b);if(\"string\"!=typeof b)if(w&&\"title\"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b=\"\";a;){do 1!=a.nodeType&&(b+=a.nodeValue),w&&\"title\"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}\nfunction C(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Ca&&\"class\"==b&&(b=\"className\");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function D(a,b,c,d,e){return(w?Pa:Qa).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new E)}\nfunction Pa(a,b,c,d,e){if(a instanceof F||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=Ra(a);if(\"*\"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],h=0;b=f[h++];)C(b,c,d)&&g.push(b);f=g}for(h=0;b=f[h++];)\"*\"==a&&\"!\"==b.tagName||e.add(b);return e}Sa(a,b,c,d,e);return e}\nfunction Qa(a,b,c,d,e){b.getElementsByName&&d&&\"name\"==c&&!v?(b=b.getElementsByName(d),n(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&\"class\"==c?(b=b.getElementsByClassName(d),n(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof G?Sa(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),n(b,function(f){C(f,c,d)&&e.add(f)}));return e}\nfunction Ta(a,b,c,d,e){var f;if((a instanceof F||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=Ra(a);if(\"*\"!=g&&(f=ja(f,function(h){return h.tagName&&h.tagName.toLowerCase()==g}),!f))return e;c&&(f=ja(f,function(h){return C(h,c,d)}));n(f,function(h){\"*\"==g&&(\"!\"==h.tagName||\"*\"==g&&1!=h.nodeType)||e.add(h)});return e}return Ua(a,b,c,d,e)}function Ua(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b);return e}\nfunction Sa(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b),Sa(a,b,c,d,e)}function Ra(a){if(a instanceof G){if(8==a.b)return\"!\";if(null===a.b)return\"*\"}return a.f()};function E(){this.b=this.a=null;this.l=0}function Va(a){this.f=a;this.a=this.b=null}function Wa(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof y&&g instanceof y&&e.a==g.a?(e=c,c=c.a,b=b.a):0\",4,2,function(a,b,c){return P(function(d,e){return d>e},a,b,c)});Q(\"<=\",4,2,function(a,b,c){return P(function(d,e){return d<=e},a,b,c)});Q(\">=\",4,2,function(a,b,c){return P(function(d,e){return d>=e},a,b,c)});var cb=Q(\"=\",3,2,function(a,b,c){return P(function(d,e){return d==e},a,b,c,!0)});Q(\"!=\",3,2,function(a,b,c){return P(function(d,e){return d!=e},a,b,c,!0)});Q(\"and\",2,2,function(a,b,c){return N(a,c)&&N(b,c)});Q(\"or\",1,2,function(a,b,c){return N(a,c)||N(b,c)});function fb(a,b){if(b.a.length&&4!=a.i)throw Error(\"Primary expression must evaluate to nodeset if filter has predicate(s).\");J.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}l(fb,J);fb.prototype.a=function(a){a=this.c.a(a);return gb(this.h,a)};fb.prototype.toString=function(){var a=\"Filter:\"+K(this.c);return a+=K(this.h)};function hb(a,b){if(b.lengtha.B)throw Error(\"Function \"+a.j+\" expects at most \"+a.B+\" arguments, \"+b.length+\" given\");a.H&&n(b,function(c,d){if(4!=c.i)throw Error(\"Argument \"+d+\" to function \"+a.j+\" is not of type Nodeset: \"+c);});J.call(this,a.i);this.v=a;this.c=b;ab(this,a.g||r(b,function(c){return c.g}));bb(this,a.G&&!b.length||a.F&&!!b.length||r(b,function(c){return c.b}))}l(hb,J);\nhb.prototype.a=function(a){return this.v.m.apply(null,la(a,this.c))};hb.prototype.toString=function(){var a=\"Function: \"+this.v;if(this.c.length){var b=p(this.c,function(c,d){return c+K(d)},\"Arguments:\");a+=K(b)}return a};function ib(a,b,c,d,e,f,g,h){this.j=a;this.i=b;this.g=c;this.G=d;this.F=!1;this.m=e;this.C=f;this.B=void 0!==g?g:f;this.H=!!h}ib.prototype.toString=function(){return this.j};var jb={};\nfunction R(a,b,c,d,e,f,g,h){if(jb.hasOwnProperty(a))throw Error(\"Function already created: \"+a+\".\");jb[a]=new ib(a,b,c,d,e,f,g,h)}R(\"boolean\",2,!1,!1,function(a,b){return N(b,a)},1);R(\"ceiling\",1,!1,!1,function(a,b){return Math.ceil(L(b,a))},1);R(\"concat\",3,!1,!1,function(a,b){return p(ma(arguments,1),function(c,d){return c+M(d,a)},\"\")},2,null);R(\"contains\",2,!1,!1,function(a,b,c){b=M(b,a);a=M(c,a);return-1!=b.indexOf(a)},2);R(\"count\",1,!1,!1,function(a,b){return b.a(a).l},1,1,!0);\nR(\"false\",2,!1,!1,function(){return!1},0);R(\"floor\",1,!1,!1,function(a,b){return Math.floor(L(b,a))},1);R(\"id\",4,!1,!1,function(a,b){function c(h){if(w){var q=e.all[h];if(q){if(q.nodeType&&h==q.id)return q;if(q.length)return ka(q,function(x){return h==x.id})}return null}return e.getElementById(h)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=M(b,a).split(/\\s+/);var f=[];n(a,function(h){h=c(h);!h||0<=ia(f,h)||f.push(h)});f.sort(La);var g=new E;n(f,function(h){g.add(h)});return g},1);\nR(\"lang\",2,!1,!1,function(){return!1},1);R(\"last\",1,!0,!1,function(a){if(1!=arguments.length)throw Error(\"Function last expects ()\");return a.f},0);R(\"local-name\",3,!1,!0,function(a,b){return(a=b?Ya(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():\"\"},0,1,!0);R(\"name\",3,!1,!0,function(a,b){return(a=b?Ya(b.a(a)):a.a)?a.nodeName.toLowerCase():\"\"},0,1,!0);R(\"namespace-uri\",3,!0,!1,function(){return\"\"},0,1,!0);\nR(\"normalize-space\",3,!1,!0,function(a,b){return(b?M(b,a):B(a.a)).replace(/[\\s\\xa0]+/g,\" \").replace(/^\\s+|\\s+$/g,\"\")},0,1);R(\"not\",2,!1,!1,function(a,b){return!N(b,a)},1);R(\"number\",1,!1,!0,function(a,b){return b?L(b,a):+B(a.a)},0,1);R(\"position\",1,!0,!1,function(a){return a.b},0);R(\"round\",1,!1,!1,function(a,b){return Math.round(L(b,a))},1);R(\"starts-with\",2,!1,!1,function(a,b,c){b=M(b,a);a=M(c,a);return 0==b.lastIndexOf(a,0)},2);R(\"string\",3,!1,!0,function(a,b){return b?M(b,a):B(a.a)},0,1);\nR(\"string-length\",1,!1,!0,function(a,b){return(b?M(b,a):B(a.a)).length},0,1);R(\"substring\",3,!1,!1,function(a,b,c,d){c=L(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return\"\";d=d?L(d,a):Infinity;if(isNaN(d)||-Infinity===d)return\"\";c=Math.round(c)-1;var e=Math.max(c,0);a=M(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);R(\"substring-after\",3,!1,!1,function(a,b,c){b=M(b,a);a=M(c,a);c=b.indexOf(a);return-1==c?\"\":b.substring(c+a.length)},2);\nR(\"substring-before\",3,!1,!1,function(a,b,c){b=M(b,a);a=M(c,a);a=b.indexOf(a);return-1==a?\"\":b.substring(0,a)},2);R(\"sum\",1,!1,!1,function(a,b){a=H(b.a(a));b=0;for(var c=I(a);c;c=I(a))b+=+B(c);return b},1,1,!0);R(\"translate\",3,!1,!1,function(a,b,c,d){b=M(b,a);c=M(c,a);var e=M(d,a);a={};for(d=0;da.length)throw Error(\"Unclosed literal string\");return new lb(a)}\nfunction Ib(a){var b=[];if(qb(z(a.a))){var c=A(a.a);var d=z(a.a);if(\"/\"==c&&(Ia(a.a)||\".\"!=d&&\"..\"!=d&&\"@\"!=d&&\"*\"!=d&&!/(?![0-9])[\\w]/.test(d)))return new S;d=new S;W(a,\"Missing next location step.\");c=Jb(a,c);b.push(c)}else{a:{c=z(a.a);d=c.charAt(0);switch(d){case \"$\":throw Error(\"Variable reference not allowed in HTML XPath\");case \"(\":A(a.a);c=Db(a);W(a,'unclosed \"(\"');Fb(a,\")\");break;case '\"':case \"'\":c=Hb(a);break;default:if(isNaN(+c))if(!kb(c)&&/(?![0-9])[\\w]/.test(d)&&\"(\"==z(a.a,1)){c=A(a.a);\nc=jb[c]||null;A(a.a);for(d=[];\")\"!=z(a.a);){W(a,\"Missing function argument list.\");d.push(Db(a));if(\",\"!=z(a.a))break;A(a.a)}W(a,\"Unclosed function argument list.\");Gb(a);c=new hb(c,d)}else{c=null;break a}else c=new mb(+A(a.a))}\"[\"==z(a.a)&&(d=new tb(Kb(a)),c=new fb(c,d))}if(c)if(qb(z(a.a)))d=c;else return c;else c=Jb(a,\"/\"),d=new pb,b.push(c)}for(;qb(z(a.a));)c=A(a.a),W(a,\"Missing next location step.\"),c=Jb(a,c),b.push(c);return new nb(d,b)}\nfunction Jb(a,b){if(\"/\"!=b&&\"//\"!=b)throw Error('Step op should be \"/\" or \"//\"');if(\".\"==z(a.a)){var c=new U(zb,new G(\"node\"));A(a.a);return c}if(\"..\"==z(a.a))return c=new U(yb,new G(\"node\")),A(a.a),c;if(\"@\"==z(a.a)){var d=ob;A(a.a);W(a,\"Missing attribute name\")}else if(\"::\"==z(a.a,1)){if(!/(?![0-9])[\\w]/.test(z(a.a).charAt(0)))throw Error(\"Bad token: \"+A(a.a));var e=A(a.a);d=xb[e]||null;if(!d)throw Error(\"No axis with name: \"+e);A(a.a);W(a,\"Missing node name\")}else d=ub;e=z(a.a);if(/(?![0-9])[\\w\\*]/.test(e.charAt(0)))if(\"(\"==\nz(a.a,1)){if(!kb(e))throw Error(\"Invalid node type: \"+e);e=A(a.a);if(!kb(e))throw Error(\"Invalid type name: \"+e);Fb(a,\"(\");W(a,\"Bad nodetype\");var f=z(a.a).charAt(0),g=null;if('\"'==f||\"'\"==f)g=Hb(a);W(a,\"Bad nodetype\");Gb(a);e=new G(e,g)}else if(e=A(a.a),f=e.indexOf(\":\"),-1==f)e=new F(e);else{g=e.substring(0,f);if(\"*\"==g)var h=\"*\";else if(h=a.b(g),!h)throw Error(\"Namespace prefix not declared: \"+g);e=e.substr(f+1);e=new F(e,h)}else throw Error(\"Bad token: \"+A(a.a));a=new tb(Kb(a),d.s);return c||new U(d,\ne,a,\"//\"==b)}function Kb(a){for(var b=[];\"[\"==z(a.a);){A(a.a);W(a,\"Missing predicate expression.\");var c=Db(a);b.push(c);W(a,\"Unclosed predicate expression.\");Fb(a,\"]\")}return b}function Eb(a){if(\"-\"==z(a.a))return A(a.a),new Ab(Eb(a));var b=Ib(a);if(\"|\"!=z(a.a))a=b;else{for(b=[b];\"|\"==A(a.a);)W(a,\"Missing next union location path.\"),b.push(Ib(a));a.a.a--;a=new Bb(b)}return a};function Lb(a){switch(a.nodeType){case 1:return ha(Mb,a);case 9:return Lb(a.documentElement);case 11:case 10:case 6:case 12:return Nb;default:return a.parentNode?Lb(a.parentNode):Nb}}function Nb(){return null}function Mb(a,b){if(a.prefix==b)return a.namespaceURI||\"http://www.w3.org/1999/xhtml\";var c=a.getAttributeNode(\"xmlns:\"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?Mb(a.parentNode,b):null};function Ob(a,b){if(!a.length)throw Error(\"Empty XPath expression.\");a=Fa(a);if(Ia(a))throw Error(\"Invalid XPath expression.\");b?\"function\"==ca(b)||(b=fa(b.lookupNamespaceURI,b)):b=function(){return null};var c=Db(new Cb(a,b));if(!Ia(a))throw Error(\"Bad token: \"+A(a));this.evaluate=function(d,e){d=c.a(new m(d));return new X(d,e)}}\nfunction X(a,b){if(0==b)if(a instanceof E)b=4;else if(\"string\"==typeof a)b=2;else if(\"number\"==typeof a)b=1;else if(\"boolean\"==typeof a)b=3;else throw Error(\"Unexpected evaluation result.\");if(2!=b&&1!=b&&3!=b&&!(a instanceof E))throw Error(\"value could not be converted to the specified type\");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof E?Za(a):\"\"+a;break;case 1:this.numberValue=a instanceof E?+Za(a):+a;break;case 3:this.booleanValue=a instanceof E?0=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error(\"snapshotItem called with wrong result type\");return g>=d.length||\n0>g?null:d[g]}}X.ANY_TYPE=0;X.NUMBER_TYPE=1;X.STRING_TYPE=2;X.BOOLEAN_TYPE=3;X.UNORDERED_NODE_ITERATOR_TYPE=4;X.ORDERED_NODE_ITERATOR_TYPE=5;X.UNORDERED_NODE_SNAPSHOT_TYPE=6;X.ORDERED_NODE_SNAPSHOT_TYPE=7;X.ANY_UNORDERED_NODE_TYPE=8;X.FIRST_ORDERED_NODE_TYPE=9;function Pb(a){this.lookupNamespaceURI=Lb(a)}\nfunction Qb(a,b){a=a||k;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=X,c.evaluate=function(d,e,f,g){return(new Ob(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new Ob(d,e)},c.createNSResolver=function(d){return new Pb(d)}}ba(\"wgxpath.install\",Qb);ba(\"wgxpath.install\",Qb);var Rb=pa(),Sb=ra()||u(\"iPod\"),Tb=u(\"iPad\"),Ub=u(\"Android\")&&!(qa()||pa()||u(\"Opera\")||u(\"Silk\")),Vb=qa(),Wb=u(\"Safari\")&&!(qa()||u(\"Coast\")||u(\"Opera\")||u(\"Edge\")||u(\"Edg/\")||u(\"OPR\")||pa()||u(\"Silk\")||u(\"Android\"))&&!(ra()||u(\"iPad\")||u(\"iPod\"));function Y(a){return(a=a.exec(t))?a[1]:\"\"}(function(){if(Rb)return Y(/Firefox\\/([0-9.]+)/);if(v||ta||sa)return xa;if(Vb)return ra()||u(\"iPad\")||u(\"iPod\")?Y(/CriOS\\/([0-9.]+)/):Y(/Chrome\\/([0-9.]+)/);if(Wb&&!(ra()||u(\"iPad\")||u(\"iPod\")))return Y(/Version\\/([0-9.]+)/);if(Sb||Tb){var a=/Version\\/(\\S+).*Mobile\\/(\\S+)/.exec(t);if(a)return a[1]+\".\"+a[2]}else if(Ub)return(a=Y(/Android\\s+([0-9.]+)/))?a:Y(/Version\\/([0-9.]+)/);return\"\"})();function Z(a,b){b&&\"string\"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};var Xb=\"BUTTON INPUT OPTGROUP OPTION SELECT TEXTAREA\".split(\" \");function Yb(a){return r(Xb,function(b){return Z(a,b)})?a.disabled?!1:a.parentNode&&1==a.parentNode.nodeType&&Z(a,\"OPTGROUP\")||Z(a,\"OPTION\")?Yb(a.parentNode):!Oa(a,function(b){var c=b.parentNode;if(c&&Z(c,\"FIELDSET\")&&c.disabled){if(!Z(b,\"LEGEND\"))return!0;for(;b=void 0!==b.previousElementSibling?b.previousElementSibling:Ja(b.previousSibling);)if(Z(b,\"LEGEND\"))return!0}return!1}):!0};ba(\"_\",Yb);; return this._.apply(null,arguments);}).apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);}\n", }; atom.getVisibleText = async function (element, window) { @@ -40,10 +39,6 @@ atom.isElementDisplayed = function (element, window) { return executeInContent("isElementDisplayed", element, window); } -atom.isElementEnabled = function (element, window) { - return executeInContent("isElementEnabled", element, window); -} - function executeInContent(name, element, window) { const sandbox = lazy.sandbox.createMutable(window); diff --git a/remote/marionette/driver.sys.mjs b/remote/marionette/driver.sys.mjs index 6b5b1cf082..95b1352ab3 100644 --- a/remote/marionette/driver.sys.mjs +++ b/remote/marionette/driver.sys.mjs @@ -29,7 +29,7 @@ ChromeUtils.defineESModuleGetters(lazy, { MarionettePrefs: "chrome://remote/content/marionette/prefs.sys.mjs", modal: "chrome://remote/content/shared/Prompt.sys.mjs", navigate: "chrome://remote/content/marionette/navigate.sys.mjs", - permissions: "chrome://remote/content/marionette/permissions.sys.mjs", + permissions: "chrome://remote/content/shared/Permissions.sys.mjs", pprint: "chrome://remote/content/shared/Format.sys.mjs", print: "chrome://remote/content/shared/PDF.sys.mjs", PromptListener: @@ -1373,9 +1373,20 @@ GeckoDriver.prototype.switchToFrame = async function (cmd) { byFrame = el; } - const { browsingContext } = await this.getActor({ top }).switchToFrame( - byFrame || id - ); + // If the current context changed during the switchToFrame call, attempt to + // call switchToFrame again until the browsing context remains stable. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1786640#c11 + let browsingContext; + for (let i = 0; i < 5; i++) { + const currentBrowsingContext = this.currentSession.contentBrowsingContext; + ({ browsingContext } = await this.getActor({ top }).switchToFrame( + byFrame || id + )); + + if (currentBrowsingContext == this.currentSession.contentBrowsingContext) { + break; + } + } this.currentSession.contentBrowsingContext = browsingContext; }; @@ -3346,22 +3357,16 @@ GeckoDriver.prototype.setPermission = async function (cmd) { const { descriptor, state, oneRealm = false } = cmd.parameters; const browsingContext = lazy.assert.open(this.getBrowsingContext()); - // XXX: We currently depend on camera/microphone tests throwing UnsupportedOperationError, - // the fix is ongoing in bug 1609427. - if (["camera", "microphone"].includes(descriptor.name)) { - throw new lazy.error.UnsupportedOperationError( - "setPermission: camera and microphone permissions are currently unsupported" - ); - } + lazy.permissions.validatePermission(descriptor.name); - // XXX: Allowing this permission causes timing related Android crash, see also bug 1878741 + // Bug 1878741: Allowing this permission causes timing related Android crash. if (descriptor.name === "notifications") { if (Services.prefs.getBoolPref("notification.prompt.testing", false)) { // Okay, do nothing. The notifications module will work without permission. return; } throw new lazy.error.UnsupportedOperationError( - "setPermission: expected notification.prompt.testing to be set" + `Setting "descriptor.name" "notifications" expected "notification.prompt.testing" preference to be set` ); } @@ -3378,7 +3383,26 @@ GeckoDriver.prototype.setPermission = async function (cmd) { lazy.assert.boolean(oneRealm); - lazy.permissions.set(params.type, params.state, oneRealm, browsingContext); + if (!lazy.MarionettePrefs.setPermissionEnabled) { + throw new lazy.error.UnsupportedOperationError( + "'Set Permission' is not available" + ); + } + + let origin = browsingContext.currentURI.prePath; + + // storage-access is a special case. + if (descriptor.name === "storage-access") { + origin = browsingContext.top.currentURI.prePath; + + params = { + type: lazy.permissions.getStorageAccessPermissionsType( + browsingContext.currentWindowGlobal.documentURI + ), + }; + } + + lazy.permissions.set(params, state, origin); }; /** diff --git a/remote/marionette/interaction.sys.mjs b/remote/marionette/interaction.sys.mjs index d710f2eb46..7fa7df0abe 100644 --- a/remote/marionette/interaction.sys.mjs +++ b/remote/marionette/interaction.sys.mjs @@ -9,7 +9,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { setTimeout: "resource://gre/modules/Timer.sys.mjs", - accessibility: "chrome://remote/content/marionette/accessibility.sys.mjs", + accessibility: + "chrome://remote/content/shared/webdriver/Accessibility.sys.mjs", atom: "chrome://remote/content/marionette/atom.sys.mjs", dom: "chrome://remote/content/shared/DOM.sys.mjs", error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", @@ -236,9 +237,7 @@ async function webdriverClickElement(el, a11y) { } async function chromeClick(el, a11y) { - const win = getWindow(el); - - if (!(await lazy.atom.isElementEnabled(el, win))) { + if (!(await lazy.dom.isEnabled(el))) { throw new lazy.error.InvalidElementStateError("Element is not enabled"); } @@ -266,7 +265,7 @@ async function seleniumClickElement(el, a11y) { throw new lazy.error.ElementNotInteractableError(); } - if (!(await lazy.atom.isElementEnabled(el, win))) { + if (!(await lazy.dom.isEnabled(el))) { throw new lazy.error.InvalidElementStateError("Element is not enabled"); } @@ -775,7 +774,7 @@ interaction.isElementEnabled = async function (el, strict = false) { ) { enabled = false; } else { - enabled = await lazy.atom.isElementEnabled(el, win); + enabled = await lazy.dom.isEnabled(el); } let a11y = lazy.accessibility.get(strict); diff --git a/remote/marionette/jar.mn b/remote/marionette/jar.mn index 21c37ee455..2c04e0866c 100644 --- a/remote/marionette/jar.mn +++ b/remote/marionette/jar.mn @@ -4,7 +4,6 @@ remote.jar: % content remote %content/ - content/marionette/accessibility.sys.mjs (accessibility.sys.mjs) content/marionette/actors/MarionetteCommandsChild.sys.mjs (actors/MarionetteCommandsChild.sys.mjs) content/marionette/actors/MarionetteCommandsParent.sys.mjs (actors/MarionetteCommandsParent.sys.mjs) content/marionette/actors/MarionetteEventsChild.sys.mjs (actors/MarionetteEventsChild.sys.mjs) @@ -24,7 +23,6 @@ remote.jar: content/marionette/message.sys.mjs (message.sys.mjs) content/marionette/navigate.sys.mjs (navigate.sys.mjs) content/marionette/packets.sys.mjs (packets.sys.mjs) - content/marionette/permissions.sys.mjs (permissions.sys.mjs) content/marionette/prefs.sys.mjs (prefs.sys.mjs) content/marionette/reftest.sys.mjs (reftest.sys.mjs) content/marionette/reftest.xhtml (chrome/reftest.xhtml) diff --git a/remote/marionette/permissions.sys.mjs b/remote/marionette/permissions.sys.mjs deleted file mode 100644 index 5238bf8347..0000000000 --- a/remote/marionette/permissions.sys.mjs +++ /dev/null @@ -1,95 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const lazy = {}; - -ChromeUtils.defineESModuleGetters(lazy, { - error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", - MarionettePrefs: "chrome://remote/content/marionette/prefs.sys.mjs", -}); - -/** @namespace */ -export const permissions = {}; - -function mapToInternalPermissionParameters(browsingContext, permissionType) { - const currentURI = browsingContext.currentWindowGlobal.documentURI; - - // storage-access is quite special... - if (permissionType === "storage-access") { - const thirdPartyPrincipalSite = Services.eTLD.getSite(currentURI); - - const topLevelURI = browsingContext.top.currentWindowGlobal.documentURI; - const topLevelPrincipal = - Services.scriptSecurityManager.createContentPrincipal(topLevelURI, {}); - - return { - name: "3rdPartyFrameStorage^" + thirdPartyPrincipalSite, - principal: topLevelPrincipal, - }; - } - - const currentPrincipal = - Services.scriptSecurityManager.createContentPrincipal(currentURI, {}); - - return { - name: permissionType, - principal: currentPrincipal, - }; -} - -/** - * Set a permission's state. - * Note: Currently just a shim to support testdriver's set_permission. - * - * @param {object} permissionType - * The Gecko internal permission type - * @param {string} state - * State of the permission. It can be `granted`, `denied` or `prompt`. - * @param {boolean} oneRealm - * Currently ignored - * @param {browsingContext=} browsingContext - * Current browsing context object - * @throws {UnsupportedOperationError} - * If `marionette.setpermission.enabled` is not set or - * an unsupported permission is used. - */ -permissions.set = function (permissionType, state, oneRealm, browsingContext) { - if (!lazy.MarionettePrefs.setPermissionEnabled) { - throw new lazy.error.UnsupportedOperationError( - "'Set Permission' is not available" - ); - } - - const { name, principal } = mapToInternalPermissionParameters( - browsingContext, - permissionType - ); - - switch (state) { - case "granted": { - Services.perms.addFromPrincipal( - principal, - name, - Services.perms.ALLOW_ACTION - ); - return; - } - case "denied": { - Services.perms.addFromPrincipal( - principal, - name, - Services.perms.DENY_ACTION - ); - return; - } - case "prompt": { - Services.perms.removeFromPrincipal(principal, name); - return; - } - default: - throw new lazy.error.UnsupportedOperationError( - "Unrecognized permission keyword for 'Set Permission' operation" - ); - } -}; diff --git a/remote/shared/Capture.sys.mjs b/remote/shared/Capture.sys.mjs index ec34d09aba..a9a20c0b81 100644 --- a/remote/shared/Capture.sys.mjs +++ b/remote/shared/Capture.sys.mjs @@ -74,7 +74,7 @@ capture.canvas = async function ( ) { // FIXME(bug 1761032): This looks a bit sketchy, overrideDPPX doesn't // influence rendering... - const scale = win.browsingContext.overrideDPPX || win.devicePixelRatio; + const scale = browsingContext.overrideDPPX || win.devicePixelRatio; let canvasHeight = height * scale; let canvasWidth = width * scale; diff --git a/remote/shared/DOM.sys.mjs b/remote/shared/DOM.sys.mjs index 664f02328c..51c9298183 100644 --- a/remote/shared/DOM.sys.mjs +++ b/remote/shared/DOM.sys.mjs @@ -622,24 +622,14 @@ dom.isDisabled = function (el) { return false; } - switch (el.localName) { - case "option": - case "optgroup": - if (el.disabled) { - return true; - } - let parent = dom.findClosest(el, "optgroup,select"); - return dom.isDisabled(parent); - - case "button": - case "input": - case "select": - case "textarea": - return el.disabled; - - default: - return false; + // Selenium expects that even an enabled "option" element that is a child + // of a disabled "optgroup" or "select" element to be disabled. + if (["optgroup", "option"].includes(el.localName) && !el.disabled) { + const parent = dom.findClosest(el, "optgroup,select"); + return dom.isDisabled(parent); } + + return el.matches(":disabled"); }; /** @@ -1064,6 +1054,16 @@ dom.isElement = function (obj) { return dom.isDOMElement(obj) || dom.isXULElement(obj); }; +dom.isEnabled = function (el) { + let enabled = false; + + if (el.ownerDocument.contentType !== "text/xml") { + enabled = !dom.isDisabled(el); + } + + return enabled; +}; + /** * Returns the shadow root of an element. * diff --git a/remote/shared/Navigate.sys.mjs b/remote/shared/Navigate.sys.mjs index 9b72c0dfbf..cdb23b54c7 100644 --- a/remote/shared/Navigate.sys.mjs +++ b/remote/shared/Navigate.sys.mjs @@ -91,9 +91,14 @@ export async function waitForInitialNavigationCompleted( isInitial = browsingContext.currentWindowGlobal.isInitialDocument; } + const isLoadingDocument = listener.isLoadingDocument; + lazy.logger.trace( + lazy.truncate`[${browsingContext.id}] Wait for initial navigation: isInitial=${isInitial}, isLoadingDocument=${isLoadingDocument}` + ); + // If the current document is not the initial "about:blank" and is also // no longer loading, assume the navigation is done and return. - if (!isInitial && !listener.isLoadingDocument) { + if (!isInitial && !isLoadingDocument) { lazy.logger.trace( lazy.truncate`[${browsingContext.id}] Document already finished loading: ${browsingContext.currentURI?.spec}` ); diff --git a/remote/shared/NetworkRequest.sys.mjs b/remote/shared/NetworkRequest.sys.mjs new file mode 100644 index 0000000000..6524132752 --- /dev/null +++ b/remote/shared/NetworkRequest.sys.mjs @@ -0,0 +1,254 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + NetworkUtils: + "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs", + + notifyNavigationStarted: + "chrome://remote/content/shared/NavigationManager.sys.mjs", + TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", +}); + +/** + * The NetworkRequest class is a wrapper around the internal channel which + * provides getters and methods closer to fetch's response concept + * (https://fetch.spec.whatwg.org/#concept-response). + */ +export class NetworkRequest { + #channel; + #contextId; + #navigationId; + #navigationManager; + #postData; + #rawHeaders; + #redirectCount; + #requestId; + #timedChannel; + #wrappedChannel; + + /** + * + * @param {nsIChannel} channel + * The channel for the request. + * @param {object} params + * @param {NavigationManager} params.navigationManager + * The NavigationManager where navigations for the current session are + * monitored. + * @param {string=} params.rawHeaders + * The request's raw (ie potentially compressed) headers + */ + constructor(channel, params) { + const { navigationManager, rawHeaders = "" } = params; + + this.#channel = channel; + this.#navigationManager = navigationManager; + this.#rawHeaders = rawHeaders; + + this.#timedChannel = this.#channel.QueryInterface(Ci.nsITimedChannel); + this.#wrappedChannel = ChannelWrapper.get(channel); + + this.#redirectCount = this.#timedChannel.redirectCount; + // The wrappedChannel id remains identical across redirects, whereas + // nsIChannel.channelId is different for each and every request. + this.#requestId = this.#wrappedChannel.id.toString(); + + this.#contextId = this.#getContextId(); + this.#navigationId = this.#getNavigationId(); + } + + get contextId() { + return this.#contextId; + } + + get errorText() { + // TODO: Update with a proper error text. Bug 1873037. + return ChromeUtils.getXPCOMErrorName(this.#channel.status); + } + + get headersSize() { + // TODO: rawHeaders will not be updated after modifying the headers via + // request interception. Need to find another way to retrieve the + // information dynamically. + return this.#rawHeaders.length; + } + + get method() { + return this.#channel.requestMethod; + } + + get navigationId() { + return this.#navigationId; + } + + get postDataSize() { + return this.#postData ? this.#postData.size : 0; + } + + get redirectCount() { + return this.#redirectCount; + } + + get requestId() { + return this.#requestId; + } + + get serializedURL() { + return this.#channel.URI.spec; + } + + get wrappedChannel() { + return this.#wrappedChannel; + } + + /** + * Retrieve the Fetch timings for the NetworkRequest. + * + * @returns {object} + * Object with keys corresponding to fetch timing names, and their + * corresponding values. + */ + getFetchTimings() { + const { + channelCreationTime, + redirectStartTime, + redirectEndTime, + dispatchFetchEventStartTime, + cacheReadStartTime, + domainLookupStartTime, + domainLookupEndTime, + connectStartTime, + connectEndTime, + secureConnectionStartTime, + requestStartTime, + responseStartTime, + responseEndTime, + } = this.#timedChannel; + + // fetchStart should be the post-redirect start time, which should be the + // first non-zero timing from: dispatchFetchEventStart, cacheReadStart and + // domainLookupStart. See https://www.w3.org/TR/navigation-timing-2/#processing-model + const fetchStartTime = + dispatchFetchEventStartTime || + cacheReadStartTime || + domainLookupStartTime; + + // Bug 1805478: Per spec, the origin time should match Performance API's + // timeOrigin for the global which initiated the request. This is not + // available in the parent process, so for now we will use 0. + const timeOrigin = 0; + + return { + timeOrigin, + requestTime: this.#convertTimestamp(channelCreationTime, timeOrigin), + redirectStart: this.#convertTimestamp(redirectStartTime, timeOrigin), + redirectEnd: this.#convertTimestamp(redirectEndTime, timeOrigin), + fetchStart: this.#convertTimestamp(fetchStartTime, timeOrigin), + dnsStart: this.#convertTimestamp(domainLookupStartTime, timeOrigin), + dnsEnd: this.#convertTimestamp(domainLookupEndTime, timeOrigin), + connectStart: this.#convertTimestamp(connectStartTime, timeOrigin), + connectEnd: this.#convertTimestamp(connectEndTime, timeOrigin), + tlsStart: this.#convertTimestamp(secureConnectionStartTime, timeOrigin), + tlsEnd: this.#convertTimestamp(connectEndTime, timeOrigin), + requestStart: this.#convertTimestamp(requestStartTime, timeOrigin), + responseStart: this.#convertTimestamp(responseStartTime, timeOrigin), + responseEnd: this.#convertTimestamp(responseEndTime, timeOrigin), + }; + } + + /** + * Retrieve the list of headers for the NetworkRequest. + * + * @returns {Array.Array} + * Array of (name, value) tuples. + */ + getHeadersList() { + const headers = []; + + this.#channel.visitRequestHeaders({ + visitHeader(name, value) { + // The `Proxy-Authorization` header even though it appears on the channel is not + // actually sent to the server for non CONNECT requests after the HTTP/HTTPS tunnel + // is setup by the proxy. + if (name == "Proxy-Authorization") { + return; + } + headers.push([name, value]); + }, + }); + + return headers; + } + + /** + * Update the postData for this NetworkRequest. This is currently forwarded + * by the DevTools' NetworkObserver. + * + * TODO: We should read this information dynamically from the channel so that + * we can get updated information in case it was modified via network + * interception. + * + * @param {object} postData + * The request POST data. + */ + setPostData(postData) { + this.#postData = postData; + } + + /** + * Convert the provided request timing to a timing relative to the beginning + * of the request. All timings are numbers representing high definition + * timestamps. + * + * @param {number} timing + * High definition timestamp for a request timing relative from the time + * origin. + * @param {number} requestTime + * High definition timestamp for the request start time relative from the + * time origin. + * + * @returns {number} + * High definition timestamp for the request timing relative to the start + * time of the request, or 0 if the provided timing was 0. + */ + #convertTimestamp(timing, requestTime) { + if (timing == 0) { + return 0; + } + + return timing - requestTime; + } + + #getContextId() { + const id = lazy.NetworkUtils.getChannelBrowsingContextID(this.#channel); + const browsingContext = BrowsingContext.get(id); + return lazy.TabManager.getIdForBrowsingContext(browsingContext); + } + + #getNavigationId() { + if (!this.#channel.isMainDocumentChannel) { + return null; + } + + const browsingContext = lazy.TabManager.getBrowsingContextById( + this.#contextId + ); + + let navigation = + this.#navigationManager.getNavigationForBrowsingContext(browsingContext); + + // `onBeforeRequestSent` might be too early for the NavigationManager. + // If there is no ongoing navigation, create one ourselves. + // TODO: Bug 1835704 to detect navigations earlier and avoid this. + if (!navigation || navigation.finished) { + navigation = lazy.notifyNavigationStarted({ + contextDetails: { context: browsingContext }, + url: this.serializedURL, + }); + } + + return navigation ? navigation.navigationId : null; + } +} diff --git a/remote/shared/NetworkResponse.sys.mjs b/remote/shared/NetworkResponse.sys.mjs new file mode 100644 index 0000000000..45a03fb445 --- /dev/null +++ b/remote/shared/NetworkResponse.sys.mjs @@ -0,0 +1,131 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + NetworkUtils: + "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs", +}); + +/** + * The NetworkResponse class is a wrapper around the internal channel which + * provides getters and methods closer to fetch's response concept + * (https://fetch.spec.whatwg.org/#concept-response). + */ +export class NetworkResponse { + #channel; + #decodedBodySize; + #encodedBodySize; + #fromCache; + #headersTransmittedSize; + #status; + #statusMessage; + #totalTransmittedSize; + #wrappedChannel; + + /** + * + * @param {nsIChannel} channel + * The channel for the response. + * @param {object} params + * @param {boolean} params.fromCache + * Whether the response was read from the cache or not. + * @param {string=} params.rawHeaders + * The response's raw (ie potentially compressed) headers + */ + constructor(channel, params) { + this.#channel = channel; + const { fromCache, rawHeaders = "" } = params; + this.#fromCache = fromCache; + this.#wrappedChannel = ChannelWrapper.get(channel); + + this.#decodedBodySize = 0; + this.#encodedBodySize = 0; + this.#headersTransmittedSize = rawHeaders.length; + this.#totalTransmittedSize = rawHeaders.length; + + // TODO: responseStatus and responseStatusText are sometimes inconsistent. + // For instance, they might be (304, Not Modified) when retrieved during the + // responseStarted event, and then (200, OK) during the responseCompleted + // event. + // For now consider them as immutable and store them on startup. + this.#status = this.#channel.responseStatus; + this.#statusMessage = this.#channel.responseStatusText; + } + + get decodedBodySize() { + return this.#decodedBodySize; + } + + get encodedBodySize() { + return this.#encodedBodySize; + } + + get headersTransmittedSize() { + return this.#headersTransmittedSize; + } + + get fromCache() { + return this.#fromCache; + } + + get protocol() { + return lazy.NetworkUtils.getProtocol(this.#channel); + } + + get serializedURL() { + return this.#channel.URI.spec; + } + + get status() { + return this.#status; + } + + get statusMessage() { + return this.#statusMessage; + } + + get totalTransmittedSize() { + return this.#totalTransmittedSize; + } + + addResponseContent(responseContent) { + this.#decodedBodySize = responseContent.decodedBodySize; + this.#encodedBodySize = responseContent.bodySize; + this.#totalTransmittedSize = responseContent.transferredSize; + } + + getComputedMimeType() { + // TODO: DevTools NetworkObserver is computing a similar value in + // addResponseContent, but uses an inconsistent implementation in + // addResponseStart. This approach can only be used as early as in + // addResponseHeaders. We should move this logic to the NetworkObserver and + // expose mimeType in addResponseStart. Bug 1809670. + let mimeType = ""; + + try { + mimeType = this.#wrappedChannel.contentType; + const contentCharset = this.#channel.contentCharset; + if (contentCharset) { + mimeType += `;charset=${contentCharset}`; + } + } catch (e) { + // Ignore exceptions when reading contentType/contentCharset + } + + return mimeType; + } + + getHeadersList() { + const headers = []; + + this.#channel.visitOriginalResponseHeaders({ + visitHeader(name, value) { + headers.push([name, value]); + }, + }); + + return headers; + } +} diff --git a/remote/shared/Permissions.sys.mjs b/remote/shared/Permissions.sys.mjs new file mode 100644 index 0000000000..50996bf701 --- /dev/null +++ b/remote/shared/Permissions.sys.mjs @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", +}); + +/** @namespace */ +export const permissions = {}; + +/** + * Get a permission type for the "storage-access" permission. + * + * @param {nsIURI} uri + * The URI to use for building the permission type. + * + * @returns {string} permissionType + * The permission type for the "storage-access" permission. + */ +permissions.getStorageAccessPermissionsType = function (uri) { + const thirdPartyPrincipalSite = Services.eTLD.getSite(uri); + return "3rdPartyFrameStorage^" + thirdPartyPrincipalSite; +}; + +/** + * Set a permission given a permission descriptor, a permission state, + * an origin. + * + * @param {PermissionDescriptor} descriptor + * The descriptor of the permission which will be updated. + * @param {string} state + * State of the permission. It can be `granted`, `denied` or `prompt`. + * @param {string} origin + * The origin which is used as a target for permission update. + * + * @throws {UnsupportedOperationError} + * If state has unsupported value. + */ +permissions.set = function (descriptor, state, origin) { + const principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin); + + switch (state) { + case "granted": { + Services.perms.addFromPrincipal( + principal, + descriptor.type, + Services.perms.ALLOW_ACTION + ); + return; + } + case "denied": { + Services.perms.addFromPrincipal( + principal, + descriptor.type, + Services.perms.DENY_ACTION + ); + return; + } + case "prompt": { + Services.perms.removeFromPrincipal(principal, descriptor.type); + return; + } + default: + throw new lazy.error.UnsupportedOperationError( + "Unrecognized permission keyword for 'Set Permission' operation" + ); + } +}; + +/** + * Validate the permission. + * + * @param {string} permissionName + * The name of the permission which will be validated. + * + * @throws {UnsupportedOperationError} + * If permissionName is not supported. + */ +permissions.validatePermission = function (permissionName) { + // Bug 1609427: PermissionDescriptor for "camera" and "microphone" are not yet implemented. + if (["camera", "microphone"].includes(permissionName)) { + throw new lazy.error.UnsupportedOperationError( + `"descriptor.name" "${permissionName}" is currently unsupported` + ); + } +}; diff --git a/remote/shared/listeners/NetworkEventRecord.sys.mjs b/remote/shared/listeners/NetworkEventRecord.sys.mjs index 72b43e3de1..0f592d62b0 100644 --- a/remote/shared/listeners/NetworkEventRecord.sys.mjs +++ b/remote/shared/listeners/NetworkEventRecord.sys.mjs @@ -4,10 +4,8 @@ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { - NetworkUtils: - "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs", - - TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", + NetworkRequest: "chrome://remote/content/shared/NetworkRequest.sys.mjs", + NetworkResponse: "chrome://remote/content/shared/NetworkResponse.sys.mjs", }); /** @@ -18,17 +16,10 @@ ChromeUtils.defineESModuleGetters(lazy, { * NetworkListener instance which created it. */ export class NetworkEventRecord { - #contextId; #fromCache; - #isMainDocumentChannel; #networkListener; - #redirectCount; - #requestChannel; - #requestData; - #requestId; - #responseChannel; - #responseData; - #wrappedChannel; + #request; + #response; /** * @@ -39,56 +30,21 @@ export class NetworkEventRecord { * The nsIChannel behind this network event. * @param {NetworkListener} networkListener * The NetworkListener which created this NetworkEventRecord. + * @param {NavigationManager} navigationManager + * The NavigationManager which belongs to the same session as this + * NetworkEventRecord. */ - constructor(networkEvent, channel, networkListener) { - this.#requestChannel = channel; - this.#responseChannel = null; + constructor(networkEvent, channel, networkListener, navigationManager) { + this.#request = new lazy.NetworkRequest(channel, { + navigationManager, + rawHeaders: networkEvent.rawHeaders, + }); + this.#response = null; this.#fromCache = networkEvent.fromCache; - this.#isMainDocumentChannel = channel.isMainDocumentChannel; - - this.#wrappedChannel = ChannelWrapper.get(channel); this.#networkListener = networkListener; - // The context ids computed by TabManager have the lifecycle of a navigable - // and can be reused for all the events emitted from this record. - this.#contextId = this.#getContextId(); - - // The wrappedChannel id remains identical across redirects, whereas - // nsIChannel.channelId is different for each and every request. - this.#requestId = this.#wrappedChannel.id.toString(); - - const { cookies, headers } = - lazy.NetworkUtils.fetchRequestHeadersAndCookies(channel); - - // See the RequestData type definition for the full list of properties that - // should be set on this object. - this.#requestData = { - bodySize: null, - cookies, - headers, - headersSize: networkEvent.rawHeaders ? networkEvent.rawHeaders.length : 0, - method: channel.requestMethod, - request: this.#requestId, - timings: {}, - url: channel.URI.spec, - }; - - // See the ResponseData type definition for the full list of properties that - // should be set on this object. - this.#responseData = { - // encoded size (body) - bodySize: null, - content: { - // decoded size - size: null, - }, - // encoded size (headers) - headersSize: null, - url: channel.URI.spec, - }; - // NetworkObserver creates a network event when request headers have been // parsed. // According to the BiDi spec, we should emit beforeRequestSent when adding @@ -113,8 +69,7 @@ export class NetworkEventRecord { * The request POST data. */ addRequestPostData(postData) { - // Only the postData size is needed for RemoteAgent consumers. - this.#requestData.bodySize = postData.size; + this.#request.setPostData(postData); } /** @@ -130,25 +85,11 @@ export class NetworkEventRecord { * @param {string} options.rawHeaders */ addResponseStart(options) { - const { channel, fromCache, rawHeaders = "" } = options; - this.#responseChannel = channel; - - const { headers } = - lazy.NetworkUtils.fetchResponseHeadersAndCookies(channel); - - const headersSize = rawHeaders.length; - this.#responseData = { - ...this.#responseData, - bodySize: 0, - bytesReceived: headersSize, + const { channel, fromCache, rawHeaders } = options; + this.#response = new lazy.NetworkResponse(channel, { + rawHeaders, fromCache: this.#fromCache || !!fromCache, - headers, - headersSize, - mimeType: this.#getMimeType(), - protocol: lazy.NetworkUtils.getProtocol(channel), - status: channel.responseStatus, - statusText: channel.responseStatusText, - }; + }); // This should be triggered when all headers have been received, matching // the WebDriverBiDi response started trigger in `4.6. HTTP-network fetch` @@ -189,25 +130,16 @@ export class NetworkEventRecord { * * Required API for a NetworkObserver event owner. * - * @param {object} response + * @param {object} responseContent * An object which represents the response content. * @param {object} responseInfo * Additional meta data about the response. */ - addResponseContent(response, responseInfo) { - // Update content-related sizes with the latest data from addResponseContent. - this.#responseData = { - ...this.#responseData, - bodySize: response.bodySize, - bytesReceived: response.transferredSize, - content: { - size: response.decodedBodySize, - }, - }; - + addResponseContent(responseContent, responseInfo) { if (responseInfo.blockedReason) { this.#emitFetchError(); } else { + this.#response.addResponseContent(responseContent); this.#emitResponseCompleted(); } } @@ -234,201 +166,37 @@ export class NetworkEventRecord { this.#emitAuthRequired(authCallbacks); } - /** - * Convert the provided request timing to a timing relative to the beginning - * of the request. All timings are numbers representing high definition - * timestamps. - * - * @param {number} timing - * High definition timestamp for a request timing relative from the time - * origin. - * @param {number} requestTime - * High definition timestamp for the request start time relative from the - * time origin. - * @returns {number} - * High definition timestamp for the request timing relative to the start - * time of the request, or 0 if the provided timing was 0. - */ - #convertTimestamp(timing, requestTime) { - if (timing == 0) { - return 0; - } - - return timing - requestTime; - } - #emitAuthRequired(authCallbacks) { - this.#updateDataFromTimedChannel(); - this.#networkListener.emit("auth-required", { authCallbacks, - contextId: this.#contextId, - isNavigationRequest: this.#isMainDocumentChannel, - redirectCount: this.#redirectCount, - requestChannel: this.#requestChannel, - requestData: this.#requestData, - responseChannel: this.#responseChannel, - responseData: this.#responseData, - timestamp: Date.now(), + request: this.#request, + response: this.#response, }); } #emitBeforeRequestSent() { - this.#updateDataFromTimedChannel(); - this.#networkListener.emit("before-request-sent", { - contextId: this.#contextId, - isNavigationRequest: this.#isMainDocumentChannel, - redirectCount: this.#redirectCount, - requestChannel: this.#requestChannel, - requestData: this.#requestData, - timestamp: Date.now(), + request: this.#request, }); } #emitFetchError() { - this.#updateDataFromTimedChannel(); - this.#networkListener.emit("fetch-error", { - contextId: this.#contextId, - // TODO: Update with a proper error text. Bug 1873037. - errorText: ChromeUtils.getXPCOMErrorName(this.#requestChannel.status), - isNavigationRequest: this.#isMainDocumentChannel, - redirectCount: this.#redirectCount, - requestChannel: this.#requestChannel, - requestData: this.#requestData, - timestamp: Date.now(), + request: this.#request, }); } #emitResponseCompleted() { - this.#updateDataFromTimedChannel(); - this.#networkListener.emit("response-completed", { - contextId: this.#contextId, - isNavigationRequest: this.#isMainDocumentChannel, - redirectCount: this.#redirectCount, - requestChannel: this.#requestChannel, - requestData: this.#requestData, - responseChannel: this.#responseChannel, - responseData: this.#responseData, - timestamp: Date.now(), + request: this.#request, + response: this.#response, }); } #emitResponseStarted() { - this.#updateDataFromTimedChannel(); - this.#networkListener.emit("response-started", { - contextId: this.#contextId, - isNavigationRequest: this.#isMainDocumentChannel, - redirectCount: this.#redirectCount, - requestChannel: this.#requestChannel, - requestData: this.#requestData, - responseChannel: this.#responseChannel, - responseData: this.#responseData, - timestamp: Date.now(), + request: this.#request, + response: this.#response, }); } - - #getBrowsingContext() { - const id = lazy.NetworkUtils.getChannelBrowsingContextID( - this.#requestChannel - ); - return BrowsingContext.get(id); - } - - /** - * Retrieve the navigable id for the current browsing context associated to - * the requests' channel. Network events are recorded in the parent process - * so we always expect to be able to use TabManager.getIdForBrowsingContext. - * - * @returns {string} - * The navigable id corresponding to the given browsing context. - */ - #getContextId() { - return lazy.TabManager.getIdForBrowsingContext(this.#getBrowsingContext()); - } - - #getMimeType() { - // TODO: DevTools NetworkObserver is computing a similar value in - // addResponseContent, but uses an inconsistent implementation in - // addResponseStart. This approach can only be used as early as in - // addResponseHeaders. We should move this logic to the NetworkObserver and - // expose mimeType in addResponseStart. Bug 1809670. - let mimeType = ""; - - try { - mimeType = this.#wrappedChannel.contentType; - const contentCharset = this.#requestChannel.contentCharset; - if (contentCharset) { - mimeType += `;charset=${contentCharset}`; - } - } catch (e) { - // Ignore exceptions when reading contentType/contentCharset - } - - return mimeType; - } - - #getTimingsFromTimedChannel(timedChannel) { - const { - channelCreationTime, - redirectStartTime, - redirectEndTime, - dispatchFetchEventStartTime, - cacheReadStartTime, - domainLookupStartTime, - domainLookupEndTime, - connectStartTime, - connectEndTime, - secureConnectionStartTime, - requestStartTime, - responseStartTime, - responseEndTime, - } = timedChannel; - - // fetchStart should be the post-redirect start time, which should be the - // first non-zero timing from: dispatchFetchEventStart, cacheReadStart and - // domainLookupStart. See https://www.w3.org/TR/navigation-timing-2/#processing-model - const fetchStartTime = - dispatchFetchEventStartTime || - cacheReadStartTime || - domainLookupStartTime; - - // Bug 1805478: Per spec, the origin time should match Performance API's - // timeOrigin for the global which initiated the request. This is not - // available in the parent process, so for now we will use 0. - const timeOrigin = 0; - - return { - timeOrigin, - requestTime: this.#convertTimestamp(channelCreationTime, timeOrigin), - redirectStart: this.#convertTimestamp(redirectStartTime, timeOrigin), - redirectEnd: this.#convertTimestamp(redirectEndTime, timeOrigin), - fetchStart: this.#convertTimestamp(fetchStartTime, timeOrigin), - dnsStart: this.#convertTimestamp(domainLookupStartTime, timeOrigin), - dnsEnd: this.#convertTimestamp(domainLookupEndTime, timeOrigin), - connectStart: this.#convertTimestamp(connectStartTime, timeOrigin), - connectEnd: this.#convertTimestamp(connectEndTime, timeOrigin), - tlsStart: this.#convertTimestamp(secureConnectionStartTime, timeOrigin), - tlsEnd: this.#convertTimestamp(connectEndTime, timeOrigin), - requestStart: this.#convertTimestamp(requestStartTime, timeOrigin), - responseStart: this.#convertTimestamp(responseStartTime, timeOrigin), - responseEnd: this.#convertTimestamp(responseEndTime, timeOrigin), - }; - } - - /** - * Update the timings and the redirect count from the nsITimedChannel - * corresponding to the current channel. This should be called before emitting - * any event from this class. - */ - #updateDataFromTimedChannel() { - const timedChannel = this.#requestChannel.QueryInterface( - Ci.nsITimedChannel - ); - this.#redirectCount = timedChannel.redirectCount; - this.#requestData.timings = this.#getTimingsFromTimedChannel(timedChannel); - } } diff --git a/remote/shared/listeners/NetworkListener.sys.mjs b/remote/shared/listeners/NetworkListener.sys.mjs index 500d2005dc..d0d6d0e44f 100644 --- a/remote/shared/listeners/NetworkListener.sys.mjs +++ b/remote/shared/listeners/NetworkListener.sys.mjs @@ -44,11 +44,13 @@ ChromeUtils.defineESModuleGetters(lazy, { export class NetworkListener { #devtoolsNetworkObserver; #listening; + #navigationManager; - constructor() { + constructor(navigationManager) { lazy.EventEmitter.decorate(this); this.#listening = false; + this.#navigationManager = navigationManager; } destroy() { @@ -104,6 +106,11 @@ export class NetworkListener { }; #onNetworkEvent = (networkEvent, channel) => { - return new lazy.NetworkEventRecord(networkEvent, channel, this); + return new lazy.NetworkEventRecord( + networkEvent, + channel, + this, + this.#navigationManager + ); }; } diff --git a/remote/shared/listeners/test/browser/browser_NetworkListener.js b/remote/shared/listeners/test/browser/browser_NetworkListener.js index cc1b42f2fc..211ccef49c 100644 --- a/remote/shared/listeners/test/browser/browser_NetworkListener.js +++ b/remote/shared/listeners/test/browser/browser_NetworkListener.js @@ -2,6 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +const { NavigationManager } = ChromeUtils.importESModule( + "chrome://remote/content/shared/NavigationManager.sys.mjs" +); const { NetworkListener } = ChromeUtils.importESModule( "chrome://remote/content/shared/listeners/NetworkListener.sys.mjs" ); @@ -10,7 +13,10 @@ const { TabManager } = ChromeUtils.importESModule( ); add_task(async function test_beforeRequestSent() { - const listener = new NetworkListener(); + const navigationManager = new NavigationManager(); + navigationManager.startMonitoring(); + + const listener = new NetworkListener(navigationManager); const events = []; const onEvent = (name, data) => events.push(data); listener.on("before-request-sent", onEvent); @@ -54,10 +60,14 @@ add_task(async function test_beforeRequestSent() { gBrowser.removeTab(tab2); listener.off("before-request-sent", onEvent); listener.destroy(); + navigationManager.destroy(); }); add_task(async function test_beforeRequestSent_newTab() { - const listener = new NetworkListener(); + const navigationManager = new NavigationManager(); + navigationManager.startMonitoring(); + + const listener = new NetworkListener(navigationManager); const onBeforeRequestSent = listener.once("before-request-sent"); listener.startListening(); @@ -76,10 +86,14 @@ add_task(async function test_beforeRequestSent_newTab() { "https://example.com/document-builder.sjs?html=tab" ); gBrowser.removeTab(tab); + navigationManager.destroy(); }); add_task(async function test_fetchError() { - const listener = new NetworkListener(); + const navigationManager = new NavigationManager(); + navigationManager.startMonitoring(); + + const listener = new NetworkListener(navigationManager); const onFetchError = listener.once("fetch-error"); listener.startListening(); @@ -90,11 +104,16 @@ add_task(async function test_fetchError() { const event = await onFetchError; assertNetworkEvent(event, contextId, "https://not_a_valid_url/"); - is(event.errorText, "NS_ERROR_UNKNOWN_HOST"); + is(event.request.errorText, "NS_ERROR_UNKNOWN_HOST"); gBrowser.removeTab(tab); + navigationManager.destroy(); }); function assertNetworkEvent(event, expectedContextId, expectedUrl) { - is(event.contextId, expectedContextId, "Event has the expected context id"); - is(event.requestData.url, expectedUrl, "Event has the expected url"); + is( + event.request.contextId, + expectedContextId, + "Event has the expected context id" + ); + is(event.request.serializedURL, expectedUrl, "Event has the expected url"); } diff --git a/remote/shared/test/xpcshell/test_DOM.js b/remote/shared/test/xpcshell/test_DOM.js index 19844659b9..03ac27ed45 100644 --- a/remote/shared/test/xpcshell/test_DOM.js +++ b/remote/shared/test/xpcshell/test_DOM.js @@ -75,27 +75,55 @@ function setupTest() { - +
+