Bug 1869647 - Mark hasStorageAccess.sub.https.window.html as intermittent after wpt...
[gecko.git] / toolkit / modules / E10SUtils.sys.mjs
blob34da17fc501de70b38ebc1e17968e73eb7be6c3a
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/. */
5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
7 const lazy = {};
9 XPCOMUtils.defineLazyPreferenceGetter(
10   lazy,
11   "useSeparateFileUriProcess",
12   "browser.tabs.remote.separateFileUriProcess",
13   false
15 XPCOMUtils.defineLazyPreferenceGetter(
16   lazy,
17   "useSeparatePrivilegedAboutContentProcess",
18   "browser.tabs.remote.separatePrivilegedContentProcess",
19   false
21 XPCOMUtils.defineLazyPreferenceGetter(
22   lazy,
23   "separatePrivilegedMozillaWebContentProcess",
24   "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
25   false
27 XPCOMUtils.defineLazyPreferenceGetter(
28   lazy,
29   "separatedMozillaDomains",
30   "browser.tabs.remote.separatedMozillaDomains",
31   "",
32   false,
33   val => val.split(",")
36 XPCOMUtils.defineLazyPreferenceGetter(
37   lazy,
38   "useCrossOriginOpenerPolicy",
39   "browser.tabs.remote.useCrossOriginOpenerPolicy",
40   false
42 XPCOMUtils.defineLazyServiceGetter(
43   lazy,
44   "serializationHelper",
45   "@mozilla.org/network/serialization-helper;1",
46   "nsISerializationHelper"
48 XPCOMUtils.defineLazyServiceGetter(
49   lazy,
50   "extProtService",
51   "@mozilla.org/uriloader/external-protocol-service;1",
52   "nsIExternalProtocolService"
55 function getOriginalReaderModeURI(aURI) {
56   try {
57     let searchParams = new URLSearchParams(aURI.query);
58     if (searchParams.has("url")) {
59       return Services.io.newURI(searchParams.get("url"));
60     }
61   } catch (e) {}
62   return null;
65 const NOT_REMOTE = null;
67 // These must match the similar ones in RemoteTypes.h, ProcInfo.h, ChromeUtils.webidl and ChromeUtils.cpp
68 const WEB_REMOTE_TYPE = "web";
69 const FISSION_WEB_REMOTE_TYPE = "webIsolated";
70 const WEB_REMOTE_COOP_COEP_TYPE_PREFIX = "webCOOP+COEP=";
71 const FILE_REMOTE_TYPE = "file";
72 const EXTENSION_REMOTE_TYPE = "extension";
73 const PRIVILEGEDABOUT_REMOTE_TYPE = "privilegedabout";
74 const PRIVILEGEDMOZILLA_REMOTE_TYPE = "privilegedmozilla";
75 const SERVICEWORKER_REMOTE_TYPE = "webServiceWorker";
77 // This must start with the WEB_REMOTE_TYPE above.
78 const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE;
80 // This list is duplicated between Navigator.cpp and here because navigator
81 // is not accessible in this context. Please update both if the list changes.
82 const kSafeSchemes = [
83   "bitcoin",
84   "ftp",
85   "ftps",
86   "geo",
87   "im",
88   "irc",
89   "ircs",
90   "magnet",
91   "mailto",
92   "matrix",
93   "mms",
94   "news",
95   "nntp",
96   "openpgp4fpr",
97   "sftp",
98   "sip",
99   "sms",
100   "smsto",
101   "ssh",
102   "tel",
103   "urn",
104   "webcal",
105   "wtai",
106   "xmpp",
109 const STANDARD_SAFE_PROTOCOLS = kSafeSchemes;
111 // Note that even if the scheme fits the criteria for a web-handled scheme
112 // (ie it is compatible with the checks registerProtocolHandler uses), it may
113 // not be web-handled - it could still be handled via the OS by another app.
114 function hasPotentiallyWebHandledScheme({ scheme }) {
115   // Note that `scheme` comes from a URI object so is already lowercase.
116   if (kSafeSchemes.includes(scheme)) {
117     return true;
118   }
119   if (!scheme.startsWith("web+") || scheme.length < 5) {
120     return false;
121   }
122   // Check the rest of the scheme only consists of ascii a-z chars
123   return /^[a-z]+$/.test(scheme.substr("web+".length));
126 function validatedWebRemoteType(
127   aPreferredRemoteType,
128   aTargetUri,
129   aCurrentUri,
130   aResultPrincipal,
131   aRemoteSubframes,
132   aOriginAttributes = {}
133 ) {
134   // To load into the Privileged Mozilla Content Process you must be https,
135   // and be an exact match or a subdomain of an allowlisted domain.
136   if (
137     lazy.separatePrivilegedMozillaWebContentProcess &&
138     aTargetUri.asciiHost &&
139     aTargetUri.scheme == "https" &&
140     lazy.separatedMozillaDomains.some(function (val) {
141       return (
142         aTargetUri.asciiHost == val || aTargetUri.asciiHost.endsWith("." + val)
143       );
144     })
145   ) {
146     return PRIVILEGEDMOZILLA_REMOTE_TYPE;
147   }
149   // If we're in the parent and we were passed a web-handled scheme,
150   // transform it now to avoid trying to load it in the wrong process.
151   if (aRemoteSubframes && hasPotentiallyWebHandledScheme(aTargetUri)) {
152     if (
153       Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT &&
154       Services.appinfo.remoteType.startsWith(FISSION_WEB_REMOTE_TYPE + "=")
155     ) {
156       // If we're in a child process, assume we're OK to load this non-web
157       // URL for now. We'll either load it externally or re-evaluate once
158       // we know the "real" URL to which we'll redirect.
159       return Services.appinfo.remoteType;
160     }
162     // This doesn't work (throws) in the child - see
163     // https://bugzilla.mozilla.org/show_bug.cgi?id=1589082
164     // Even if it did, it'd cause sync IPC
165     // ( https://bugzilla.mozilla.org/show_bug.cgi?id=1589085 ), and this code
166     // can get called several times per page load so that seems like something
167     // we'd want to avoid.
168     let handlerInfo = lazy.extProtService.getProtocolHandlerInfo(
169       aTargetUri.scheme
170     );
171     try {
172       if (!handlerInfo.alwaysAskBeforeHandling) {
173         let app = handlerInfo.preferredApplicationHandler;
174         app.QueryInterface(Ci.nsIWebHandlerApp);
175         // If we get here, the default handler is a web app.
176         // Target to the origin of that web app:
177         let uriStr = app.uriTemplate.replace(/%s/, aTargetUri.spec);
178         aTargetUri = Services.io.newURI(uriStr);
179       }
180     } catch (ex) {
181       // It's not strange for this to throw, we just ignore it and fall through.
182     }
183   }
185   // If the domain is allow listed to allow it to use file:// URIs, then we have
186   // to run it in a file content process, in case it uses file:// sub-resources.
187   const sm = Services.scriptSecurityManager;
188   if (sm.inFileURIAllowlist(aTargetUri)) {
189     return FILE_REMOTE_TYPE;
190   }
192   // If we're within a fission window, extract site information from the URI in
193   // question, and use it to generate an isolated origin.
194   if (aRemoteSubframes) {
195     let originAttributes = {};
196     // Only use specific properties of OriginAttributes in our remoteType
197     let { userContextId, privateBrowsingId, geckoViewSessionContextId } =
198       aOriginAttributes;
199     originAttributes = {
200       userContextId,
201       privateBrowsingId,
202       geckoViewSessionContextId,
203     };
205     // Get a principal to use for isolation.
206     let targetPrincipal;
207     if (aResultPrincipal) {
208       targetPrincipal = sm.principalWithOA(aResultPrincipal, originAttributes);
209     } else {
210       targetPrincipal = sm.createContentPrincipal(aTargetUri, originAttributes);
211     }
213     // If this is a special webCOOP+COEP= remote type that matches the
214     // principal's siteOrigin, we don't want to override it with webIsolated=
215     // as it's already isolated.
216     if (
217       aPreferredRemoteType &&
218       aPreferredRemoteType.startsWith(
219         `${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}${targetPrincipal.siteOrigin}`
220       )
221     ) {
222       return aPreferredRemoteType;
223     }
225     return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`;
226     // else fall through and probably return WEB_REMOTE_TYPE
227   }
229   if (!aPreferredRemoteType) {
230     return WEB_REMOTE_TYPE;
231   }
233   if (aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)) {
234     return aPreferredRemoteType;
235   }
237   return WEB_REMOTE_TYPE;
240 export var E10SUtils = {
241   DEFAULT_REMOTE_TYPE,
242   NOT_REMOTE,
243   WEB_REMOTE_TYPE,
244   WEB_REMOTE_COOP_COEP_TYPE_PREFIX,
245   FILE_REMOTE_TYPE,
246   EXTENSION_REMOTE_TYPE,
247   PRIVILEGEDABOUT_REMOTE_TYPE,
248   PRIVILEGEDMOZILLA_REMOTE_TYPE,
249   FISSION_WEB_REMOTE_TYPE,
250   SERVICEWORKER_REMOTE_TYPE,
251   STANDARD_SAFE_PROTOCOLS,
253   /**
254    * @param aURI The URI of the about page
255    * @return The instance of the nsIAboutModule related to this uri
256    */
257   getAboutModule(aURL) {
258     // Needs to match NS_GetAboutModuleName
259     let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase();
260     let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
261     try {
262       return Cc[contract].getService(Ci.nsIAboutModule);
263     } catch (e) {
264       // Either the about module isn't defined or it is broken. In either case
265       // ignore it.
266       return null;
267     }
268   },
270   useCrossOriginOpenerPolicy() {
271     return lazy.useCrossOriginOpenerPolicy;
272   },
274   _log: null,
275   _uriStr: function uriStr(aUri) {
276     return aUri ? aUri.spec : "undefined";
277   },
279   log: function log() {
280     if (!this._log) {
281       this._log = console.createInstance({
282         prefix: "ProcessSwitch",
283         maxLogLevel: "Error", // Change to "Debug" the process switching code
284       });
286       this._log.debug("Setup logger");
287     }
289     return this._log;
290   },
292   /**
293    * Serialize csp data.
294    *
295    * @param {nsIContentSecurity} csp. The csp to serialize.
296    * @return {String} The base64 encoded csp data.
297    */
298   serializeCSP(csp) {
299     let serializedCSP = null;
301     try {
302       if (csp) {
303         serializedCSP = lazy.serializationHelper.serializeToString(csp);
304       }
305     } catch (e) {
306       this.log().error(`Failed to serialize csp '${csp}' ${e}`);
307     }
308     return serializedCSP;
309   },
311   /**
312    * Deserialize a base64 encoded csp (serialized with
313    * Utils::serializeCSP).
314    *
315    * @param {String} csp_b64 A base64 encoded serialized csp.
316    * @return {nsIContentSecurityPolicy} A deserialized csp.
317    */
318   deserializeCSP(csp_b64) {
319     if (!csp_b64) {
320       return null;
321     }
323     try {
324       let csp = lazy.serializationHelper.deserializeObject(csp_b64);
325       csp.QueryInterface(Ci.nsIContentSecurityPolicy);
326       return csp;
327     } catch (e) {
328       this.log().error(`Failed to deserialize csp_b64 '${csp_b64}' ${e}`);
329     }
330     return null;
331   },
333   canLoadURIInRemoteType(
334     aURL,
335     aRemoteSubframes,
336     aRemoteType = DEFAULT_REMOTE_TYPE,
337     aOriginAttributes = {}
338   ) {
339     // aRemoteType cannot be undefined, as that would cause it to default to
340     // `DEFAULT_REMOTE_TYPE`. This means any falsy remote types are
341     // intentionally `NOT_REMOTE`.
343     return (
344       aRemoteType ==
345       this.getRemoteTypeForURI(
346         aURL,
347         true,
348         aRemoteSubframes,
349         aRemoteType,
350         null,
351         aOriginAttributes
352       )
353     );
354   },
356   getRemoteTypeForURI(
357     aURL,
358     aMultiProcess,
359     aRemoteSubframes,
360     aPreferredRemoteType = DEFAULT_REMOTE_TYPE,
361     aCurrentUri,
362     aOriginAttributes = {}
363   ) {
364     if (!aMultiProcess) {
365       return NOT_REMOTE;
366     }
368     // loadURI in browser.js treats null as about:blank
369     if (!aURL) {
370       aURL = "about:blank";
371     }
373     let uri;
374     try {
375       uri = Services.uriFixup.getFixupURIInfo(aURL).preferredURI;
376     } catch (e) {
377       // If we have an invalid URI, it's still possible that it might get
378       // fixed-up into a valid URI later on. However, we don't want to return
379       // aPreferredRemoteType here, in case the URI gets fixed-up into
380       // something that wouldn't normally run in that process.
381       return DEFAULT_REMOTE_TYPE;
382     }
384     return this.getRemoteTypeForURIObject(uri, {
385       multiProcess: aMultiProcess,
386       remoteSubFrames: aRemoteSubframes,
387       preferredRemoteType: aPreferredRemoteType,
388       currentURI: aCurrentUri,
389       originAttributes: aOriginAttributes,
390     });
391   },
393   getRemoteTypeForURIObject(aURI, options) {
394     let {
395       multiProcess = Services.appinfo.browserTabsRemoteAutostart,
396       remoteSubFrames = Services.appinfo.fissionAutostart,
397       preferredRemoteType = DEFAULT_REMOTE_TYPE,
398       currentURI = null,
399       resultPrincipal = null,
400       originAttributes = {},
401     } = options;
402     if (!multiProcess) {
403       return NOT_REMOTE;
404     }
406     switch (aURI.scheme) {
407       case "javascript":
408         // javascript URIs can load in any, they apply to the current document.
409         return preferredRemoteType;
411       case "data":
412       case "blob":
413         // We need data: and blob: URIs to load in any remote process, because
414         // they need to be able to load in whatever is the current process
415         // unless it is non-remote. In that case we don't want to load them in
416         // the parent process, so we load them in the default remote process,
417         // which is sandboxed and limits any risk.
418         return preferredRemoteType == NOT_REMOTE
419           ? DEFAULT_REMOTE_TYPE
420           : preferredRemoteType;
422       case "file":
423         return lazy.useSeparateFileUriProcess
424           ? FILE_REMOTE_TYPE
425           : DEFAULT_REMOTE_TYPE;
427       case "about":
428         let module = this.getAboutModule(aURI);
429         // If the module doesn't exist then an error page will be loading, that
430         // should be ok to load in any process
431         if (!module) {
432           return preferredRemoteType;
433         }
435         let flags = module.getURIFlags(aURI);
436         if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
437           return WebExtensionPolicy.useRemoteWebExtensions
438             ? EXTENSION_REMOTE_TYPE
439             : NOT_REMOTE;
440         }
442         if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
443           if (
444             flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS &&
445             (lazy.useSeparatePrivilegedAboutContentProcess ||
446               aURI.filePath == "logins" ||
447               // Force about:welcome and about:home into the privileged content process to
448               // workaround code coverage test failures which result from the
449               // workaround in bug 161269. Once that bug is fixed for real,
450               // the about:welcome and about:home case below can be removed.
451               aURI.filePath == "welcome" ||
452               aURI.filePath == "home")
453           ) {
454             return PRIVILEGEDABOUT_REMOTE_TYPE;
455           }
457           // When loading about:reader, try to display the document in the same
458           // web remote type as the document it's loading.
459           if (aURI.filePath == "reader") {
460             let readerModeURI = getOriginalReaderModeURI(aURI);
461             if (readerModeURI) {
462               let innerRemoteType = this.getRemoteTypeForURIObject(
463                 readerModeURI,
464                 options
465               );
466               if (
467                 innerRemoteType &&
468                 innerRemoteType.startsWith(WEB_REMOTE_TYPE)
469               ) {
470                 return innerRemoteType;
471               }
472             }
473           }
475           return DEFAULT_REMOTE_TYPE;
476         }
478         // If the about page can load in parent or child, it should be safe to
479         // load in any remote type.
480         if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
481           return preferredRemoteType;
482         }
484         return NOT_REMOTE;
486       case "chrome":
487         let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
488           Ci.nsIXULChromeRegistry
489         );
490         if (chromeReg.mustLoadURLRemotely(aURI)) {
491           return DEFAULT_REMOTE_TYPE;
492         }
494         if (
495           chromeReg.canLoadURLRemotely(aURI) &&
496           preferredRemoteType != NOT_REMOTE
497         ) {
498           return DEFAULT_REMOTE_TYPE;
499         }
501         return NOT_REMOTE;
503       case "moz-extension":
504         // Extension iframes should load in the same process
505         // as their outer frame, but that's handled elsewhere.
506         return WebExtensionPolicy.useRemoteWebExtensions
507           ? EXTENSION_REMOTE_TYPE
508           : NOT_REMOTE;
510       case "imap":
511       case "mailbox":
512       case "news":
513       case "nntp":
514       case "snews":
515         // Protocols used by Thunderbird to display email messages.
516         return NOT_REMOTE;
518       default:
519         // WebExtensions may set up protocol handlers for protocol names
520         // beginning with ext+.  These may redirect to http(s) pages or to
521         // moz-extension pages.  We can't actually tell here where one of
522         // these pages will end up loading but Talos tests use protocol
523         // handlers that redirect to extension pages that rely on this
524         // behavior so a pageloader frame script is injected correctly.
525         // Protocols that redirect to http(s) will just flip back to a
526         // regular content process after the redirect.
527         if (aURI.scheme.startsWith("ext+")) {
528           return WebExtensionPolicy.useRemoteWebExtensions
529             ? EXTENSION_REMOTE_TYPE
530             : NOT_REMOTE;
531         }
533         // For any other nested URIs, we use the innerURI to determine the
534         // remote type. In theory we should use the innermost URI, but some URIs
535         // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and
536         // if such URIs are wrapped in other nested schemes like view-source:,
537         // we don't want to "skip" past "about:" by going straight to the
538         // innermost URI. Any URIs like this will need to be handled in the
539         // cases above, so we don't still end up using the fake inner URI here.
540         if (aURI instanceof Ci.nsINestedURI) {
541           let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
542           return this.getRemoteTypeForURIObject(innerURI, options);
543         }
545         var log = this.log();
546         log.debug("validatedWebRemoteType()");
547         log.debug(`  aPreferredRemoteType: ${preferredRemoteType}`);
548         log.debug(`  aTargetUri: ${this._uriStr(aURI)}`);
549         log.debug(`  aCurrentUri: ${this._uriStr(currentURI)}`);
550         var remoteType = validatedWebRemoteType(
551           preferredRemoteType,
552           aURI,
553           currentURI,
554           resultPrincipal,
555           remoteSubFrames,
556           originAttributes
557         );
558         log.debug(`  validatedWebRemoteType() returning: ${remoteType}`);
559         return remoteType;
560     }
561   },
563   makeInputStream(data) {
564     if (typeof data == "string") {
565       let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
566         Ci.nsISupportsCString
567       );
568       stream.data = data;
569       return stream; // XPConnect will QI this to nsIInputStream for us.
570     }
572     let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
573       Ci.nsISupportsCString
574     );
575     stream.data = data.content;
577     if (data.headers) {
578       let mimeStream = Cc[
579         "@mozilla.org/network/mime-input-stream;1"
580       ].createInstance(Ci.nsIMIMEInputStream);
582       mimeStream.setData(stream);
583       for (let [name, value] of data.headers) {
584         mimeStream.addHeader(name, value);
585       }
586       return mimeStream;
587     }
589     return stream; // XPConnect will QI this to nsIInputStream for us.
590   },
592   /**
593    * Serialize principal data.
594    *
595    * @param {nsIPrincipal} principal The principal to serialize.
596    * @return {String} The serialized principal data.
597    */
598   serializePrincipal(principal) {
599     let serializedPrincipal = null;
601     try {
602       if (principal) {
603         serializedPrincipal =
604           Services.scriptSecurityManager.principalToJSON(principal);
605       }
606     } catch (e) {
607       this.log().error(`Failed to serialize principal '${principal}' ${e}`);
608     }
610     return serializedPrincipal;
611   },
613   /**
614    * Deserialize a principal (serialized with serializePrincipal).
615    *
616    * @param {String} serializedPincipal A serialized principal.
617    * @return {nsIPrincipal} A deserialized principal.
618    */
619   deserializePrincipal(serializedPincipal, fallbackPrincipalCallback = null) {
620     if (!serializedPincipal) {
621       if (!fallbackPrincipalCallback) {
622         this.log().warn(
623           "No principal passed to deserializePrincipal and no fallbackPrincipalCallback"
624         );
625         return null;
626       }
628       return fallbackPrincipalCallback();
629     }
631     try {
632       let principal;
633       // The current JSON representation of principal is not stored as base64. We start by checking
634       // if the serialized data starts with '{' to determine if we're using the new JSON representation.
635       // If it doesn't we try the two legacy formats, old JSON and nsISerializable.
636       if (serializedPincipal.startsWith("{")) {
637         principal =
638           Services.scriptSecurityManager.JSONToPrincipal(serializedPincipal);
639       } else {
640         // Both the legacy and legacy  JSON representation of principals are stored as base64
641         // The legacy JSON kind are the only ones that will start with "{" when decoded.
642         // We check here for the legacy JSON serialized, if it doesn't start with that continue using nsISerializable.
643         // JSONToPrincipal accepts a *non* base64 encoded string and returns a principal or a null.
644         let tmpa = atob(serializedPincipal);
645         if (tmpa.startsWith("{")) {
646           principal = Services.scriptSecurityManager.JSONToPrincipal(tmpa);
647         } else {
648           principal =
649             lazy.serializationHelper.deserializeObject(serializedPincipal);
650         }
651       }
652       principal.QueryInterface(Ci.nsIPrincipal);
653       return principal;
654     } catch (e) {
655       this.log().error(
656         `Failed to deserialize serializedPincipal '${serializedPincipal}' ${e}`
657       );
658     }
659     if (!fallbackPrincipalCallback) {
660       this.log().warn(
661         "No principal passed to deserializePrincipal and no fallbackPrincipalCallback"
662       );
663       return null;
664     }
665     return fallbackPrincipalCallback();
666   },
668   /**
669    * Serialize cookieJarSettings.
670    *
671    * @param {nsICookieJarSettings} cookieJarSettings The cookieJarSettings to
672    *   serialize.
673    * @return {String} The base64 encoded cookieJarSettings data.
674    */
675   serializeCookieJarSettings(cookieJarSettings) {
676     let serialized = null;
677     if (cookieJarSettings) {
678       try {
679         serialized =
680           lazy.serializationHelper.serializeToString(cookieJarSettings);
681       } catch (e) {
682         this.log().error(
683           `Failed to serialize cookieJarSettings '${cookieJarSettings}' ${e}`
684         );
685       }
686     }
687     return serialized;
688   },
690   /**
691    * Deserialize a base64 encoded cookieJarSettings
692    *
693    * @param {String} cookieJarSettings_b64 A base64 encoded serialized cookieJarSettings.
694    * @return {nsICookieJarSettings} A deserialized cookieJarSettings.
695    */
696   deserializeCookieJarSettings(cookieJarSettings_b64) {
697     let deserialized = null;
698     if (cookieJarSettings_b64) {
699       try {
700         deserialized = lazy.serializationHelper.deserializeObject(
701           cookieJarSettings_b64
702         );
703         deserialized.QueryInterface(Ci.nsICookieJarSettings);
704       } catch (e) {
705         this.log().error(
706           `Failed to deserialize cookieJarSettings_b64 '${cookieJarSettings_b64}' ${e}`
707         );
708       }
709     }
710     return deserialized;
711   },
713   wrapHandlingUserInput(aWindow, aIsHandling, aCallback) {
714     var handlingUserInput;
715     try {
716       handlingUserInput = aWindow.windowUtils.setHandlingUserInput(aIsHandling);
717       aCallback();
718     } finally {
719       handlingUserInput.destruct();
720     }
721   },
723   /**
724    * Serialize referrerInfo.
725    *
726    * @param {nsIReferrerInfo} The referrerInfo to serialize.
727    * @return {String} The base64 encoded referrerInfo.
728    */
729   serializeReferrerInfo(referrerInfo) {
730     let serialized = null;
731     if (referrerInfo) {
732       try {
733         serialized = lazy.serializationHelper.serializeToString(referrerInfo);
734       } catch (e) {
735         this.log().error(
736           `Failed to serialize referrerInfo '${referrerInfo}' ${e}`
737         );
738       }
739     }
740     return serialized;
741   },
742   /**
743    * Deserialize a base64 encoded referrerInfo
744    *
745    * @param {String} referrerInfo_b64 A base64 encoded serialized referrerInfo.
746    * @return {nsIReferrerInfo} A deserialized referrerInfo.
747    */
748   deserializeReferrerInfo(referrerInfo_b64) {
749     let deserialized = null;
750     if (referrerInfo_b64) {
751       try {
752         deserialized =
753           lazy.serializationHelper.deserializeObject(referrerInfo_b64);
754         deserialized.QueryInterface(Ci.nsIReferrerInfo);
755       } catch (e) {
756         this.log().error(
757           `Failed to deserialize referrerInfo_b64 '${referrerInfo_b64}' ${e}`
758         );
759       }
760     }
761     return deserialized;
762   },
764   /**
765    * Returns the pids for a remote browser and its remote subframes.
766    */
767   getBrowserPids(aBrowser, aRemoteSubframes) {
768     if (!aBrowser.isRemoteBrowser || !aBrowser.frameLoader) {
769       return [];
770     }
771     let tabPid = aBrowser.frameLoader.remoteTab.osPid;
772     let pids = new Set();
773     if (aRemoteSubframes) {
774       let stack = [aBrowser.browsingContext];
775       while (stack.length) {
776         let bc = stack.pop();
777         stack.push(...bc.children);
778         if (bc.currentWindowGlobal) {
779           let pid = bc.currentWindowGlobal.osPid;
780           if (pid != tabPid) {
781             pids.add(pid);
782           }
783         }
784       }
785     }
786     return [tabPid, ...pids];
787   },
789   /**
790    * The suffix after a `=` in a remoteType is dynamic, and used to control the
791    * process pool to use. The C++ version of this method is mozilla::dom::RemoteTypePrefix().
792    */
793   remoteTypePrefix(aRemoteType) {
794     return aRemoteType.split("=")[0];
795   },
797   /**
798    * There are various types of remote types that are for web content processes, but
799    * they all start with "web". The C++ version of this method is
800    * mozilla::dom::IsWebRemoteType().
801    */
802   isWebRemoteType(aRemoteType) {
803     return aRemoteType.startsWith(WEB_REMOTE_TYPE);
804   },
806   /**
807    * Assemble or predict originAttributes from available arguments.
808    */
809   predictOriginAttributes({
810     window,
811     browser,
812     userContextId,
813     geckoViewSessionContextId,
814     privateBrowsingId,
815   }) {
816     if (browser) {
817       if (browser.browsingContext) {
818         return browser.browsingContext.originAttributes;
819       }
820       if (!window) {
821         window = browser.contentDocument?.defaultView;
822       }
823       if (!userContextId) {
824         userContextId = browser.getAttribute("usercontextid") || 0;
825       }
826       if (!geckoViewSessionContextId) {
827         geckoViewSessionContextId =
828           browser.getAttribute("geckoViewSessionContextId") || "";
829       }
830     }
832     if (window && !privateBrowsingId) {
833       privateBrowsingId = window.browsingContext.usePrivateBrowsing ? 1 : 0;
834     }
835     return { privateBrowsingId, userContextId, geckoViewSessionContextId };
836   },
839 ChromeUtils.defineLazyGetter(
840   E10SUtils,
841   "SERIALIZED_SYSTEMPRINCIPAL",
842   function () {
843     return btoa(
844       E10SUtils.serializePrincipal(
845         Services.scriptSecurityManager.getSystemPrincipal()
846       )
847     );
848   }