UserPromptHandler.sys.mjs (8497B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs", 9 pprint: "chrome://remote/content/shared/Format.sys.mjs", 10 }); 11 12 /** 13 * @typedef {string} PromptHandlers 14 */ 15 16 /** 17 * Enum of known prompt handlers. 18 * 19 * @readonly 20 * @enum {PromptHandlers} 21 * 22 * @see https://w3c.github.io/webdriver/#dfn-known-prompt-handlers 23 */ 24 export const PromptHandlers = { 25 /** All simple dialogs encountered should be accepted. */ 26 Accept: "accept", 27 /** 28 * All simple dialogs encountered should be accepted, and an error 29 * returned that the dialog was handled. 30 */ 31 AcceptAndNotify: "accept and notify", 32 /** All simple dialogs encountered should be dismissed. */ 33 Dismiss: "dismiss", 34 /** 35 * All simple dialogs encountered should be dismissed, and an error 36 * returned that the dialog was handled. 37 */ 38 DismissAndNotify: "dismiss and notify", 39 /** All simple dialogs encountered should be left to the user to handle. */ 40 Ignore: "ignore", 41 }; 42 43 /** 44 * @typedef {string} PromptTypes 45 */ 46 47 /** 48 * Enum of valid prompt types. 49 * 50 * @readonly 51 * @enum {PromptTypes} 52 * 53 * @see https://w3c.github.io/webdriver/#dfn-valid-prompt-types 54 */ 55 export const PromptTypes = { 56 // A simple alert dialog 57 Alert: "alert", 58 // A simple beforeunload dialog. If no handler is set it falls back to the 59 // `accept` handler to keep backward compatibility with WebDriver classic, 60 // which doesn't customize this prompt type. 61 BeforeUnload: "beforeUnload", 62 // A simple confirm dialog 63 Confirm: "confirm", 64 // Default value when no specific handler is configured. Can only be set when 65 // specifying the unhandlePromptBehavior capability with a map containing a 66 // "default" entry. See FALLBACK_DEFAULT_PROMPT_TYPE. 67 Default: "default", 68 // A file picker dialog 69 File: "file", 70 // A simple prompt dialog 71 Prompt: "prompt", 72 }; 73 74 /** 75 * Internal prompt type used when the unhandledPromptBehavior capability is 76 * set as a string. The "fallbackDefault" type will apply to "alert", "confirm" 77 * and "prompt" prompts, but will not apply to "beforeUnload" prompts. 78 */ 79 const FALLBACK_DEFAULT_PROMPT_TYPE = "fallbackDefault"; 80 81 export class PromptHandlerConfiguration { 82 #handler; 83 #notify; 84 85 /** 86 * Create an instance of a prompt handler configuration. 87 * 88 * @param {string} handler 89 * Handler to use for the user prompt. One of "accept", "dismiss" or "ignore". 90 * @param {boolean} notify 91 * Flag to indicate if the user needs to be notified when the dialog was 92 * handled. 93 * 94 * @see https://w3c.github.io/webdriver/#dfn-prompt-handler-configuration 95 */ 96 constructor(handler, notify) { 97 this.#handler = handler; 98 this.#notify = notify; 99 } 100 101 get handler() { 102 return this.#handler; 103 } 104 105 get notify() { 106 return this.#notify; 107 } 108 109 toString() { 110 return "[object PromptHandlerConfiguration]"; 111 } 112 113 /** 114 * JSON serialization of the prompt handler configuration object. 115 * 116 * @returns {Record<string, ?>} json 117 * 118 * @see https://w3c.github.io/webdriver/#dfn-serialize-a-prompt-handler-configuration 119 */ 120 toJSON() { 121 let serialized = this.#handler; 122 if (["accept", "dismiss"].includes(serialized) && this.#notify) { 123 serialized += " and notify"; 124 } 125 126 return serialized; 127 } 128 } 129 130 export class UserPromptHandler { 131 #promptTypeToHandlerMap; 132 133 constructor() { 134 // Note: this map is null until update-the-user-prompt-handler initializes 135 // it. 136 this.#promptTypeToHandlerMap = null; 137 } 138 139 get activePromptHandlers() { 140 return this.#promptTypeToHandlerMap; 141 } 142 143 get PromptHandlers() { 144 return PromptHandlers; 145 } 146 147 get PromptTypes() { 148 return PromptTypes; 149 } 150 151 /** 152 * Unmarshal a JSON object representation of the unhandledPromptBehavior capability. 153 * 154 * @param {Record<string, ?>} json 155 * JSON Object to unmarshal. 156 * 157 * @throws {InvalidArgumentError} 158 * When the value of the unhandledPromptBehavior capability is invalid. 159 * 160 * @returns {UserPromptHandler} 161 * 162 * @see https://w3c.github.io/webdriver/#dfn-deserialize-as-an-unhandled-prompt-behavior 163 */ 164 static fromJSON(json) { 165 let isStringValue = false; 166 let value = json; 167 if (typeof value === "string") { 168 // A single specified prompt behavior or for WebDriver classic. 169 value = { [FALLBACK_DEFAULT_PROMPT_TYPE]: value }; 170 isStringValue = true; 171 } 172 173 lazy.assert.object( 174 value, 175 lazy.pprint`Expected "unhandledPromptBehavior" to be an object, got ${value}` 176 ); 177 178 const userPromptHandlerCapability = new Map(); 179 for (let [promptType, handler] of Object.entries(value)) { 180 if (!isStringValue) { 181 const validPromptTypes = Object.values(PromptTypes); 182 lazy.assert.in( 183 promptType, 184 validPromptTypes, 185 lazy.pprint`Expected "promptType" to be one of ${validPromptTypes}, got ${promptType}` 186 ); 187 } 188 189 const knownPromptHandlers = Object.values(PromptHandlers); 190 lazy.assert.in( 191 handler, 192 knownPromptHandlers, 193 lazy.pprint`Expected "handler" to be one of ${knownPromptHandlers}, got ${handler}` 194 ); 195 196 let notify = false; 197 switch (handler) { 198 case PromptHandlers.AcceptAndNotify: 199 handler = PromptHandlers.Accept; 200 notify = true; 201 break; 202 case PromptHandlers.DismissAndNotify: 203 handler = PromptHandlers.Dismiss; 204 notify = true; 205 break; 206 case PromptHandlers.Ignore: 207 notify = true; 208 break; 209 } 210 211 const configuration = new PromptHandlerConfiguration(handler, notify); 212 userPromptHandlerCapability.set(promptType, configuration); 213 } 214 const userPromptHandler = new UserPromptHandler(); 215 userPromptHandler.update(userPromptHandlerCapability); 216 return userPromptHandler; 217 } 218 219 /** 220 * Get the handler for the given prompt type. 221 * 222 * @param {string} promptType 223 * The prompt type to retrieve the handler for. 224 * 225 * @returns {PromptHandlerConfiguration} 226 * 227 * @see https://w3c.github.io/webdriver/#dfn-get-the-prompt-handler 228 */ 229 getPromptHandler(promptType) { 230 let handlers; 231 232 if (this.#promptTypeToHandlerMap === null) { 233 handlers = new Map(); 234 } else { 235 handlers = this.#promptTypeToHandlerMap; 236 } 237 238 if (handlers.has(promptType)) { 239 return handlers.get(promptType); 240 } 241 242 if (handlers.has(PromptTypes.Default)) { 243 return handlers.get(PromptTypes.Default); 244 } 245 246 if (promptType === PromptTypes.BeforeUnload) { 247 return new PromptHandlerConfiguration(PromptHandlers.Accept, false); 248 } 249 250 if (handlers.has(FALLBACK_DEFAULT_PROMPT_TYPE)) { 251 return handlers.get(FALLBACK_DEFAULT_PROMPT_TYPE); 252 } 253 254 return new PromptHandlerConfiguration(PromptHandlers.Dismiss, true); 255 } 256 257 /** 258 * Updates the prompt handler configuration for a given requested prompt 259 * handler map. 260 * 261 * @param {Map} requestedPromptHandler 262 * The request prompt handler configuration map. 263 * 264 * @see https://w3c.github.io/webdriver/#dfn-update-the-user-prompt-handler 265 */ 266 update(requestedPromptHandler) { 267 if (this.#promptTypeToHandlerMap === null) { 268 this.#promptTypeToHandlerMap = new Map(); 269 } 270 271 for (const [promptType, handler] of requestedPromptHandler) { 272 this.#promptTypeToHandlerMap.set(promptType, handler); 273 } 274 } 275 276 /** 277 * JSON serialization of the user prompt handler object. 278 * 279 * @returns {Record<string, ?>} json 280 * 281 * @see https://w3c.github.io/webdriver/#dfn-serialize-the-user-prompt-handler 282 */ 283 toJSON() { 284 if (this.#promptTypeToHandlerMap === null) { 285 // Fallback to "dismiss and notify" if no handler is set 286 return PromptHandlers.DismissAndNotify; 287 } 288 289 if ( 290 this.#promptTypeToHandlerMap.size === 1 && 291 this.#promptTypeToHandlerMap.has(FALLBACK_DEFAULT_PROMPT_TYPE) 292 ) { 293 return this.#promptTypeToHandlerMap 294 .get(FALLBACK_DEFAULT_PROMPT_TYPE) 295 .toJSON(); 296 } 297 298 const serialized = {}; 299 for (const [key, value] of this.#promptTypeToHandlerMap.entries()) { 300 serialized[key] = value.toJSON(); 301 } 302 303 return serialized; 304 } 305 306 toString() { 307 return "[object UserPromptHandler]"; 308 } 309 }