diff options
Diffstat (limited to 'toolkit/modules/E10SUtils.sys.mjs')
-rw-r--r-- | toolkit/modules/E10SUtils.sys.mjs | 994 |
1 files changed, 994 insertions, 0 deletions
diff --git a/toolkit/modules/E10SUtils.sys.mjs b/toolkit/modules/E10SUtils.sys.mjs new file mode 100644 index 0000000000..41f4f1f01b --- /dev/null +++ b/toolkit/modules/E10SUtils.sys.mjs @@ -0,0 +1,994 @@ +/* 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, + aMultiProcess, + aRemoteSubframes, + aPreferredRemoteType, + aCurrentUri, + null, //aResultPrincipal + false, //aIsSubframe + false, // aIsWorker + aOriginAttributes + ); + }, + + getRemoteTypeForURIObject( + aURI, + aMultiProcess, + aRemoteSubframes, + aPreferredRemoteType = DEFAULT_REMOTE_TYPE, + aCurrentUri = null, + aResultPrincipal = null, + aIsSubframe = false, + aIsWorker = false, + aOriginAttributes = {}, + aWorkerType = Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SHARED + ) { + if (!aMultiProcess) { + return NOT_REMOTE; + } + + switch (aURI.scheme) { + case "javascript": + // javascript URIs can load in any, they apply to the current document. + return aPreferredRemoteType; + + 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 aPreferredRemoteType == NOT_REMOTE + ? DEFAULT_REMOTE_TYPE + : aPreferredRemoteType; + + 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 aPreferredRemoteType; + } + + 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, + aMultiProcess, + aRemoteSubframes, + aPreferredRemoteType, + aCurrentUri, + null, // aResultPrincipal + aIsSubframe, + aIsWorker, + aOriginAttributes + ); + 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 aPreferredRemoteType; + } + + 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) && + aPreferredRemoteType != NOT_REMOTE + ) { + return DEFAULT_REMOTE_TYPE; + } + + return NOT_REMOTE; + + case "moz-extension": + if (WebExtensionPolicy.useRemoteWebExtensions) { + // Extension iframes should load in the same process + // as their outer frame, top-level ones should load + // in the extension process. + return aIsSubframe ? aPreferredRemoteType : EXTENSION_REMOTE_TYPE; + } + + return 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 (aIsWorker) { + 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 (aIsWorker) { + throw Components.Exception( + "Unexpected worker with a NestedURI", + Cr.NS_ERROR_UNEXPECTED + ); + } + + let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI; + return this.getRemoteTypeForURIObject( + innerURI, + aMultiProcess, + aRemoteSubframes, + aPreferredRemoteType, + aCurrentUri, + aResultPrincipal, + false, // aIsSubframe + false, // aIsWorker + aOriginAttributes + ); + } + + var log = this.log(); + log.debug("validatedWebRemoteType()"); + log.debug(` aPreferredRemoteType: ${aPreferredRemoteType}`); + log.debug(` aTargetUri: ${this._uriStr(aURI)}`); + log.debug(` aCurrentUri: ${this._uriStr(aCurrentUri)}`); + var remoteType = validatedWebRemoteType( + aPreferredRemoteType, + aURI, + aCurrentUri, + aResultPrincipal, + aRemoteSubframes, + aIsWorker, + aOriginAttributes, + aWorkerType + ); + 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, + aIsMultiProcess, + aIsFission, + aPreferredRemoteType, + null, + aPrincipal, + false, // aIsSubFrame + true, // aIsWorker + aPrincipal.originAttributes, + 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 base64 encoded principal data. + */ + serializePrincipal(principal) { + let serializedPrincipal = null; + + try { + if (principal) { + serializedPrincipal = btoa( + Services.scriptSecurityManager.principalToJSON(principal) + ); + } + } catch (e) { + this.log().error(`Failed to serialize principal '${principal}' ${e}`); + } + + return serializedPrincipal; + }, + + /** + * Deserialize a base64 encoded principal (serialized with + * serializePrincipal). + * + * @param {String} principal_b64 A base64 encoded serialized principal. + * @return {nsIPrincipal} A deserialized principal. + */ + deserializePrincipal(principal_b64, fallbackPrincipalCallback = null) { + if (!principal_b64) { + if (!fallbackPrincipalCallback) { + this.log().warn( + "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" + ); + return null; + } + + return fallbackPrincipalCallback(); + } + + try { + let principal; + let tmpa = atob(principal_b64); + // Both the legacy and new JSON representation of principals are stored as base64 + // The new kind are the only ones that will start with "{" when decoded. + // We check here for the new 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. + if (tmpa.startsWith("{")) { + principal = Services.scriptSecurityManager.JSONToPrincipal(tmpa); + } else { + principal = lazy.serializationHelper.deserializeObject(principal_b64); + } + principal.QueryInterface(Ci.nsIPrincipal); + return principal; + } catch (e) { + this.log().error( + `Failed to deserialize principal_b64 '${principal_b64}' ${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 E10SUtils.serializePrincipal( + Services.scriptSecurityManager.getSystemPrincipal() + ); + } +); |