browser.sys.mjs (9434B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { RootBiDiModule } from "chrome://remote/content/webdriver-bidi/modules/RootBiDiModule.sys.mjs"; 6 7 const lazy = {}; 8 9 ChromeUtils.defineESModuleGetters(lazy, { 10 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs", 11 Certificates: "chrome://remote/content/shared/webdriver/Certificates.sys.mjs", 12 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs", 13 getWebDriverSessionById: 14 "chrome://remote/content/shared/webdriver/Session.sys.mjs", 15 pprint: "chrome://remote/content/shared/Format.sys.mjs", 16 ProxyConfiguration: 17 "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs", 18 ProxyPerUserContextManager: 19 "chrome://remote/content/webdriver-bidi/ProxyPerUserContextManager.sys.mjs", 20 ProxyTypes: "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs", 21 TabManager: "chrome://remote/content/shared/TabManager.sys.mjs", 22 UserContextManager: 23 "chrome://remote/content/shared/UserContextManager.sys.mjs", 24 windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs", 25 WindowState: "chrome://remote/content/shared/WindowManager.sys.mjs", 26 }); 27 28 /** 29 * An object that holds information about the client window 30 * 31 * @typedef ClientWindowInfo 32 * 33 * @property {boolean} active 34 * True if client window is keyboard-interactable. False, if 35 * otherwise. 36 * @property {string} clientWindow 37 * The id of the client window. 38 * @property {number} height 39 * The height of the client window. 40 * @property {WindowState} state 41 * The client window state. 42 * @property {number} width 43 * The width of the client window. 44 * @property {number} x 45 * The x-coordinate of the client window. 46 * @property {number} y 47 * The y-coordinate of the client window. 48 */ 49 50 /** 51 * Return value of the getClientWindows command. 52 * 53 * @typedef GetClientWindowsResult 54 * 55 * @property {Array<ClientWindowInfo>} clientWindows 56 */ 57 58 /** 59 * An object that holds information about a user context. 60 * 61 * @typedef UserContextInfo 62 * 63 * @property {string} userContext 64 * The id of the user context. 65 */ 66 67 /** 68 * Return value for the getUserContexts command. 69 * 70 * @typedef GetUserContextsResult 71 * 72 * @property {Array<UserContextInfo>} userContexts 73 * Array of UserContextInfo for the current user contexts. 74 */ 75 76 class BrowserModule extends RootBiDiModule { 77 #proxyManager; 78 #userContextsWithInsecureCertificatesOverrides; 79 80 constructor(messageHandler) { 81 super(messageHandler); 82 83 this.#proxyManager = new lazy.ProxyPerUserContextManager(); 84 85 // A set of internal user context ids to keep track of user contexts 86 // which had insecure certificates overrides set for them. 87 this.#userContextsWithInsecureCertificatesOverrides = new Set(); 88 } 89 90 destroy() { 91 // Reset "allowInsecureCerts" for the userContexts, 92 // which were created in the scope of this session. 93 for (const userContext of this 94 .#userContextsWithInsecureCertificatesOverrides) { 95 lazy.Certificates.resetSecurityChecksForUserContext(userContext); 96 } 97 98 this.#userContextsWithInsecureCertificatesOverrides = null; 99 100 this.#proxyManager.destroy(); 101 } 102 103 /** 104 * Commands 105 */ 106 107 /** 108 * Terminate all WebDriver sessions and clean up automation state in the 109 * remote browser instance. 110 * 111 * The actual session clean-up and closing the browser will happen later 112 * in WebDriverBiDiConnection class. 113 */ 114 async close() { 115 const session = lazy.getWebDriverSessionById(this.messageHandler.sessionId); 116 117 // TODO Bug 1838269. Enable browser.close command for the case of classic + bidi session, when 118 // session ending for this type of session is supported. 119 if (session.http) { 120 throw new lazy.error.UnsupportedOperationError( 121 "Closing the browser in a session started with WebDriver classic" + 122 ' is not supported. Use the WebDriver classic "Delete Session"' + 123 " command instead which will also close the browser." 124 ); 125 } 126 127 // Close all open top-level browsing contexts by not prompting for beforeunload. 128 for (const tab of lazy.TabManager.allTabs) { 129 lazy.TabManager.removeTab(tab, { skipPermitUnload: true }); 130 } 131 } 132 133 /** 134 * Returns a list of client windows info 135 * 136 * @returns {GetClientWindowsResult} 137 * The list of client windows info 138 */ 139 async getClientWindows() { 140 const clientWindowsIds = new Set(); 141 const clientWindows = []; 142 143 for (const win of lazy.windowManager.windows) { 144 let clientWindowId = lazy.windowManager.getIdForWindow(win); 145 if (clientWindowsIds.has(clientWindowId)) { 146 continue; 147 } 148 clientWindowsIds.add(clientWindowId); 149 let clientWindowInfo = this.#getClientWindowInfo(win); 150 clientWindows.push(clientWindowInfo); 151 } 152 153 return { clientWindows }; 154 } 155 156 /** 157 * Creates a user context. 158 * 159 * @param {object=} options 160 * @param {boolean=} options.acceptInsecureCerts 161 * Indicates whether untrusted and self-signed TLS certificates 162 * should be implicitly trusted on navigation for this user context. 163 * @param {object=} options.proxy 164 * An object which holds the proxy settings. 165 * 166 * @returns {UserContextInfo} 167 * UserContextInfo object for the created user context. 168 * 169 * @throws {InvalidArgumentError} 170 * Raised if an argument is of an invalid type or value. 171 * @throws {UnsupportedOperationError} 172 * Raised when the command is called with unsupported proxy types. 173 */ 174 async createUserContext(options = {}) { 175 const { acceptInsecureCerts = null, proxy = null } = options; 176 177 if (acceptInsecureCerts !== null) { 178 lazy.assert.boolean( 179 acceptInsecureCerts, 180 lazy.pprint`Expected "acceptInsecureCerts" to be a boolean, got ${acceptInsecureCerts}` 181 ); 182 } 183 184 let proxyObject; 185 if (proxy !== null) { 186 proxyObject = lazy.ProxyConfiguration.fromJSON(proxy); 187 188 if ( 189 proxyObject.proxyType === lazy.ProxyTypes.System || 190 proxyObject.proxyType === lazy.ProxyTypes.Autodetect || 191 proxyObject.proxyType === lazy.ProxyTypes.Pac 192 ) { 193 // Bug 1968887: Add support for "system", "autodetect" and "pac" proxy types. 194 throw new lazy.error.UnsupportedOperationError( 195 `Proxy type "${proxyObject.proxyType}" is not supported` 196 ); 197 } 198 } 199 200 const userContextId = lazy.UserContextManager.createContext("webdriver"); 201 const internalId = lazy.UserContextManager.getInternalIdById(userContextId); 202 203 if (acceptInsecureCerts !== null) { 204 this.#userContextsWithInsecureCertificatesOverrides.add(internalId); 205 if (acceptInsecureCerts) { 206 lazy.Certificates.disableSecurityChecks(internalId); 207 } else { 208 lazy.Certificates.enableSecurityChecks(internalId); 209 } 210 } 211 212 if (proxy !== null) { 213 this.#proxyManager.addConfiguration(internalId, proxyObject); 214 } 215 216 return { userContext: userContextId }; 217 } 218 219 /** 220 * Returns the list of available user contexts. 221 * 222 * @returns {GetUserContextsResult} 223 * Object containing an array of UserContextInfo. 224 */ 225 async getUserContexts() { 226 const userContexts = lazy.UserContextManager.getUserContextIds().map( 227 userContextId => ({ 228 userContext: userContextId, 229 }) 230 ); 231 232 return { userContexts }; 233 } 234 235 /** 236 * Closes a user context and all browsing contexts in it without running 237 * beforeunload handlers. 238 * 239 * @param {object=} options 240 * @param {string} options.userContext 241 * Id of the user context to close. 242 * 243 * @throws {InvalidArgumentError} 244 * Raised if an argument is of an invalid type or value. 245 * @throws {NoSuchUserContextError} 246 * Raised if the user context id could not be found. 247 */ 248 async removeUserContext(options = {}) { 249 const { userContext: userContextId } = options; 250 251 lazy.assert.string( 252 userContextId, 253 lazy.pprint`Expected "userContext" to be a string, got ${userContextId}` 254 ); 255 256 if (userContextId === lazy.UserContextManager.defaultUserContextId) { 257 throw new lazy.error.InvalidArgumentError( 258 `Default user context cannot be removed` 259 ); 260 } 261 262 if (!lazy.UserContextManager.hasUserContextId(userContextId)) { 263 throw new lazy.error.NoSuchUserContextError( 264 `User Context with id ${userContextId} was not found` 265 ); 266 } 267 268 const internalId = lazy.UserContextManager.getInternalIdById(userContextId); 269 270 lazy.UserContextManager.removeUserContext(userContextId, { 271 closeContextTabs: true, 272 }); 273 274 // Reset the state to clean up the platform state. 275 lazy.Certificates.resetSecurityChecksForUserContext(internalId); 276 this.#userContextsWithInsecureCertificatesOverrides.delete(internalId); 277 278 this.#proxyManager.deleteConfiguration(internalId); 279 } 280 281 #getClientWindowInfo(window) { 282 return { 283 active: Services.focus.activeWindow === window, 284 clientWindow: lazy.windowManager.getIdForWindow(window), 285 height: window.outerHeight, 286 state: lazy.WindowState.from(window.windowState), 287 width: window.outerWidth, 288 x: window.screenX, 289 y: window.screenY, 290 }; 291 } 292 } 293 294 // To export the class as lower-case 295 export const browser = BrowserModule;