Bug 1586801 - Use the contextual WalkerFront in _duplicateNode(). r=pbro
[gecko.git] / toolkit / modules / E10SUtils.jsm
blob3c890db8f2eb73674f01e592142029a79294f09b
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 "use strict";
7 var EXPORTED_SYMBOLS = ["E10SUtils"];
9 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
10 const { XPCOMUtils } = ChromeUtils.import(
11   "resource://gre/modules/XPCOMUtils.jsm"
14 XPCOMUtils.defineLazyPreferenceGetter(
15   this,
16   "useSeparateFileUriProcess",
17   "browser.tabs.remote.separateFileUriProcess",
18   false
20 XPCOMUtils.defineLazyPreferenceGetter(
21   this,
22   "allowLinkedWebInFileUriProcess",
23   "browser.tabs.remote.allowLinkedWebInFileUriProcess",
24   false
26 XPCOMUtils.defineLazyPreferenceGetter(
27   this,
28   "useSeparatePrivilegedAboutContentProcess",
29   "browser.tabs.remote.separatePrivilegedContentProcess",
30   false
32 XPCOMUtils.defineLazyPreferenceGetter(
33   this,
34   "separatePrivilegedMozillaWebContentProcess",
35   "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
36   false
38 XPCOMUtils.defineLazyPreferenceGetter(
39   this,
40   "separatedMozillaDomains",
41   "browser.tabs.remote.separatedMozillaDomains",
42   false,
43   false,
44   val => val.split(",")
46 XPCOMUtils.defineLazyPreferenceGetter(
47   this,
48   "useHttpResponseProcessSelection",
49   "browser.tabs.remote.useHTTPResponseProcessSelection",
50   false
52 XPCOMUtils.defineLazyPreferenceGetter(
53   this,
54   "useCrossOriginOpenerPolicy",
55   "browser.tabs.remote.useCrossOriginOpenerPolicy",
56   false
58 XPCOMUtils.defineLazyServiceGetter(
59   this,
60   "serializationHelper",
61   "@mozilla.org/network/serialization-helper;1",
62   "nsISerializationHelper"
65 function debug(msg) {
66   Cu.reportError(new Error("E10SUtils: " + msg));
69 function getAboutModule(aURL) {
70   // Needs to match NS_GetAboutModuleName
71   let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase();
72   let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
73   try {
74     return Cc[contract].getService(Ci.nsIAboutModule);
75   } catch (e) {
76     // Either the about module isn't defined or it is broken. In either case
77     // ignore it.
78     return null;
79   }
82 const NOT_REMOTE = null;
84 // These must match any similar ones in ContentParent.h and ProcInfo.h
85 const WEB_REMOTE_TYPE = "web";
86 const FISSION_WEB_REMOTE_TYPE_PREFIX = "webIsolated=";
87 const FILE_REMOTE_TYPE = "file";
88 const EXTENSION_REMOTE_TYPE = "extension";
89 const PRIVILEGEDABOUT_REMOTE_TYPE = "privilegedabout";
90 const PRIVILEGEDMOZILLA_REMOTE_TYPE = "privilegedmozilla";
92 // This must start with the WEB_REMOTE_TYPE above.
93 const LARGE_ALLOCATION_REMOTE_TYPE = "webLargeAllocation";
94 const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE;
96 function validatedWebRemoteType(
97   aPreferredRemoteType,
98   aTargetUri,
99   aCurrentUri,
100   aRemoteSubframes
101 ) {
102   // To load into the Privileged Mozilla Content Process you must be https,
103   // and be an exact match or a subdomain of an allowlisted domain.
104   if (
105     separatePrivilegedMozillaWebContentProcess &&
106     aTargetUri.asciiHost &&
107     aTargetUri.scheme == "https" &&
108     separatedMozillaDomains.some(function(val) {
109       return (
110         aTargetUri.asciiHost == val || aTargetUri.asciiHost.endsWith("." + val)
111       );
112     })
113   ) {
114     return PRIVILEGEDMOZILLA_REMOTE_TYPE;
115   }
117   // If the domain is whitelisted to allow it to use file:// URIs, then we have
118   // to run it in a file content process, in case it uses file:// sub-resources.
119   const sm = Services.scriptSecurityManager;
120   if (sm.inFileURIAllowlist(aTargetUri)) {
121     return FILE_REMOTE_TYPE;
122   }
124   // If we're within a fission window, extract site information from the URI in
125   // question, and use it to generate an isolated origin.
126   if (aRemoteSubframes) {
127     let targetPrincipal = sm.createContentPrincipal(aTargetUri, {});
128     return FISSION_WEB_REMOTE_TYPE_PREFIX + targetPrincipal.siteOrigin;
129   }
131   if (!aPreferredRemoteType) {
132     return WEB_REMOTE_TYPE;
133   }
135   if (aPreferredRemoteType.startsWith(WEB_REMOTE_TYPE)) {
136     return aPreferredRemoteType;
137   }
139   if (
140     allowLinkedWebInFileUriProcess &&
141     aPreferredRemoteType == FILE_REMOTE_TYPE
142   ) {
143     // If aCurrentUri is passed then we should only allow FILE_REMOTE_TYPE
144     // when it is same origin as target.
145     if (aCurrentUri) {
146       try {
147         // checkSameOriginURI throws when not same origin.
148         // todo: if you intend to update CheckSameOriginURI to log the error to the
149         // console you also need to update the 'aFromPrivateWindow' argument.
150         sm.checkSameOriginURI(aCurrentUri, aTargetUri, false, false);
151         return FILE_REMOTE_TYPE;
152       } catch (e) {
153         return WEB_REMOTE_TYPE;
154       }
155     }
157     return FILE_REMOTE_TYPE;
158   }
160   return WEB_REMOTE_TYPE;
163 var E10SUtils = {
164   DEFAULT_REMOTE_TYPE,
165   NOT_REMOTE,
166   WEB_REMOTE_TYPE,
167   FILE_REMOTE_TYPE,
168   EXTENSION_REMOTE_TYPE,
169   PRIVILEGEDABOUT_REMOTE_TYPE,
170   PRIVILEGEDMOZILLA_REMOTE_TYPE,
171   LARGE_ALLOCATION_REMOTE_TYPE,
173   useHttpResponseProcessSelection() {
174     return useHttpResponseProcessSelection;
175   },
176   useCrossOriginOpenerPolicy() {
177     return useCrossOriginOpenerPolicy;
178   },
180   /**
181    * Serialize csp data.
182    *
183    * @param {nsIContentSecurity} csp. The csp to serialize.
184    * @return {String} The base64 encoded csp data.
185    */
186   serializeCSP(csp) {
187     let serializedCSP = null;
189     try {
190       if (csp) {
191         serializedCSP = serializationHelper.serializeToString(csp);
192       }
193     } catch (e) {
194       debug(`Failed to serialize csp '${csp}' ${e}`);
195     }
196     return serializedCSP;
197   },
199   /**
200    * Deserialize a base64 encoded csp (serialized with
201    * Utils::serializeCSP).
202    *
203    * @param {String} csp_b64 A base64 encoded serialized csp.
204    * @return {nsIContentSecurityPolicy} A deserialized csp.
205    */
206   deserializeCSP(csp_b64) {
207     if (!csp_b64) {
208       return null;
209     }
211     try {
212       let csp = serializationHelper.deserializeObject(csp_b64);
213       csp.QueryInterface(Ci.nsIContentSecurityPolicy);
214       return csp;
215     } catch (e) {
216       debug(`Failed to deserialize csp_b64 '${csp_b64}' ${e}`);
217     }
218     return null;
219   },
221   canLoadURIInRemoteType(
222     aURL,
223     aRemoteSubframes,
224     aRemoteType = DEFAULT_REMOTE_TYPE,
225     aPreferredRemoteType = undefined
226   ) {
227     // We need a strict equality here because the value of `NOT_REMOTE` is
228     // `null`, and there is a possibility that `undefined` is passed as an
229     // argument, which might result a load in the parent process.
230     if (aPreferredRemoteType === undefined) {
231       aPreferredRemoteType =
232         aRemoteType === NOT_REMOTE ? NOT_REMOTE : DEFAULT_REMOTE_TYPE;
233     }
235     return (
236       aRemoteType ==
237       this.getRemoteTypeForURI(
238         aURL,
239         true,
240         aRemoteSubframes,
241         aPreferredRemoteType
242       )
243     );
244   },
246   getRemoteTypeForURI(
247     aURL,
248     aMultiProcess,
249     aRemoteSubframes,
250     aPreferredRemoteType = DEFAULT_REMOTE_TYPE,
251     aCurrentUri
252   ) {
253     if (!aMultiProcess) {
254       return NOT_REMOTE;
255     }
257     // loadURI in browser.js treats null as about:blank
258     if (!aURL) {
259       aURL = "about:blank";
260     }
262     let uri;
263     try {
264       uri = Services.uriFixup.createFixupURI(
265         aURL,
266         Ci.nsIURIFixup.FIXUP_FLAG_NONE
267       );
268     } catch (e) {
269       // If we have an invalid URI, it's still possible that it might get
270       // fixed-up into a valid URI later on. However, we don't want to return
271       // aPreferredRemoteType here, in case the URI gets fixed-up into
272       // something that wouldn't normally run in that process.
273       return DEFAULT_REMOTE_TYPE;
274     }
276     return this.getRemoteTypeForURIObject(
277       uri,
278       aMultiProcess,
279       aRemoteSubframes,
280       aPreferredRemoteType,
281       aCurrentUri
282     );
283   },
285   getRemoteTypeForURIObject(
286     aURI,
287     aMultiProcess,
288     aRemoteSubframes,
289     aPreferredRemoteType = DEFAULT_REMOTE_TYPE,
290     aCurrentUri
291   ) {
292     if (!aMultiProcess) {
293       return NOT_REMOTE;
294     }
296     switch (aURI.scheme) {
297       case "javascript":
298         // javascript URIs can load in any, they apply to the current document.
299         return aPreferredRemoteType;
301       case "data":
302       case "blob":
303         // We need data: and blob: URIs to load in any remote process, because
304         // they need to be able to load in whatever is the current process
305         // unless it is non-remote. In that case we don't want to load them in
306         // the parent process, so we load them in the default remote process,
307         // which is sandboxed and limits any risk.
308         return aPreferredRemoteType == NOT_REMOTE
309           ? DEFAULT_REMOTE_TYPE
310           : aPreferredRemoteType;
312       case "file":
313         return useSeparateFileUriProcess
314           ? FILE_REMOTE_TYPE
315           : DEFAULT_REMOTE_TYPE;
317       case "about":
318         let module = getAboutModule(aURI);
319         // If the module doesn't exist then an error page will be loading, that
320         // should be ok to load in any process
321         if (!module) {
322           return aPreferredRemoteType;
323         }
325         let flags = module.getURIFlags(aURI);
326         if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
327           return WebExtensionPolicy.useRemoteWebExtensions
328             ? EXTENSION_REMOTE_TYPE
329             : NOT_REMOTE;
330         }
332         if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
333           if (
334             flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS &&
335             (useSeparatePrivilegedAboutContentProcess ||
336               aURI.filePath == "logins")
337           ) {
338             return PRIVILEGEDABOUT_REMOTE_TYPE;
339           }
340           return DEFAULT_REMOTE_TYPE;
341         }
343         // If the about page can load in parent or child, it should be safe to
344         // load in any remote type.
345         if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
346           return aPreferredRemoteType;
347         }
349         return NOT_REMOTE;
351       case "chrome":
352         let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(
353           Ci.nsIXULChromeRegistry
354         );
355         if (chromeReg.mustLoadURLRemotely(aURI)) {
356           return DEFAULT_REMOTE_TYPE;
357         }
359         if (
360           chromeReg.canLoadURLRemotely(aURI) &&
361           aPreferredRemoteType != NOT_REMOTE
362         ) {
363           return DEFAULT_REMOTE_TYPE;
364         }
366         return NOT_REMOTE;
368       case "moz-extension":
369         return WebExtensionPolicy.useRemoteWebExtensions
370           ? EXTENSION_REMOTE_TYPE
371           : NOT_REMOTE;
373       default:
374         // WebExtensions may set up protocol handlers for protocol names
375         // beginning with ext+.  These may redirect to http(s) pages or to
376         // moz-extension pages.  We can't actually tell here where one of
377         // these pages will end up loading but Talos tests use protocol
378         // handlers that redirect to extension pages that rely on this
379         // behavior so a pageloader frame script is injected correctly.
380         // Protocols that redirect to http(s) will just flip back to a
381         // regular content process after the redirect.
382         if (aURI.scheme.startsWith("ext+")) {
383           return WebExtensionPolicy.useRemoteWebExtensions
384             ? EXTENSION_REMOTE_TYPE
385             : NOT_REMOTE;
386         }
388         // For any other nested URIs, we use the innerURI to determine the
389         // remote type. In theory we should use the innermost URI, but some URIs
390         // have fake inner URIs (e.g. about URIs with inner moz-safe-about) and
391         // if such URIs are wrapped in other nested schemes like view-source:,
392         // we don't want to "skip" past "about:" by going straight to the
393         // innermost URI. Any URIs like this will need to be handled in the
394         // cases above, so we don't still end up using the fake inner URI here.
395         if (aURI instanceof Ci.nsINestedURI) {
396           let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
397           return this.getRemoteTypeForURIObject(
398             innerURI,
399             aMultiProcess,
400             aRemoteSubframes,
401             aPreferredRemoteType,
402             aCurrentUri
403           );
404         }
406         return validatedWebRemoteType(
407           aPreferredRemoteType,
408           aURI,
409           aCurrentUri,
410           aRemoteSubframes
411         );
412     }
413   },
415   getRemoteTypeForPrincipal(
416     aPrincipal,
417     aMultiProcess,
418     aRemoteSubframes,
419     aPreferredRemoteType = DEFAULT_REMOTE_TYPE,
420     aCurrentPrincipal
421   ) {
422     if (!aMultiProcess) {
423       return NOT_REMOTE;
424     }
426     // We can't pick a process based on a system principal or expanded
427     // principal. In fact, we should never end up with one here!
428     if (aPrincipal.isSystemPrincipal || aPrincipal.isExpandedPrincipal) {
429       throw Cr.NS_ERROR_UNEXPECTED;
430     }
432     // Null principals can be loaded in any remote process.
433     if (aPrincipal.isNullPrincipal) {
434       return aPreferredRemoteType == NOT_REMOTE
435         ? DEFAULT_REMOTE_TYPE
436         : aPreferredRemoteType;
437     }
439     // We might care about the currently loaded URI. Pull it out of our current
440     // principal. We never care about the current URI when working with a
441     // non-content principal.
442     let currentURI =
443       aCurrentPrincipal && aCurrentPrincipal.isContentPrincipal
444         ? aCurrentPrincipal.URI
445         : null;
446     return E10SUtils.getRemoteTypeForURIObject(
447       aPrincipal.URI,
448       aMultiProcess,
449       aRemoteSubframes,
450       aPreferredRemoteType,
451       currentURI
452     );
453   },
455   makeInputStream(data) {
456     if (typeof data == "string") {
457       let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
458         Ci.nsISupportsCString
459       );
460       stream.data = data;
461       return stream; // XPConnect will QI this to nsIInputStream for us.
462     }
464     let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
465       Ci.nsISupportsCString
466     );
467     stream.data = data.content;
469     if (data.headers) {
470       let mimeStream = Cc[
471         "@mozilla.org/network/mime-input-stream;1"
472       ].createInstance(Ci.nsIMIMEInputStream);
474       mimeStream.setData(stream);
475       for (let [name, value] of data.headers) {
476         mimeStream.addHeader(name, value);
477       }
478       return mimeStream;
479     }
481     return stream; // XPConnect will QI this to nsIInputStream for us.
482   },
484   /**
485    * Serialize principal data.
486    *
487    * @param {nsIPrincipal} principal The principal to serialize.
488    * @return {String} The base64 encoded principal data.
489    */
490   serializePrincipal(principal) {
491     let serializedPrincipal = null;
493     try {
494       if (principal) {
495         serializedPrincipal = btoa(
496           Services.scriptSecurityManager.principalToJSON(principal)
497         );
498       }
499     } catch (e) {
500       debug(`Failed to serialize principal '${principal}' ${e}`);
501     }
503     return serializedPrincipal;
504   },
506   /**
507    * Deserialize a base64 encoded principal (serialized with
508    * serializePrincipal).
509    *
510    * @param {String} principal_b64 A base64 encoded serialized principal.
511    * @return {nsIPrincipal} A deserialized principal.
512    */
513   deserializePrincipal(principal_b64, fallbackPrincipalCallback = null) {
514     if (!principal_b64) {
515       if (!fallbackPrincipalCallback) {
516         debug(
517           "No principal passed to deserializePrincipal and no fallbackPrincipalCallback"
518         );
519         return null;
520       }
522       return fallbackPrincipalCallback();
523     }
525     try {
526       let principal;
527       let tmpa = atob(principal_b64);
528       // Both the legacy and new JSON representation of principals are stored as base64
529       // The new kind are the only ones that will start with "{" when decoded.
530       // We check here for the new JSON serialized, if it doesn't start with that continue using nsISerializable.
531       // JSONToPrincipal accepts a *non* base64 encoded string and returns a principal or a null.
532       if (tmpa.startsWith("{")) {
533         principal = Services.scriptSecurityManager.JSONToPrincipal(tmpa);
534       } else {
535         principal = serializationHelper.deserializeObject(principal_b64);
536       }
537       principal.QueryInterface(Ci.nsIPrincipal);
538       return principal;
539     } catch (e) {
540       debug(`Failed to deserialize principal_b64 '${principal_b64}' ${e}`);
541     }
542     if (!fallbackPrincipalCallback) {
543       debug(
544         "No principal passed to deserializePrincipal and no fallbackPrincipalCallback"
545       );
546       return null;
547     }
548     return fallbackPrincipalCallback();
549   },
551   shouldLoadURIInBrowser(
552     browser,
553     uri,
554     multiProcess = true,
555     remoteSubframes = false,
556     flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE
557   ) {
558     let currentRemoteType = browser.remoteType;
559     let requiredRemoteType;
560     let uriObject;
561     try {
562       let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE;
563       if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
564         fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
565       }
566       if (flags & Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS) {
567         fixupFlags |= Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS;
568       }
569       uriObject = Services.uriFixup.createFixupURI(uri, fixupFlags);
570       // Note that I had thought that we could set uri = uriObject.spec here, to
571       // save on fixup later on, but that changes behavior and breaks tests.
572       requiredRemoteType = this.getRemoteTypeForURIObject(
573         uriObject,
574         multiProcess,
575         remoteSubframes,
576         currentRemoteType,
577         browser.currentURI
578       );
579     } catch (e) {
580       // createFixupURI throws if it can't create a URI. If that's the case then
581       // we still need to pass down the uri because docshell handles this case.
582       requiredRemoteType = multiProcess ? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
583     }
585     let mustChangeProcess = requiredRemoteType != currentRemoteType;
586     let newFrameloader = false;
587     if (
588       browser.getAttribute("preloadedState") === "consumed" &&
589       uri != "about:newtab"
590     ) {
591       // Leaving about:newtab from a used to be preloaded browser should run the process
592       // selecting algorithm again.
593       mustChangeProcess = true;
594       newFrameloader = true;
595     }
597     return {
598       uriObject,
599       requiredRemoteType,
600       mustChangeProcess,
601       newFrameloader,
602     };
603   },
605   shouldLoadURIInThisProcess(aURI, aRemoteSubframes) {
606     let remoteType = Services.appinfo.remoteType;
607     return (
608       remoteType ==
609       this.getRemoteTypeForURIObject(
610         aURI,
611         /* remote */ true,
612         aRemoteSubframes,
613         remoteType
614       )
615     );
616   },
618   shouldLoadURI(aDocShell, aURI, aHasPostData) {
619     let remoteSubframes = aDocShell.useRemoteSubframes;
621     // Inner frames should always load in the current process
622     // XXX(nika): Handle shouldLoadURI-triggered process switches for remote
623     // subframes! (bug 1548942)
624     if (aDocShell.sameTypeParent) {
625       return true;
626     }
628     let webNav = aDocShell.QueryInterface(Ci.nsIWebNavigation);
629     let sessionHistory = webNav.sessionHistory;
630     if (
631       !aHasPostData &&
632       Services.appinfo.remoteType == WEB_REMOTE_TYPE &&
633       sessionHistory.count == 1 &&
634       webNav.currentURI.spec == "about:newtab"
635     ) {
636       // This is possibly a preloaded browser and we're about to navigate away for
637       // the first time. On the child side there is no way to tell for sure if that
638       // is the case, so let's redirect this request to the parent to decide if a new
639       // process is needed. But we don't currently properly handle POST data in
640       // redirects (bug 1457520), so if there is POST data, don't return false here.
641       return false;
642     }
644     // If we are performing HTTP response process selection, and are loading an
645     // HTTP URI, we can start the load in the current process, and then perform
646     // the switch later-on using the RedirectProcessChooser mechanism.
647     //
648     // We should never be sending a POST request from the parent process to a
649     // http(s) uri, so make sure we switch if we're currently in that process.
650     if (
651       useHttpResponseProcessSelection &&
652       (aURI.scheme == "http" || aURI.scheme == "https") &&
653       Services.appinfo.remoteType != NOT_REMOTE
654     ) {
655       return true;
656     }
658     // If we are in a Large-Allocation process, and it wouldn't be content visible
659     // to change processes, we want to load into a new process so that we can throw
660     // this one out. We don't want to move into a new process if we have post data,
661     // because we would accidentally throw out that data.
662     if (
663       !aHasPostData &&
664       Services.appinfo.remoteType == LARGE_ALLOCATION_REMOTE_TYPE &&
665       !aDocShell.awaitingLargeAlloc &&
666       aDocShell.isOnlyToplevelInTabGroup
667     ) {
668       return false;
669     }
671     // Allow history load if loaded in this process before.
672     let requestedIndex = sessionHistory.legacySHistory.requestedIndex;
673     if (requestedIndex >= 0) {
674       if (
675         sessionHistory.legacySHistory.getEntryAtIndex(requestedIndex)
676           .loadedInThisProcess
677       ) {
678         return true;
679       }
681       // If not originally loaded in this process allow it if the URI would
682       // normally be allowed to load in this process by default.
683       let remoteType = Services.appinfo.remoteType;
684       return (
685         remoteType ==
686         this.getRemoteTypeForURIObject(
687           aURI,
688           true,
689           remoteSubframes,
690           remoteType,
691           webNav.currentURI
692         )
693       );
694     }
696     // If the URI can be loaded in the current process then continue
697     return this.shouldLoadURIInThisProcess(aURI, remoteSubframes);
698   },
700   redirectLoad(
701     aDocShell,
702     aURI,
703     aReferrerInfo,
704     aTriggeringPrincipal,
705     aFreshProcess,
706     aFlags,
707     aCsp
708   ) {
709     // Retarget the load to the correct process
710     let messageManager = aDocShell.messageManager;
711     let sessionHistory = aDocShell.QueryInterface(Ci.nsIWebNavigation)
712       .sessionHistory;
714     messageManager.sendAsyncMessage("Browser:LoadURI", {
715       loadOptions: {
716         uri: aURI.spec,
717         flags: aFlags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
718         referrerInfo: this.serializeReferrerInfo(aReferrerInfo),
719         triggeringPrincipal: this.serializePrincipal(
720           aTriggeringPrincipal ||
721             Services.scriptSecurityManager.createNullPrincipal({})
722         ),
723         csp: aCsp ? this.serializeCSP(aCsp) : null,
724         reloadInFreshProcess: !!aFreshProcess,
725       },
726       historyIndex: sessionHistory.legacySHistory.requestedIndex,
727     });
728     return false;
729   },
731   wrapHandlingUserInput(aWindow, aIsHandling, aCallback) {
732     var handlingUserInput;
733     try {
734       handlingUserInput = aWindow.windowUtils.setHandlingUserInput(aIsHandling);
735       aCallback();
736     } finally {
737       handlingUserInput.destruct();
738     }
739   },
741   /**
742    * Serialize referrerInfo.
743    *
744    * @param {nsIReferrerInfo} The referrerInfo to serialize.
745    * @return {String} The base64 encoded referrerInfo.
746    */
747   serializeReferrerInfo(referrerInfo) {
748     let serialized = null;
749     if (referrerInfo) {
750       try {
751         serialized = serializationHelper.serializeToString(referrerInfo);
752       } catch (e) {
753         debug(`Failed to serialize referrerInfo '${referrerInfo}' ${e}`);
754       }
755     }
756     return serialized;
757   },
758   /**
759    * Deserialize a base64 encoded referrerInfo
760    *
761    * @param {String} referrerInfo_b64 A base64 encoded serialized referrerInfo.
762    * @return {nsIReferrerInfo} A deserialized referrerInfo.
763    */
764   deserializeReferrerInfo(referrerInfo_b64) {
765     let deserialized = null;
766     if (referrerInfo_b64) {
767       try {
768         deserialized = serializationHelper.deserializeObject(referrerInfo_b64);
769         deserialized.QueryInterface(Ci.nsIReferrerInfo);
770       } catch (e) {
771         debug(
772           `Failed to deserialize referrerInfo_b64 '${referrerInfo_b64}' ${e}`
773         );
774       }
775     }
776     return deserialized;
777   },
779   /**
780    * Returns the pids for a remote browser and its remote subframes.
781    */
782   getBrowserPids(aBrowser, aRemoteSubframes) {
783     if (!aBrowser.isRemoteBrowser || !aBrowser.frameLoader) {
784       return [];
785     }
786     let tabPid = aBrowser.frameLoader.remoteTab.osPid;
787     let pids = new Set();
788     if (aRemoteSubframes) {
789       let stack = [aBrowser.browsingContext];
790       while (stack.length) {
791         let bc = stack.pop();
792         stack.push(...bc.getChildren());
793         if (bc.currentWindowGlobal) {
794           let pid = bc.currentWindowGlobal.osPid;
795           if (pid != tabPid) {
796             pids.add(pid);
797           }
798         }
799       }
800     }
801     return [tabPid, ...pids];
802   },
804   /**
805    * The suffix after a `=` in a remoteType is dynamic, and used to control the
806    * process pool to use. The C++ version of this method is mozilla::dom::RemoteTypePrefix().
807    */
808   remoteTypePrefix(aRemoteType) {
809     return aRemoteType.split("=")[0];
810   },
812   /**
813    * There are various types of remote types that are for web content processes, but
814    * they all start with "web". The C++ version of this method is
815    * mozilla::dom::IsWebRemoteType().
816    */
817   isWebRemoteType(aRemoteType) {
818     return aRemoteType.startsWith(WEB_REMOTE_TYPE);
819   },
822 XPCOMUtils.defineLazyGetter(
823   E10SUtils,
824   "SERIALIZED_SYSTEMPRINCIPAL",
825   function() {
826     return E10SUtils.serializePrincipal(
827       Services.scriptSecurityManager.getSystemPrincipal()
828     );
829   }