/* 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/. */ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; const lazy = {}; XPCOMUtils.defineLazyPreferenceGetter( lazy, "useSeparateFileUriProcess", "browser.tabs.remote.separateFileUriProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "useSeparatePrivilegedAboutContentProcess", "browser.tabs.remote.separatePrivilegedContentProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "separatePrivilegedMozillaWebContentProcess", "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "separatedMozillaDomains", "browser.tabs.remote.separatedMozillaDomains", "", false, val => val.split(",") ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "useCrossOriginOpenerPolicy", "browser.tabs.remote.useCrossOriginOpenerPolicy", false ); XPCOMUtils.defineLazyServiceGetter( lazy, "serializationHelper", "@mozilla.org/network/serialization-helper;1", "nsISerializationHelper" ); XPCOMUtils.defineLazyServiceGetter( lazy, "extProtService", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService" ); function getOriginalReaderModeURI(aURI) { try { let searchParams = new URLSearchParams(aURI.query); if (searchParams.has("url")) { return Services.io.newURI(searchParams.get("url")); } } catch (e) {} return null; } const NOT_REMOTE = null; // These must match the similar ones in RemoteTypes.h, ProcInfo.h, ChromeUtils.webidl and ChromeUtils.cpp const WEB_REMOTE_TYPE = "web"; const FISSION_WEB_REMOTE_TYPE = "webIsolated"; const WEB_REMOTE_COOP_COEP_TYPE_PREFIX = "webCOOP+COEP="; const FILE_REMOTE_TYPE = "file"; const EXTENSION_REMOTE_TYPE = "extension"; const PRIVILEGEDABOUT_REMOTE_TYPE = "privilegedabout"; const PRIVILEGEDMOZILLA_REMOTE_TYPE = "privilegedmozilla"; const SERVICEWORKER_REMOTE_TYPE = "webServiceWorker"; // This must start with the WEB_REMOTE_TYPE above. const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE; // This list is duplicated between Navigator.cpp and here because navigator // is not accessible in this context. Please update both if the list changes. const kSafeSchemes = [ "bitcoin", "ftp", "ftps", "geo", "im", "irc", "ircs", "magnet", "mailto", "matrix", "mms", "news", "nntp", "openpgp4fpr", "sftp", "sip", "sms", "smsto", "ssh", "tel", "urn", "webcal", "wtai", "xmpp", ]; const STANDARD_SAFE_PROTOCOLS = kSafeSchemes; // Note that even if the scheme fits the criteria for a web-handled scheme // (ie it is compatible with the checks registerProtocolHandler uses), it may // not be web-handled - it could still be handled via the OS by another app. function hasPotentiallyWebHandledScheme({ scheme }) { // Note that `scheme` comes from a URI object so is already lowercase. if (kSafeSchemes.includes(scheme)) { return true; } if (!scheme.startsWith("web+") || scheme.length < 5) { return false; } // Check the rest of the scheme only consists of ascii a-z chars return /^[a-z]+$/.test(scheme.substr("web+".length)); } function validatedWebRemoteType( aPreferredRemoteType, aTargetUri, aCurrentUri, aResultPrincipal, aRemoteSubframes, aIsWorker = false, aOriginAttributes = {}, aWorkerType = Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SHARED ) { // To load into the Privileged Mozilla Content Process you must be https, // and be an exact match or a subdomain of an allowlisted domain. if ( lazy.separatePrivilegedMozillaWebContentProcess && aTargetUri.asciiHost && aTargetUri.scheme == "https" && lazy.separatedMozillaDomains.some(function (val) { return ( aTargetUri.asciiHost == val || aTargetUri.asciiHost.endsWith("." + val) ); }) ) { return PRIVILEGEDMOZILLA_REMOTE_TYPE; } // If we're in the parent and we were passed a web-handled scheme, // transform it now to avoid trying to load it in the wrong process. if (aRemoteSubframes && hasPotentiallyWebHandledScheme(aTargetUri)) { // We shouldn't even get to this for a worker, throw an unexpected error // if we do. if (aIsWorker) { throw Components.Exception( "Unexpected remote worker with a web handled scheme", Cr.NS_ERROR_UNEXPECTED ); } if ( Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT && Services.appinfo.remoteType.startsWith(FISSION_WEB_REMOTE_TYPE + "=") ) { // If we're in a child process, assume we're OK to load this non-web // URL for now. We'll either load it externally or re-evaluate once // we know the "real" URL to which we'll redirect. return Services.appinfo.remoteType; } // This doesn't work (throws) in the child - see // https://bugzilla.mozilla.org/show_bug.cgi?id=1589082 // Even if it did, it'd cause sync IPC // ( https://bugzilla.mozilla.org/show_bug.cgi?id=1589085 ), and this code // can get called several times per page load so that seems like something // we'd want to avoid. let handlerInfo = lazy.extProtService.getProtocolHandlerInfo( aTargetUri.scheme ); try { if (!handlerInfo.alwaysAskBeforeHandling) { let app = handlerInfo.preferredApplicationHandler; app.QueryInterface(Ci.nsIWebHandlerApp); // If we get here, the default handler is a web app. // Target to the origin of that web app: let uriStr = app.uriTemplate.replace(/%s/, aTargetUri.spec); aTargetUri = Services.io.newURI(uriStr); } } catch (ex) { // It's not strange for this to throw, we just ignore it and fall through. } } // If the domain is allow listed to allow it to use file:// URIs, then we have // to run it in a file content process, in case it uses file:// sub-resources. const sm = Services.scriptSecurityManager; if (!aIsWorker && sm.inFileURIAllowlist(aTargetUri)) { return FILE_REMOTE_TYPE; } // If we're within a fission window, extract site information from the URI in // question, and use it to generate an isolated origin. if (aRemoteSubframes) { let originAttributes = {}; // Only use specific properties of OriginAttributes in our remoteType let { userContextId, privateBrowsingId, geckoViewSessionContextId } = aOriginAttributes; originAttributes = { userContextId, privateBrowsingId, geckoViewSessionContextId, }; // Get a principal to use for isolation. let targetPrincipal; if (aResultPrincipal) { targetPrincipal = sm.principalWithOA(aResultPrincipal, originAttributes); } else { targetPrincipal = sm.createContentPrincipal(aTargetUri, originAttributes); } // If this is a special webCOOP+COEP= remote type that matches the // principal's siteOrigin, we don't want to override it with webIsolated= // as it's already isolated. if ( aPreferredRemoteType && aPreferredRemoteType.startsWith( `${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}${targetPrincipal.siteOrigin}` ) ) { return aPreferredRemoteType; } if ( aIsWorker && aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE ) { return `${SERVICEWORKER_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`; } return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`; // else fall through and probably return WEB_REMOTE_TYPE } if (!aPreferredRemoteType) { return WEB_REMOTE_TYPE; } if (aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)) { return aPreferredRemoteType; } return WEB_REMOTE_TYPE; } // remoteTypes allowed to host system-principal remote workers. const SYSTEM_WORKERS_REMOTE_TYPES_ALLOWED = [ NOT_REMOTE, PRIVILEGEDABOUT_REMOTE_TYPE, ]; export var E10SUtils = { DEFAULT_REMOTE_TYPE, NOT_REMOTE, WEB_REMOTE_TYPE, WEB_REMOTE_COOP_COEP_TYPE_PREFIX, FILE_REMOTE_TYPE, EXTENSION_REMOTE_TYPE, PRIVILEGEDABOUT_REMOTE_TYPE, PRIVILEGEDMOZILLA_REMOTE_TYPE, FISSION_WEB_REMOTE_TYPE, SERVICEWORKER_REMOTE_TYPE, STANDARD_SAFE_PROTOCOLS, /** * @param aURI The URI of the about page * @return The instance of the nsIAboutModule related to this uri */ getAboutModule(aURL) { // Needs to match NS_GetAboutModuleName let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase(); let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName; try { return Cc[contract].getService(Ci.nsIAboutModule); } catch (e) { // Either the about module isn't defined or it is broken. In either case // ignore it. return null; } }, useCrossOriginOpenerPolicy() { return lazy.useCrossOriginOpenerPolicy; }, _log: null, _uriStr: function uriStr(aUri) { return aUri ? aUri.spec : "undefined"; }, log: function log() { if (!this._log) { this._log = console.createInstance({ prefix: "ProcessSwitch", maxLogLevel: "Error", // Change to "Debug" the process switching code }); this._log.debug("Setup logger"); } return this._log; }, /** * Serialize csp data. * * @param {nsIContentSecurity} csp. The csp to serialize. * @return {String} The base64 encoded csp data. */ serializeCSP(csp) { let serializedCSP = null; try { if (csp) { serializedCSP = lazy.serializationHelper.serializeToString(csp); } } catch (e) { this.log().error(`Failed to serialize csp '${csp}' ${e}`); } return serializedCSP; }, /** * Deserialize a base64 encoded csp (serialized with * Utils::serializeCSP). * * @param {String} csp_b64 A base64 encoded serialized csp. * @return {nsIContentSecurityPolicy} A deserialized csp. */ deserializeCSP(csp_b64) { if (!csp_b64) { return null; } try { let csp = lazy.serializationHelper.deserializeObject(csp_b64); csp.QueryInterface(Ci.nsIContentSecurityPolicy); return csp; } catch (e) { this.log().error(`Failed to deserialize csp_b64 '${csp_b64}' ${e}`); } return null; }, canLoadURIInRemoteType( aURL, aRemoteSubframes, aRemoteType = DEFAULT_REMOTE_TYPE, aOriginAttributes = {} ) { // aRemoteType cannot be undefined, as that would cause it to default to // `DEFAULT_REMOTE_TYPE`. This means any falsy remote types are // intentionally `NOT_REMOTE`. return ( aRemoteType == this.getRemoteTypeForURI( aURL, true, aRemoteSubframes, aRemoteType, null, aOriginAttributes ) ); }, getRemoteTypeForURI( aURL, aMultiProcess, aRemoteSubframes, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, aCurrentUri, aOriginAttributes = {} ) { if (!aMultiProcess) { return NOT_REMOTE; } // loadURI in browser.js treats null as about:blank if (!aURL) { aURL = "about:blank"; } let uri; try { uri = Services.uriFixup.getFixupURIInfo(aURL).preferredURI; } catch (e) { // If we have an invalid URI, it's still possible that it might get // fixed-up into a valid URI later on. However, we don't want to return // aPreferredRemoteType here, in case the URI gets fixed-up into // something that wouldn't normally run in that process. return DEFAULT_REMOTE_TYPE; } return this.getRemoteTypeForURIObject(uri, { multiProcess: aMultiProcess, remoteSubFrames: aRemoteSubframes, preferredRemoteType: aPreferredRemoteType, currentURI: aCurrentUri, originAttributes: aOriginAttributes, }); }, getRemoteTypeForURIObject(aURI, options) { let { multiProcess = Services.appinfo.browserTabsRemoteAutostart, remoteSubFrames = Services.appinfo.fissionAutostart, preferredRemoteType = DEFAULT_REMOTE_TYPE, currentURI = null, resultPrincipal = null, isWorker = false, originAttributes = {}, workerType = Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SHARED, } = options; if (!multiProcess) { return NOT_REMOTE; } switch (aURI.scheme) { case "javascript": // javascript URIs can load in any, they apply to the current document. return preferredRemoteType; case "data": case "blob": // We need data: and blob: URIs to load in any remote process, because // they need to be able to load in whatever is the current process // unless it is non-remote. In that case we don't want to load them in // the parent process, so we load them in the default remote process, // which is sandboxed and limits any risk. return preferredRemoteType == NOT_REMOTE ? DEFAULT_REMOTE_TYPE : preferredRemoteType; case "file": return lazy.useSeparateFileUriProcess ? FILE_REMOTE_TYPE : DEFAULT_REMOTE_TYPE; case "about": let module = this.getAboutModule(aURI); // If the module doesn't exist then an error page will be loading, that // should be ok to load in any process if (!module) { return preferredRemoteType; } let flags = module.getURIFlags(aURI); if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS) { return WebExtensionPolicy.useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE; } if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) { if ( flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS && (lazy.useSeparatePrivilegedAboutContentProcess || aURI.filePath == "logins" || // Force about:welcome and about:home into the privileged content process to // workaround code coverage test failures which result from the // workaround in bug 161269. Once that bug is fixed for real, // the about:welcome and about:home case below can be removed. aURI.filePath == "welcome" || aURI.filePath == "home") ) { return PRIVILEGEDABOUT_REMOTE_TYPE; } // When loading about:reader, try to display the document in the same // web remote type as the document it's loading. if (aURI.filePath == "reader") { let readerModeURI = getOriginalReaderModeURI(aURI); if (readerModeURI) { let innerRemoteType = this.getRemoteTypeForURIObject( readerModeURI, options ); if ( innerRemoteType && innerRemoteType.startsWith(WEB_REMOTE_TYPE) ) { return innerRemoteType; } } } return DEFAULT_REMOTE_TYPE; } // If the about page can load in parent or child, it should be safe to // load in any remote type. if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) { return preferredRemoteType; } return NOT_REMOTE; case "chrome": let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService( Ci.nsIXULChromeRegistry ); if (chromeReg.mustLoadURLRemotely(aURI)) { return DEFAULT_REMOTE_TYPE; } if ( chromeReg.canLoadURLRemotely(aURI) && preferredRemoteType != NOT_REMOTE ) { return DEFAULT_REMOTE_TYPE; } return NOT_REMOTE; case "moz-extension": // Extension iframes should load in the same process // as their outer frame, but that's handled elsewhere. return WebExtensionPolicy.useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE; case "imap": case "mailbox": case "news": case "nntp": case "snews": // Protocols used by Thunderbird to display email messages. return NOT_REMOTE; default: // WebExtensions may set up protocol handlers for protocol names // beginning with ext+. These may redirect to http(s) pages or to // moz-extension pages. We can't actually tell here where one of // these pages will end up loading but Talos tests use protocol // handlers that redirect to extension pages that rely on this // behavior so a pageloader frame script is injected correctly. // Protocols that redirect to http(s) will just flip back to a // regular content process after the redirect. if (aURI.scheme.startsWith("ext+")) { // We shouldn't even get to this for a worker, throw an unexpected error // if we do. if (isWorker) { throw Components.Exception( "Unexpected remote worker with extension handled scheme", Cr.NS_ERROR_UNEXPECTED ); } return WebExtensionPolicy.useRemoteWebExtensions ? EXTENSION_REMOTE_TYPE : NOT_REMOTE; } // For any other nested URIs, we use the innerURI to determine the // remote type. In theory we should use the innermost URI, but some URIs // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and // if such URIs are wrapped in other nested schemes like view-source:, // we don't want to "skip" past "about:" by going straight to the // innermost URI. Any URIs like this will need to be handled in the // cases above, so we don't still end up using the fake inner URI here. if (aURI instanceof Ci.nsINestedURI) { // We shouldn't even get to this for a worker, throw an unexpected error // if we do. if (isWorker) { throw Components.Exception( "Unexpected worker with a NestedURI", Cr.NS_ERROR_UNEXPECTED ); } let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI; return this.getRemoteTypeForURIObject(innerURI, options); } var log = this.log(); log.debug("validatedWebRemoteType()"); log.debug(` aPreferredRemoteType: ${preferredRemoteType}`); log.debug(` aTargetUri: ${this._uriStr(aURI)}`); log.debug(` aCurrentUri: ${this._uriStr(currentURI)}`); var remoteType = validatedWebRemoteType( preferredRemoteType, aURI, currentURI, resultPrincipal, remoteSubFrames, isWorker, originAttributes, workerType ); log.debug(` validatedWebRemoteType() returning: ${remoteType}`); return remoteType; } }, getRemoteTypeForWorkerPrincipal( aPrincipal, aWorkerType, aIsMultiProcess, aIsFission, aPreferredRemoteType = DEFAULT_REMOTE_TYPE ) { if (aPrincipal.isExpandedPrincipal) { // Explicitly disallow expanded principals: // The worker principal is based on the worker script, an expanded principal // is not expected. throw new Error("Unexpected expanded principal worker"); } if ( aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE && !aPrincipal.isContentPrincipal ) { // Fails earlier on service worker with a non content principal. throw new Error("Unexpected system or null principal service worker"); } if (!aIsMultiProcess) { // Return earlier when multiprocess is disabled. return NOT_REMOTE; } // We don't want to launch the shared worker in a web coop+coep remote type // even if was registered from a frame loaded in a child process with that // remote type. if (aPreferredRemoteType?.startsWith(WEB_REMOTE_COOP_COEP_TYPE_PREFIX)) { aPreferredRemoteType = DEFAULT_REMOTE_TYPE; } // System principal shared workers are allowed to run in the main process // or in the privilegedabout child process. Early return the preferred remote type // if it is one where a system principal worked is allowed to run. if ( aPrincipal.isSystemPrincipal && SYSTEM_WORKERS_REMOTE_TYPES_ALLOWED.includes(aPreferredRemoteType) ) { return aPreferredRemoteType; } // Allow null principal shared workers to run in the same process type where they // have been registered (the preferredRemoteType), but return the DEFAULT_REMOTE_TYPE // if the preferred remote type was NOT_REMOTE. if (aPrincipal.isNullPrincipal) { return aPreferredRemoteType === NOT_REMOTE ? DEFAULT_REMOTE_TYPE : aPreferredRemoteType; } // Sanity check, there shouldn't be any system or null principal after this point. if (aPrincipal.isContentPrincipal) { // For content principal, get a remote type based on the worker principal URI // (which is based on the worker script url) and an initial preferredRemoteType // (only set for shared worker, based on the remote type where the shared worker // was registered from). return E10SUtils.getRemoteTypeForURIObject(aPrincipal.URI, { multiProcess: aIsMultiProcess, remoteSubFrames: aIsFission, preferredRemoteType: aPreferredRemoteType, resultPrincipal: aPrincipal, originAttributes: aPrincipal.originAttributes, isWorker: true, workerType: aWorkerType, }); } // Throw explicitly if we were unable to get a remoteType for the worker. throw new Error( "Failed to get a remoteType for a non content principal worker" ); }, makeInputStream(data) { if (typeof data == "string") { let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( Ci.nsISupportsCString ); stream.data = data; return stream; // XPConnect will QI this to nsIInputStream for us. } let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( Ci.nsISupportsCString ); stream.data = data.content; if (data.headers) { let mimeStream = Cc[ "@mozilla.org/network/mime-input-stream;1" ].createInstance(Ci.nsIMIMEInputStream); mimeStream.setData(stream); for (let [name, value] of data.headers) { mimeStream.addHeader(name, value); } return mimeStream; } return stream; // XPConnect will QI this to nsIInputStream for us. }, /** * Serialize principal data. * * @param {nsIPrincipal} principal The principal to serialize. * @return {String} The serialized principal data. */ serializePrincipal(principal) { let serializedPrincipal = null; try { if (principal) { serializedPrincipal = Services.scriptSecurityManager.principalToJSON(principal); } } catch (e) { this.log().error(`Failed to serialize principal '${principal}' ${e}`); } return serializedPrincipal; }, /** * Deserialize a principal (serialized with serializePrincipal). * * @param {String} serializedPincipal A serialized principal. * @return {nsIPrincipal} A deserialized principal. */ deserializePrincipal(serializedPincipal, fallbackPrincipalCallback = null) { if (!serializedPincipal) { if (!fallbackPrincipalCallback) { this.log().warn( "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" ); return null; } return fallbackPrincipalCallback(); } try { let principal; // The current JSON representation of principal is not stored as base64. We start by checking // if the serialized data starts with '{' to determine if we're using the new JSON representation. // If it doesn't we try the two legacy formats, old JSON and nsISerializable. if (serializedPincipal.startsWith("{")) { principal = Services.scriptSecurityManager.JSONToPrincipal(serializedPincipal); } else { // Both the legacy and legacy JSON representation of principals are stored as base64 // The legacy JSON kind are the only ones that will start with "{" when decoded. // We check here for the legacy JSON serialized, if it doesn't start with that continue using nsISerializable. // JSONToPrincipal accepts a *non* base64 encoded string and returns a principal or a null. let tmpa = atob(serializedPincipal); if (tmpa.startsWith("{")) { principal = Services.scriptSecurityManager.JSONToPrincipal(tmpa); } else { principal = lazy.serializationHelper.deserializeObject(serializedPincipal); } } principal.QueryInterface(Ci.nsIPrincipal); return principal; } catch (e) { this.log().error( `Failed to deserialize serializedPincipal '${serializedPincipal}' ${e}` ); } if (!fallbackPrincipalCallback) { this.log().warn( "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" ); return null; } return fallbackPrincipalCallback(); }, /** * Serialize cookieJarSettings. * * @param {nsICookieJarSettings} cookieJarSettings The cookieJarSettings to * serialize. * @return {String} The base64 encoded cookieJarSettings data. */ serializeCookieJarSettings(cookieJarSettings) { let serialized = null; if (cookieJarSettings) { try { serialized = lazy.serializationHelper.serializeToString(cookieJarSettings); } catch (e) { this.log().error( `Failed to serialize cookieJarSettings '${cookieJarSettings}' ${e}` ); } } return serialized; }, /** * Deserialize a base64 encoded cookieJarSettings * * @param {String} cookieJarSettings_b64 A base64 encoded serialized cookieJarSettings. * @return {nsICookieJarSettings} A deserialized cookieJarSettings. */ deserializeCookieJarSettings(cookieJarSettings_b64) { let deserialized = null; if (cookieJarSettings_b64) { try { deserialized = lazy.serializationHelper.deserializeObject( cookieJarSettings_b64 ); deserialized.QueryInterface(Ci.nsICookieJarSettings); } catch (e) { this.log().error( `Failed to deserialize cookieJarSettings_b64 '${cookieJarSettings_b64}' ${e}` ); } } return deserialized; }, wrapHandlingUserInput(aWindow, aIsHandling, aCallback) { var handlingUserInput; try { handlingUserInput = aWindow.windowUtils.setHandlingUserInput(aIsHandling); aCallback(); } finally { handlingUserInput.destruct(); } }, /** * Serialize referrerInfo. * * @param {nsIReferrerInfo} The referrerInfo to serialize. * @return {String} The base64 encoded referrerInfo. */ serializeReferrerInfo(referrerInfo) { let serialized = null; if (referrerInfo) { try { serialized = lazy.serializationHelper.serializeToString(referrerInfo); } catch (e) { this.log().error( `Failed to serialize referrerInfo '${referrerInfo}' ${e}` ); } } return serialized; }, /** * Deserialize a base64 encoded referrerInfo * * @param {String} referrerInfo_b64 A base64 encoded serialized referrerInfo. * @return {nsIReferrerInfo} A deserialized referrerInfo. */ deserializeReferrerInfo(referrerInfo_b64) { let deserialized = null; if (referrerInfo_b64) { try { deserialized = lazy.serializationHelper.deserializeObject(referrerInfo_b64); deserialized.QueryInterface(Ci.nsIReferrerInfo); } catch (e) { this.log().error( `Failed to deserialize referrerInfo_b64 '${referrerInfo_b64}' ${e}` ); } } return deserialized; }, /** * Returns the pids for a remote browser and its remote subframes. */ getBrowserPids(aBrowser, aRemoteSubframes) { if (!aBrowser.isRemoteBrowser || !aBrowser.frameLoader) { return []; } let tabPid = aBrowser.frameLoader.remoteTab.osPid; let pids = new Set(); if (aRemoteSubframes) { let stack = [aBrowser.browsingContext]; while (stack.length) { let bc = stack.pop(); stack.push(...bc.children); if (bc.currentWindowGlobal) { let pid = bc.currentWindowGlobal.osPid; if (pid != tabPid) { pids.add(pid); } } } } return [tabPid, ...pids]; }, /** * The suffix after a `=` in a remoteType is dynamic, and used to control the * process pool to use. The C++ version of this method is mozilla::dom::RemoteTypePrefix(). */ remoteTypePrefix(aRemoteType) { return aRemoteType.split("=")[0]; }, /** * There are various types of remote types that are for web content processes, but * they all start with "web". The C++ version of this method is * mozilla::dom::IsWebRemoteType(). */ isWebRemoteType(aRemoteType) { return aRemoteType.startsWith(WEB_REMOTE_TYPE); }, /** * Assemble or predict originAttributes from available arguments. */ predictOriginAttributes({ window, browser, userContextId, geckoViewSessionContextId, privateBrowsingId, }) { if (browser) { if (browser.browsingContext) { return browser.browsingContext.originAttributes; } if (!window) { window = browser.contentDocument?.defaultView; } if (!userContextId) { userContextId = browser.getAttribute("usercontextid") || 0; } if (!geckoViewSessionContextId) { geckoViewSessionContextId = browser.getAttribute("geckoViewSessionContextId") || ""; } } if (window && !privateBrowsingId) { privateBrowsingId = window.browsingContext.usePrivateBrowsing ? 1 : 0; } return { privateBrowsingId, userContextId, geckoViewSessionContextId }; }, }; XPCOMUtils.defineLazyGetter( E10SUtils, "SERIALIZED_SYSTEMPRINCIPAL", function () { return btoa( E10SUtils.serializePrincipal( Services.scriptSecurityManager.getSystemPrincipal() ) ); } );