/* 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/. */ "use strict"; var EXPORTED_SYMBOLS = ["E10SUtils"]; const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); const { XPCOMUtils } = ChromeUtils.import( "resource://gre/modules/XPCOMUtils.jsm" ); ChromeUtils.defineModuleGetter( this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm" ); XPCOMUtils.defineLazyPreferenceGetter( this, "useSeparateFileUriProcess", "browser.tabs.remote.separateFileUriProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "useSeparateDataUriProcess", "browser.tabs.remote.dataUriInDefaultWebProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "allowLinkedWebInFileUriProcess", "browser.tabs.remote.allowLinkedWebInFileUriProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "useSeparatePrivilegedAboutContentProcess", "browser.tabs.remote.separatePrivilegedContentProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "separatePrivilegedMozillaWebContentProcess", "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "separatedMozillaDomains", "browser.tabs.remote.separatedMozillaDomains", false, false, val => val.split(",") ); XPCOMUtils.defineLazyPreferenceGetter( this, "useHttpResponseProcessSelection", "browser.tabs.remote.useHTTPResponseProcessSelection", false ); XPCOMUtils.defineLazyPreferenceGetter( this, "useCrossOriginOpenerPolicy", "browser.tabs.remote.useCrossOriginOpenerPolicy", false ); XPCOMUtils.defineLazyServiceGetter( this, "serializationHelper", "@mozilla.org/network/serialization-helper;1", "nsISerializationHelper" ); XPCOMUtils.defineLazyServiceGetter( this, "extProtService", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService" ); function debug(msg) { Cu.reportError(new Error("E10SUtils: " + msg)); } function 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; } } const NOT_REMOTE = null; // These must match any similar ones in ContentParent.h and ProcInfo.h 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"; // This must start with the WEB_REMOTE_TYPE above. const LARGE_ALLOCATION_REMOTE_TYPE = "webLargeAllocation"; 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", "geo", "im", "irc", "ircs", "magnet", "mailto", "mms", "news", "nntp", "openpgp4fpr", "sip", "sms", "smsto", "ssh", "tel", "urn", "webcal", "wtai", "xmpp", ]; // 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 ) { // 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 ( separatePrivilegedMozillaWebContentProcess && aTargetUri.asciiHost && aTargetUri.scheme == "https" && 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)) { 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 = 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 whitelisted 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 (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) { // To be consistent with remote types when using a principal vs. a URI, // always clear OAs. // FIXME: This should be accurate. let originAttributes = {}; // 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 == `${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}${targetPrincipal.siteOrigin}` ) { return aPreferredRemoteType; } return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`; } if (!aPreferredRemoteType) { return WEB_REMOTE_TYPE; } if (aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)) { return aPreferredRemoteType; } if ( allowLinkedWebInFileUriProcess && aPreferredRemoteType == FILE_REMOTE_TYPE ) { // If aCurrentUri is passed then we should only allow FILE_REMOTE_TYPE // when it is same origin as target. if (aCurrentUri) { try { // checkSameOriginURI throws when not same origin. // todo: if you intend to update CheckSameOriginURI to log the error to the // console you also need to update the 'aFromPrivateWindow' argument. sm.checkSameOriginURI(aCurrentUri, aTargetUri, false, false); return FILE_REMOTE_TYPE; } catch (e) { return WEB_REMOTE_TYPE; } } return FILE_REMOTE_TYPE; } return WEB_REMOTE_TYPE; } 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, LARGE_ALLOCATION_REMOTE_TYPE, useHttpResponseProcessSelection() { return useHttpResponseProcessSelection; }, useCrossOriginOpenerPolicy() { return useCrossOriginOpenerPolicy; }, /** * 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 = serializationHelper.serializeToString(csp); } } catch (e) { debug(`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 = serializationHelper.deserializeObject(csp_b64); csp.QueryInterface(Ci.nsIContentSecurityPolicy); return csp; } catch (e) { debug(`Failed to deserialize csp_b64 '${csp_b64}' ${e}`); } return null; }, canLoadURIInRemoteType( aURL, aRemoteSubframes, aRemoteType = DEFAULT_REMOTE_TYPE ) { // 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) ); }, getRemoteTypeForURI( aURL, aMultiProcess, aRemoteSubframes, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, aCurrentUri ) { 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.createFixupURI( aURL, Ci.nsIURIFixup.FIXUP_FLAG_NONE ); } 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 ); }, getRemoteTypeForURIObject( aURI, aMultiProcess, aRemoteSubframes, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, aCurrentUri = null, aResultPrincipal = null, aIsSubframe = false ) { 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 useSeparateFileUriProcess ? FILE_REMOTE_TYPE : DEFAULT_REMOTE_TYPE; case "about": let module = 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 && (useSeparatePrivilegedAboutContentProcess || aURI.filePath == "logins") ) { return PRIVILEGEDABOUT_REMOTE_TYPE; } 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; 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+")) { 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) { let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI; return this.getRemoteTypeForURIObject( innerURI, aMultiProcess, aRemoteSubframes, aPreferredRemoteType, aCurrentUri, aResultPrincipal ); } return validatedWebRemoteType( aPreferredRemoteType, aURI, aCurrentUri, aResultPrincipal, aRemoteSubframes ); } }, getRemoteTypeForPrincipal( aPrincipal, aMultiProcess, aRemoteSubframes, aPreferredRemoteType = DEFAULT_REMOTE_TYPE, aCurrentPrincipal, aIsSubframe ) { if (!aMultiProcess) { return NOT_REMOTE; } // We can't pick a process based on a system principal or expanded // principal. In fact, we should never end up with one here! if (aPrincipal.isSystemPrincipal || aPrincipal.isExpandedPrincipal) { throw Cr.NS_ERROR_UNEXPECTED; } // Null principals can be loaded in any remote process, but when // using fission we add the option to force them into the default // web process for better test coverage. if (aPrincipal.isNullPrincipal) { if ( (aRemoteSubframes && useSeparateDataUriProcess) || aPreferredRemoteType == NOT_REMOTE ) { return WEB_REMOTE_TYPE; } return aPreferredRemoteType; } // We might care about the currently loaded URI. Pull it out of our current // principal. We never care about the current URI when working with a // non-content principal. let currentURI = aCurrentPrincipal && aCurrentPrincipal.isContentPrincipal ? aCurrentPrincipal.URI : null; return E10SUtils.getRemoteTypeForURIObject( aPrincipal.URI, aMultiProcess, aRemoteSubframes, aPreferredRemoteType, currentURI, aPrincipal, aIsSubframe ); }, 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) { debug(`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) { debug( "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 = serializationHelper.deserializeObject(principal_b64); } principal.QueryInterface(Ci.nsIPrincipal); return principal; } catch (e) { debug(`Failed to deserialize principal_b64 '${principal_b64}' ${e}`); } if (!fallbackPrincipalCallback) { debug( "No principal passed to deserializePrincipal and no fallbackPrincipalCallback" ); return null; } return fallbackPrincipalCallback(); }, shouldLoadURIInBrowser( browser, uri, multiProcess = true, remoteSubframes = false, flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE ) { let currentRemoteType = browser.remoteType; let requiredRemoteType; let uriObject; try { let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE; if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) { fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; } if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) { fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS; } if (PrivateBrowsingUtils.isBrowserPrivate(browser)) { fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_PRIVATE_CONTEXT; } uriObject = Services.uriFixup.createFixupURI(uri, fixupFlags); // Note that I had thought that we could set uri = uriObject.spec here, to // save on fixup later on, but that changes behavior and breaks tests. requiredRemoteType = this.getRemoteTypeForURIObject( uriObject, multiProcess, remoteSubframes, currentRemoteType, browser.currentURI ); } catch (e) { // createFixupURI throws if it can't create a URI. If that's the case then // we still need to pass down the uri because docshell handles this case. requiredRemoteType = multiProcess ? DEFAULT_REMOTE_TYPE : NOT_REMOTE; } let mustChangeProcess = requiredRemoteType != currentRemoteType; let newFrameloader = false; if ( browser.getAttribute("preloadedState") === "consumed" && uri != "about:newtab" ) { // Leaving about:newtab from a used to be preloaded browser should run the process // selecting algorithm again. mustChangeProcess = true; newFrameloader = true; } return { uriObject, requiredRemoteType, mustChangeProcess, newFrameloader, }; }, shouldLoadURIInThisProcess(aURI, aRemoteSubframes) { let remoteType = Services.appinfo.remoteType; return ( remoteType == this.getRemoteTypeForURIObject( aURI, /* remote */ true, aRemoteSubframes, remoteType ) ); }, shouldLoadURI(aDocShell, aURI, aHasPostData) { let { useRemoteSubframes } = aDocShell; // Inner frames should always load in the current process // XXX(nika): Handle shouldLoadURI-triggered process switches for remote // subframes! (bug 1548942) if (aDocShell.sameTypeParent) { return true; } let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation); let sessionHistory = webNav.sessionHistory; if ( !aHasPostData && Services.appinfo.remoteType == WEB_REMOTE_TYPE && sessionHistory.count == 1 && webNav.currentURI.spec == "about:newtab" ) { // This is possibly a preloaded browser and we're about to navigate away for // the first time. On the child side there is no way to tell for sure if that // is the case, so let's redirect this request to the parent to decide if a new // process is needed. But we don't currently properly handle POST data in // redirects (bug 1457520), so if there is POST data, don't return false here. return false; } // If we are performing HTTP response process selection, and are loading an // HTTP URI, we can start the load in the current process, and then perform // the switch later-on using the RedirectProcessChooser mechanism. // // We should never be sending a POST request from the parent process to a // http(s) uri, so make sure we switch if we're currently in that process. if ( (useRemoteSubframes || useHttpResponseProcessSelection) && (aURI.scheme == "http" || aURI.scheme == "https" || aURI.scheme == "data") && Services.appinfo.remoteType != NOT_REMOTE ) { return true; } // If we are in a Large-Allocation process, and it wouldn't be content visible // to change processes, we want to load into a new process so that we can throw // this one out. We don't want to move into a new process if we have post data, // because we would accidentally throw out that data. let isOnlyToplevelBrowsingContext = !aDocShell.browsingContext.parent && aDocShell.browsingContext.group.getToplevels().length == 1; if ( !aHasPostData && Services.appinfo.remoteType == LARGE_ALLOCATION_REMOTE_TYPE && !aDocShell.awaitingLargeAlloc && isOnlyToplevelBrowsingContext ) { return false; } // Allow history load if loaded in this process before. let requestedIndex = sessionHistory.legacySHistory.requestedIndex; if (requestedIndex >= 0) { if ( sessionHistory.legacySHistory.getEntryAtIndex(requestedIndex) .loadedInThisProcess ) { return true; } // If not originally loaded in this process allow it if the URI would // normally be allowed to load in this process by default. let remoteType = Services.appinfo.remoteType; return ( remoteType == this.getRemoteTypeForURIObject( aURI, true, useRemoteSubframes, remoteType, webNav.currentURI ) ); } // If the URI can be loaded in the current process then continue return this.shouldLoadURIInThisProcess(aURI, useRemoteSubframes); }, redirectLoad( aDocShell, aURI, aReferrerInfo, aTriggeringPrincipal, aFreshProcess, aFlags, aCsp ) { // Retarget the load to the correct process let messageManager = aDocShell.messageManager; let sessionHistory = aDocShell.QueryInterface(Ci.nsIWebNavigation) .sessionHistory; messageManager.sendAsyncMessage("Browser:LoadURI", { loadOptions: { uri: aURI.spec, flags: aFlags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE, referrerInfo: this.serializeReferrerInfo(aReferrerInfo), triggeringPrincipal: this.serializePrincipal( aTriggeringPrincipal || Services.scriptSecurityManager.createNullPrincipal({}) ), csp: aCsp ? this.serializeCSP(aCsp) : null, reloadInFreshProcess: !!aFreshProcess, }, historyIndex: sessionHistory.legacySHistory.requestedIndex, }); return false; }, 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 = serializationHelper.serializeToString(referrerInfo); } catch (e) { debug(`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 = serializationHelper.deserializeObject(referrerInfo_b64); deserialized.QueryInterface(Ci.nsIReferrerInfo); } catch (e) { debug( `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.getChildren()); 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); }, }; XPCOMUtils.defineLazyGetter( E10SUtils, "SERIALIZED_SYSTEMPRINCIPAL", function() { return E10SUtils.serializePrincipal( Services.scriptSecurityManager.getSystemPrincipal() ); } ); XPCOMUtils.defineLazyPreferenceGetter( E10SUtils, "rebuildFrameloadersOnRemotenessChange", "fission.rebuild_frameloaders_on_remoteness_change", false );