BrowsingContextUtils.sys.mjs (7283B)
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 const lazy = {}; 6 7 ChromeUtils.defineESModuleGetters(lazy, { 8 error: "chrome://remote/content/shared/messagehandler/Errors.sys.mjs", 9 PollPromise: "chrome://remote/content/shared/Sync.sys.mjs", 10 }); 11 12 /** 13 * @typedef {object} BrowsingContextDetails 14 * 15 * @property {number} browserId 16 * Browser id for this browsing context. 17 * @property {number} browsingContextId 18 * Internal id of the browsing context. 19 * @property {boolean} isTopBrowsingContext 20 * Flag that indicates if the browsing context is top-level. 21 * @property {boolean} isContent 22 * Flag that indicates if it is a content or a chrome browsing context. 23 */ 24 25 /** 26 * A browsing context might be replaced before reaching the parent process, 27 * instead we serialize enough information to retrieve the navigable in the 28 * parent process. 29 * 30 * If the browsing context is top level, then the browserId can be used to 31 * find the browser element and the new browsing context. 32 * Otherwise (frames) the browsing context should not be replaced and the 33 * browsing context id should be enough to find the browsing context. 34 * 35 * Should be used when preparing an event payload from the content to the 36 * parent process. 37 * 38 * @param {BrowsingContext} browsingContext 39 * The browsing context for which we want to get details. 40 * 41 * @returns {BrowsingContextDetails} 42 * Details of the browsing context. 43 */ 44 export function getBrowsingContextDetails(browsingContext) { 45 return { 46 browserId: browsingContext.browserId, 47 browsingContextId: browsingContext.id, 48 isContent: browsingContext.isContent, 49 isTopBrowsingContext: browsingContext.parent === null, 50 }; 51 } 52 53 function isExtensionContext(browsingContext) { 54 let principal; 55 try { 56 if (CanonicalBrowsingContext.isInstance(browsingContext)) { 57 principal = browsingContext.currentWindowGlobal.documentPrincipal; 58 } else { 59 principal = browsingContext.window.document.nodePrincipal; 60 } 61 } catch (e) { 62 throw new Error( 63 `Could not retrieve principal for browsingContext (${e.message})` 64 ); 65 } 66 67 // In practice, note that the principal will never be an expanded principal. 68 // The are only used for content scripts executed in a Sandbox, and do not 69 // have a browsing context on their own. 70 // But we still use this flag because there is no isAddonPrincipal flag. 71 return principal.isAddonOrExpandedAddonPrincipal; 72 } 73 74 function isParentProcess(browsingContext) { 75 if (CanonicalBrowsingContext.isInstance(browsingContext)) { 76 return browsingContext.currentWindowGlobal.osPid === -1; 77 } 78 79 // If `browsingContext` is not a `CanonicalBrowsingContext`, then we are 80 // necessarily in a content process page. 81 return false; 82 } 83 84 /** 85 * Check if the provided browsing context is currently displaying its initial 86 * document. For top level browsing contexts, this is usually the initial 87 * about:blank. 88 * 89 * @param {BrowsingContext} browsingContext 90 * The browsing context to check. 91 * 92 * @returns {boolean} 93 * True if the browsing context is on the initial document, false otherwise. 94 */ 95 export function isInitialDocument(browsingContext) { 96 if (!browsingContext.currentWindowGlobal) { 97 // Right after a browsing context has been attached it could happen that 98 // no window global has been set yet. Consider this as nothing has been 99 // loaded yet. 100 return true; 101 } 102 103 return browsingContext.currentWindowGlobal.isInitialDocument; 104 } 105 106 /** 107 * Check if the provided browsing context is currently displaying its initial 108 * document. For top level browsing contexts, this is usually the initial 109 * about:blank which will be replaced soon. 110 * 111 * @param {BrowsingContext} browsingContext 112 * The browsing context to check. 113 * 114 * @returns {boolean} 115 * True if the browsing context is on the initial document, false otherwise. 116 */ 117 export function isUncommittedInitialDocument(browsingContext) { 118 if (!browsingContext.currentWindowGlobal) { 119 // Right after a browsing context has been attached it could happen that 120 // no window global has been set yet. Consider this as nothing has been 121 // loaded yet. 122 return true; 123 } 124 125 return browsingContext.currentWindowGlobal.isUncommittedInitialDocument; 126 } 127 128 /** 129 * Check if the given browsing context is valid for the message handler 130 * to use. 131 * 132 * @param {BrowsingContext} browsingContext 133 * The browsing context to check. 134 * @param {object=} options 135 * @param {string=} options.browserId 136 * The id of the browser to filter the browsing contexts by (optional). 137 * @param {string=} options.userContext 138 * The id of the user context to filter the browsing contexts by (optional). 139 * 140 * @returns {boolean} 141 * True if the browsing context is valid, false otherwise. 142 */ 143 export function isBrowsingContextCompatible(browsingContext, options = {}) { 144 const { browserId, userContext } = options; 145 146 if (!BrowsingContext.isInstance(browsingContext)) { 147 return false; 148 } 149 150 // If a browserId was provided, skip browsing contexts which are not 151 // associated with this browserId. 152 if (browserId !== undefined && browsingContext.browserId !== browserId) { 153 return false; 154 } 155 156 // If a userContext was provided, skip browsing contexts which are not 157 // associated with this userContext. 158 if ( 159 userContext !== undefined && 160 browsingContext.originAttributes.userContextId !== userContext 161 ) { 162 return false; 163 } 164 165 // If this is a CanonicalBrowsingContext but the currentWindowGlobal is not 166 // attached yet, skip it. 167 if ( 168 CanonicalBrowsingContext.isInstance(browsingContext) && 169 !browsingContext.currentWindowGlobal 170 ) { 171 return false; 172 } 173 174 // Skip: 175 // - extension contexts until we support debugging webextensions, see Bug 1755014. 176 // - privileged contexts until we support debugging Chrome context, see Bug 1713440. 177 return ( 178 !isExtensionContext(browsingContext) && !isParentProcess(browsingContext) 179 ); 180 } 181 182 /** 183 * Wait until `currentWindowGlobal` is available on a browsing context. When a 184 * browsing context has just been created, the `currentWindowGlobal` might not 185 * be attached yet. 186 * 187 * @param {CanonicalBrowsingContext} browsingContext 188 * The browsing context to wait for. 189 * 190 * @returns {Promise} 191 * Promise which resolves when `currentWindowGlobal` is available. 192 * 193 * @throws DiscardedBrowsingContextError 194 * Browsing context is discarded or still no 195 * `currentWindowGlobal` set after 100ms. 196 */ 197 export async function waitForCurrentWindowGlobal(browsingContext) { 198 await lazy.PollPromise( 199 (resolve, reject) => { 200 if (browsingContext.currentWindowGlobal || browsingContext.isDiscarded) { 201 // If the browsing context is discarded while checking for 202 // the current window global, return early to avoid waiting 203 // unnecessarily until the timeout expires. 204 resolve(); 205 } else { 206 reject(); 207 } 208 }, 209 { 210 timeout: 100, 211 } 212 ); 213 214 if (!browsingContext.currentWindowGlobal) { 215 throw new lazy.error.DiscardedBrowsingContextError( 216 `BrowsingContext does no longer exist` 217 ); 218 } 219 }