forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			793 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			793 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
 | |
| import { BrowserUtils } from "resource://gre/modules/BrowserUtils.sys.mjs";
 | |
| import { PrivateBrowsingUtils } from "resource://gre/modules/PrivateBrowsingUtils.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   AboutNewTab: "resource:///modules/AboutNewTab.sys.mjs",
 | |
|   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
 | |
| });
 | |
| 
 | |
| ChromeUtils.defineLazyGetter(lazy, "ReferrerInfo", () =>
 | |
|   Components.Constructor(
 | |
|     "@mozilla.org/referrer-info;1",
 | |
|     "nsIReferrerInfo",
 | |
|     "init"
 | |
|   )
 | |
| );
 | |
| 
 | |
| function saveLink(window, url, params) {
 | |
|   if ("isContentWindowPrivate" in params) {
 | |
|     window.saveURL(
 | |
|       url,
 | |
|       null,
 | |
|       null,
 | |
|       null,
 | |
|       true,
 | |
|       true,
 | |
|       params.referrerInfo,
 | |
|       null,
 | |
|       null,
 | |
|       params.isContentWindowPrivate,
 | |
|       params.originPrincipal
 | |
|     );
 | |
|   } else {
 | |
|     if (!params.initiatingDoc) {
 | |
|       console.error(
 | |
|         "openUILink/openLinkIn was called with " +
 | |
|           "where == 'save' but without initiatingDoc.  See bug 814264."
 | |
|       );
 | |
|       return;
 | |
|     }
 | |
|     window.saveURL(
 | |
|       url,
 | |
|       null,
 | |
|       null,
 | |
|       null,
 | |
|       true,
 | |
|       true,
 | |
|       params.referrerInfo,
 | |
|       null,
 | |
|       params.initiatingDoc
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| function openInWindow(url, params, sourceWindow) {
 | |
|   let {
 | |
|     referrerInfo,
 | |
|     forceNonPrivate,
 | |
|     triggeringRemoteType,
 | |
|     forceAllowDataURI,
 | |
|     globalHistoryOptions,
 | |
|     allowThirdPartyFixup,
 | |
|     userContextId,
 | |
|     postData,
 | |
|     originPrincipal,
 | |
|     originStoragePrincipal,
 | |
|     triggeringPrincipal,
 | |
|     csp,
 | |
|     resolveOnContentBrowserCreated,
 | |
|   } = params;
 | |
|   let features = "chrome,dialog=no,all";
 | |
|   if (params.private) {
 | |
|     features += ",private";
 | |
|     // To prevent regular browsing data from leaking to private browsing sites,
 | |
|     // strip the referrer when opening a new private window. (See Bug: 1409226)
 | |
|     referrerInfo = new lazy.ReferrerInfo(
 | |
|       referrerInfo.referrerPolicy,
 | |
|       false,
 | |
|       referrerInfo.originalReferrer
 | |
|     );
 | |
|   } else if (forceNonPrivate) {
 | |
|     features += ",non-private";
 | |
|   }
 | |
| 
 | |
|   // This propagates to window.arguments.
 | |
|   var sa = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
 | |
| 
 | |
|   var wuri = Cc["@mozilla.org/supports-string;1"].createInstance(
 | |
|     Ci.nsISupportsString
 | |
|   );
 | |
|   wuri.data = url;
 | |
| 
 | |
|   let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
 | |
|     Ci.nsIWritablePropertyBag2
 | |
|   );
 | |
|   if (triggeringRemoteType) {
 | |
|     extraOptions.setPropertyAsACString(
 | |
|       "triggeringRemoteType",
 | |
|       triggeringRemoteType
 | |
|     );
 | |
|   }
 | |
|   if (params.hasValidUserGestureActivation !== undefined) {
 | |
|     extraOptions.setPropertyAsBool(
 | |
|       "hasValidUserGestureActivation",
 | |
|       params.hasValidUserGestureActivation
 | |
|     );
 | |
|   }
 | |
|   if (forceAllowDataURI) {
 | |
|     extraOptions.setPropertyAsBool("forceAllowDataURI", true);
 | |
|   }
 | |
|   if (params.fromExternal !== undefined) {
 | |
|     extraOptions.setPropertyAsBool("fromExternal", params.fromExternal);
 | |
|   }
 | |
|   if (globalHistoryOptions?.triggeringSponsoredURL) {
 | |
|     extraOptions.setPropertyAsACString(
 | |
|       "triggeringSponsoredURL",
 | |
|       globalHistoryOptions.triggeringSponsoredURL
 | |
|     );
 | |
|     if (globalHistoryOptions.triggeringSponsoredURLVisitTimeMS) {
 | |
|       extraOptions.setPropertyAsUint64(
 | |
|         "triggeringSponsoredURLVisitTimeMS",
 | |
|         globalHistoryOptions.triggeringSponsoredURLVisitTimeMS
 | |
|       );
 | |
|     }
 | |
|   }
 | |
|   if (params.wasSchemelessInput !== undefined) {
 | |
|     extraOptions.setPropertyAsBool(
 | |
|       "wasSchemelessInput",
 | |
|       params.wasSchemelessInput
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   var allowThirdPartyFixupSupports = Cc[
 | |
|     "@mozilla.org/supports-PRBool;1"
 | |
|   ].createInstance(Ci.nsISupportsPRBool);
 | |
|   allowThirdPartyFixupSupports.data = allowThirdPartyFixup;
 | |
| 
 | |
|   var userContextIdSupports = Cc[
 | |
|     "@mozilla.org/supports-PRUint32;1"
 | |
|   ].createInstance(Ci.nsISupportsPRUint32);
 | |
|   userContextIdSupports.data = userContextId;
 | |
| 
 | |
|   sa.appendElement(wuri);
 | |
|   sa.appendElement(extraOptions);
 | |
|   sa.appendElement(referrerInfo);
 | |
|   sa.appendElement(postData);
 | |
|   sa.appendElement(allowThirdPartyFixupSupports);
 | |
|   sa.appendElement(userContextIdSupports);
 | |
|   sa.appendElement(originPrincipal);
 | |
|   sa.appendElement(originStoragePrincipal);
 | |
|   sa.appendElement(triggeringPrincipal);
 | |
|   sa.appendElement(null); // allowInheritPrincipal
 | |
|   sa.appendElement(csp);
 | |
| 
 | |
|   let win;
 | |
| 
 | |
|   // Returns a promise that will be resolved when the new window's startup is finished.
 | |
|   function waitForWindowStartup() {
 | |
|     return new Promise(resolve => {
 | |
|       const delayedStartupObserver = aSubject => {
 | |
|         if (aSubject == win) {
 | |
|           Services.obs.removeObserver(
 | |
|             delayedStartupObserver,
 | |
|             "browser-delayed-startup-finished"
 | |
|           );
 | |
|           resolve();
 | |
|         }
 | |
|       };
 | |
|       Services.obs.addObserver(
 | |
|         delayedStartupObserver,
 | |
|         "browser-delayed-startup-finished"
 | |
|       );
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   if (params.frameID != undefined && sourceWindow) {
 | |
|     // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
 | |
|     // event if it contains the expected frameID params.
 | |
|     // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
 | |
|     // opening a new window using the keyboard shortcut).
 | |
|     const sourceTabBrowser = sourceWindow.gBrowser.selectedBrowser;
 | |
|     waitForWindowStartup().then(() => {
 | |
|       Services.obs.notifyObservers(
 | |
|         {
 | |
|           wrappedJSObject: {
 | |
|             url,
 | |
|             createdTabBrowser: win.gBrowser.selectedBrowser,
 | |
|             sourceTabBrowser,
 | |
|             sourceFrameID: params.frameID,
 | |
|           },
 | |
|         },
 | |
|         "webNavigation-createdNavigationTarget"
 | |
|       );
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   if (resolveOnContentBrowserCreated) {
 | |
|     waitForWindowStartup().then(() =>
 | |
|       resolveOnContentBrowserCreated(win.gBrowser.selectedBrowser)
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   win = Services.ww.openWindow(
 | |
|     sourceWindow,
 | |
|     AppConstants.BROWSER_CHROME_URL,
 | |
|     null,
 | |
|     features,
 | |
|     sa
 | |
|   );
 | |
| }
 | |
| 
 | |
| function openInCurrentTab(targetBrowser, url, uriObj, params) {
 | |
|   let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
 | |
| 
 | |
|   if (params.allowThirdPartyFixup) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
 | |
|   }
 | |
|   // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL isn't supported for javascript URIs,
 | |
|   // i.e. it causes them not to load at all. Callers should strip
 | |
|   // "javascript:" from pasted strings to prevent blank tabs
 | |
|   if (!params.allowInheritPrincipal) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
 | |
|   }
 | |
| 
 | |
|   if (params.allowPopups) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_POPUPS;
 | |
|   }
 | |
|   if (params.indicateErrorPageLoad) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
 | |
|   }
 | |
|   if (params.forceAllowDataURI) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FORCE_ALLOW_DATA_URI;
 | |
|   }
 | |
|   if (params.fromExternal) {
 | |
|     flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
 | |
|   }
 | |
| 
 | |
|   let { URI_INHERITS_SECURITY_CONTEXT } = Ci.nsIProtocolHandler;
 | |
|   if (
 | |
|     params.forceAboutBlankViewerInCurrent &&
 | |
|     (!uriObj ||
 | |
|       Services.io.getDynamicProtocolFlags(uriObj) &
 | |
|         URI_INHERITS_SECURITY_CONTEXT)
 | |
|   ) {
 | |
|     // Unless we know for sure we're not inheriting principals,
 | |
|     // force the about:blank viewer to have the right principal:
 | |
|     targetBrowser.createAboutBlankDocumentViewer(
 | |
|       params.originPrincipal,
 | |
|       params.originStoragePrincipal
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   let {
 | |
|     triggeringPrincipal,
 | |
|     csp,
 | |
|     referrerInfo,
 | |
|     postData,
 | |
|     userContextId,
 | |
|     hasValidUserGestureActivation,
 | |
|     globalHistoryOptions,
 | |
|     triggeringRemoteType,
 | |
|     wasSchemelessInput,
 | |
|   } = params;
 | |
| 
 | |
|   targetBrowser.fixupAndLoadURIString(url, {
 | |
|     triggeringPrincipal,
 | |
|     csp,
 | |
|     flags,
 | |
|     referrerInfo,
 | |
|     postData,
 | |
|     userContextId,
 | |
|     hasValidUserGestureActivation,
 | |
|     globalHistoryOptions,
 | |
|     triggeringRemoteType,
 | |
|     wasSchemelessInput,
 | |
|   });
 | |
|   params.resolveOnContentBrowserCreated?.(targetBrowser);
 | |
| }
 | |
| 
 | |
| function updatePrincipals(window, params) {
 | |
|   let { userContextId } = params;
 | |
|   // Teach the principal about the right OA to use, e.g. in case when
 | |
|   // opening a link in a new private window, or in a new container tab.
 | |
|   // Please note we do not have to do that for SystemPrincipals and we
 | |
|   // can not do it for NullPrincipals since NullPrincipals are only
 | |
|   // identical if they actually are the same object (See Bug: 1346759)
 | |
|   function useOAForPrincipal(principal) {
 | |
|     if (principal && principal.isContentPrincipal) {
 | |
|       let privateBrowsingId =
 | |
|         params.private ||
 | |
|         (window && PrivateBrowsingUtils.isWindowPrivate(window));
 | |
|       let attrs = {
 | |
|         userContextId,
 | |
|         privateBrowsingId,
 | |
|         firstPartyDomain: principal.originAttributes.firstPartyDomain,
 | |
|       };
 | |
|       return Services.scriptSecurityManager.principalWithOA(principal, attrs);
 | |
|     }
 | |
|     return principal;
 | |
|   }
 | |
|   params.originPrincipal = useOAForPrincipal(params.originPrincipal);
 | |
|   params.originStoragePrincipal = useOAForPrincipal(
 | |
|     params.originStoragePrincipal
 | |
|   );
 | |
|   params.triggeringPrincipal = useOAForPrincipal(params.triggeringPrincipal);
 | |
| }
 | |
| 
 | |
| export const URILoadingHelper = {
 | |
|   /* openLinkIn opens a URL in a place specified by the parameter |where|.
 | |
|    *
 | |
|    * The params object is the same as for `openLinkIn` and documented below.
 | |
|    *
 | |
|    * @param {String}  where
 | |
|    *   |where| can be:
 | |
|    *    "current"     current tab            (if there aren't any browser windows, then in a new window instead)
 | |
|    *    "tab"         new tab                (if there aren't any browser windows, then in a new window instead)
 | |
|    *    "tabshifted"  same as "tab" but in background if default is to select new tabs, and vice versa
 | |
|    *    "window"      new window
 | |
|    *    "save"        save to disk (with no filename hint!)
 | |
|    *
 | |
|    * @param {Object}  params
 | |
|    *
 | |
|    * Options relating to what tab/window to use and how to open it:
 | |
|    *
 | |
|    * @param {boolean} params.private
 | |
|    *                  Load the URL in a private window.
 | |
|    * @param {boolean} params.forceNonPrivate
 | |
|    *                  Force the load to happen in non-private windows.
 | |
|    * @param {boolean} params.relatedToCurrent
 | |
|    *                  Whether new tabs should go immediately next to the current tab.
 | |
|    * @param {Element} params.targetBrowser
 | |
|    *                  The browser to use for the load. Only used if where == "current".
 | |
|    * @param {boolean} params.inBackground
 | |
|    *                  If explicitly true or false, whether to switch to the tab immediately.
 | |
|    *                  If null, will switch to the tab if `forceForeground` was true. If
 | |
|    *                  neither is passed, will defer to the user preference browser.tabs.loadInBackground.
 | |
|    * @param {boolean} params.forceForeground
 | |
|    *                  Ignore the user preference and load in the foreground.
 | |
|    * @param {boolean} params.allowPinnedTabHostChange
 | |
|    *                  Allow even a pinned tab to change hosts.
 | |
|    * @param {boolean} params.allowPopups
 | |
|    *                  whether the link is allowed to open in a popup window (ie one with no browser
 | |
|    *                  chrome)
 | |
|    * @param {boolean} params.skipTabAnimation
 | |
|    *                  Skip the tab opening animation.
 | |
|    * @param {Element} params.openerBrowser
 | |
|    *                  The browser that started the load.
 | |
|    * @param {boolean} params.avoidBrowserFocus
 | |
|    *                  Don't focus the browser element immediately after starting
 | |
|    *                  the load. Used by the URL bar to avoid leaking user input
 | |
|    *                  into web content, see bug 1641287.
 | |
|    *
 | |
|    * Options relating to the load itself:
 | |
|    *
 | |
|    * @param {boolean} params.allowThirdPartyFixup
 | |
|    *                  Allow transforming the 'url' into a search query.
 | |
|    * @param {nsIInputStream} params.postData
 | |
|    *                  Data to post as part of the request.
 | |
|    * @param {nsIReferrerInfo} params.referrerInfo
 | |
|    *                  Referrer info for the request.
 | |
|    * @param {boolean} params.indicateErrorPageLoad
 | |
|    *                  Whether docshell should throw an exception (i.e. return non-NS_OK)
 | |
|    *                  if the load fails.
 | |
|    * @param {string}  params.charset
 | |
|    *                  Character set to use for the load. Only honoured for tabs.
 | |
|    *                  Legacy argument - do not use.
 | |
|    * @param {string}  params.wasSchemelessInput
 | |
|    *                  Whether the search/URL term was without an explicit scheme.
 | |
|    *
 | |
|    * Options relating to security, whether the load is allowed to happen,
 | |
|    * and what cookie container to use for the load:
 | |
|    *
 | |
|    * @param {boolean} params.forceAllowDataURI
 | |
|    *                  Force allow a data URI to load as a toplevel load.
 | |
|    * @param {number}  params.userContextId
 | |
|    *                  The userContextId (container identifier) to use for the load.
 | |
|    * @param {boolean} params.allowInheritPrincipal
 | |
|    *                  Allow the load to inherit the triggering principal.
 | |
|    * @param {boolean} params.forceAboutBlankViewerInCurrent
 | |
|    *                  Force load an about:blank page first. Only used if
 | |
|    *                  allowInheritPrincipal is passed or no URL was provided.
 | |
|    * @param {nsIPrincipal} params.triggeringPrincipal
 | |
|    *                  Triggering principal to pass to docshell for the load.
 | |
|    * @param {nsIPrincipal} params.originPrincipal
 | |
|    *                  Origin principal to pass to docshell for the load.
 | |
|    * @param {nsIPrincipal} params.originStoragePrincipal
 | |
|    *                  Storage principal to pass to docshell for the load.
 | |
|    * @param {string}  params.triggeringRemoteType
 | |
|    *                  The remoteType triggering this load.
 | |
|    * @param {nsIContentSecurityPolicy} params.csp
 | |
|    *                  The CSP that should apply to the load.
 | |
|    * @param {boolean} params.hasValidUserGestureActivation
 | |
|    *                  Indicates if a valid user gesture caused this load. This
 | |
|    *                  informs e.g. popup blocker decisions.
 | |
|    * @param {boolean} params.fromExternal
 | |
|    *                  Indicates the load was started outside of the browser,
 | |
|    *                  e.g. passed on the commandline or through OS mechanisms.
 | |
|    *
 | |
|    * Options used to track the load elsewhere
 | |
|    *
 | |
|    * @param {function} params.resolveOnNewTabCreated
 | |
|    *                   This callback will be called when a new tab is created.
 | |
|    * @param {function} params.resolveOnContentBrowserCreated
 | |
|    *                   This callback will be called with the content browser once it's created.
 | |
|    * @param {Object}   params.globalHistoryOptions
 | |
|    *                   Used by places to keep track of search related metadata for loads.
 | |
|    * @param {Number}   params.frameID
 | |
|    *                   Used by webextensions for their loads.
 | |
|    *
 | |
|    * Options used for where="save" only:
 | |
|    *
 | |
|    * @param {boolean}  params.isContentWindowPrivate
 | |
|    *                   Save content as coming from a private window.
 | |
|    * @param {Document} params.initiatingDoc
 | |
|    *                   Used to determine where to prompt for a filename.
 | |
|    */
 | |
|   openLinkIn(window, url, where, params) {
 | |
|     if (!where || !url) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let {
 | |
|       allowThirdPartyFixup,
 | |
|       postData,
 | |
|       charset,
 | |
|       relatedToCurrent,
 | |
|       allowInheritPrincipal,
 | |
|       forceAllowDataURI,
 | |
|       forceNonPrivate,
 | |
|       skipTabAnimation,
 | |
|       allowPinnedTabHostChange,
 | |
|       userContextId,
 | |
|       triggeringPrincipal,
 | |
|       originPrincipal,
 | |
|       originStoragePrincipal,
 | |
|       triggeringRemoteType,
 | |
|       csp,
 | |
|       resolveOnNewTabCreated,
 | |
|       resolveOnContentBrowserCreated,
 | |
|       globalHistoryOptions,
 | |
|     } = params;
 | |
| 
 | |
|     // We want to overwrite some things for convenience when passing it to other
 | |
|     // methods. To avoid impacting callers, copy the params.
 | |
|     params = Object.assign({}, params);
 | |
| 
 | |
|     if (!params.referrerInfo) {
 | |
|       params.referrerInfo = new lazy.ReferrerInfo(
 | |
|         Ci.nsIReferrerInfo.EMPTY,
 | |
|         true,
 | |
|         null
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (!triggeringPrincipal) {
 | |
|       throw new Error("Must load with a triggering Principal");
 | |
|     }
 | |
| 
 | |
|     if (where == "save") {
 | |
|       saveLink(window, url, params);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Establish which window we'll load the link in.
 | |
|     let w;
 | |
|     if (where == "current" && params.targetBrowser) {
 | |
|       w = params.targetBrowser.ownerGlobal;
 | |
|     } else {
 | |
|       w = this.getTargetWindow(window, { forceNonPrivate });
 | |
|     }
 | |
|     // We don't want to open tabs in popups, so try to find a non-popup window in
 | |
|     // that case.
 | |
|     if ((where == "tab" || where == "tabshifted") && w && !w.toolbar.visible) {
 | |
|       w = this.getTargetWindow(window, {
 | |
|         skipPopups: true,
 | |
|         forceNonPrivate,
 | |
|       });
 | |
|       relatedToCurrent = false;
 | |
|     }
 | |
| 
 | |
|     updatePrincipals(w, params);
 | |
| 
 | |
|     if (!w || where == "window") {
 | |
|       openInWindow(url, params, w || window);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // We're now committed to loading the link in an existing browser window.
 | |
| 
 | |
|     // Raise the target window before loading the URI, since loading it may
 | |
|     // result in a new frontmost window (e.g. "javascript:window.open('');").
 | |
|     w.focus();
 | |
| 
 | |
|     let targetBrowser;
 | |
|     let loadInBackground;
 | |
|     let uriObj;
 | |
| 
 | |
|     if (where == "current") {
 | |
|       targetBrowser = params.targetBrowser || w.gBrowser.selectedBrowser;
 | |
|       loadInBackground = false;
 | |
|       try {
 | |
|         uriObj = Services.io.newURI(url);
 | |
|       } catch (e) {}
 | |
| 
 | |
|       // In certain tabs, we restrict what if anything may replace the loaded
 | |
|       // page. If a load request bounces off for the currently selected tab,
 | |
|       // we'll open a new tab instead.
 | |
|       let tab = w.gBrowser.getTabForBrowser(targetBrowser);
 | |
|       if (tab == w.FirefoxViewHandler.tab) {
 | |
|         where = "tab";
 | |
|         targetBrowser = null;
 | |
|       } else if (
 | |
|         !allowPinnedTabHostChange &&
 | |
|         tab.pinned &&
 | |
|         url != "about:crashcontent"
 | |
|       ) {
 | |
|         try {
 | |
|           // nsIURI.host can throw for non-nsStandardURL nsIURIs.
 | |
|           if (
 | |
|             !uriObj ||
 | |
|             (!uriObj.schemeIs("javascript") &&
 | |
|               targetBrowser.currentURI.host != uriObj.host)
 | |
|           ) {
 | |
|             where = "tab";
 | |
|             targetBrowser = null;
 | |
|           }
 | |
|         } catch (err) {
 | |
|           where = "tab";
 | |
|           targetBrowser = null;
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       // `where` is "tab" or "tabshifted", so we'll load the link in a new tab.
 | |
|       loadInBackground = params.inBackground;
 | |
|       if (loadInBackground == null) {
 | |
|         loadInBackground = params.forceForeground
 | |
|           ? false
 | |
|           : Services.prefs.getBoolPref("browser.tabs.loadInBackground");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     let focusUrlBar = false;
 | |
| 
 | |
|     switch (where) {
 | |
|       case "current":
 | |
|         openInCurrentTab(targetBrowser, url, uriObj, params);
 | |
| 
 | |
|         // Don't focus the content area if focus is in the address bar and we're
 | |
|         // loading the New Tab page.
 | |
|         focusUrlBar =
 | |
|           w.document.activeElement == w.gURLBar.inputField &&
 | |
|           w.isBlankPageURL(url);
 | |
|         break;
 | |
|       case "tabshifted":
 | |
|         loadInBackground = !loadInBackground;
 | |
|       // fall through
 | |
|       case "tab":
 | |
|         focusUrlBar =
 | |
|           !loadInBackground &&
 | |
|           w.isBlankPageURL(url) &&
 | |
|           !lazy.AboutNewTab.willNotifyUser;
 | |
| 
 | |
|         let tabUsedForLoad = w.gBrowser.addTab(url, {
 | |
|           referrerInfo: params.referrerInfo,
 | |
|           charset,
 | |
|           postData,
 | |
|           inBackground: loadInBackground,
 | |
|           allowThirdPartyFixup,
 | |
|           relatedToCurrent,
 | |
|           skipAnimation: skipTabAnimation,
 | |
|           userContextId,
 | |
|           originPrincipal,
 | |
|           originStoragePrincipal,
 | |
|           triggeringPrincipal,
 | |
|           allowInheritPrincipal,
 | |
|           triggeringRemoteType,
 | |
|           csp,
 | |
|           forceAllowDataURI,
 | |
|           focusUrlBar,
 | |
|           openerBrowser: params.openerBrowser,
 | |
|           fromExternal: params.fromExternal,
 | |
|           globalHistoryOptions,
 | |
|           wasSchemelessInput: params.wasSchemelessInput,
 | |
|         });
 | |
|         targetBrowser = tabUsedForLoad.linkedBrowser;
 | |
| 
 | |
|         resolveOnNewTabCreated?.(targetBrowser);
 | |
|         resolveOnContentBrowserCreated?.(targetBrowser);
 | |
| 
 | |
|         if (params.frameID != undefined && w) {
 | |
|           // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
 | |
|           // event if it contains the expected frameID params.
 | |
|           // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
 | |
|           // opening a new tab using the keyboard shortcut).
 | |
|           Services.obs.notifyObservers(
 | |
|             {
 | |
|               wrappedJSObject: {
 | |
|                 url,
 | |
|                 createdTabBrowser: targetBrowser,
 | |
|                 sourceTabBrowser: w.gBrowser.selectedBrowser,
 | |
|                 sourceFrameID: params.frameID,
 | |
|               },
 | |
|             },
 | |
|             "webNavigation-createdNavigationTarget"
 | |
|           );
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       !params.avoidBrowserFocus &&
 | |
|       !focusUrlBar &&
 | |
|       targetBrowser == w.gBrowser.selectedBrowser
 | |
|     ) {
 | |
|       // Focus the content, but only if the browser used for the load is selected.
 | |
|       targetBrowser.focus();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Finds a browser window suitable for opening a link matching the
 | |
|    * requirements given in the `params` argument. If the current window matches
 | |
|    * the requirements then it is returned otherwise the top-most window that
 | |
|    * matches will be returned.
 | |
|    *
 | |
|    * @param {Window} window - The current window.
 | |
|    * @param {Object} params - Parameters for selecting the window.
 | |
|    * @param {boolean} params.skipPopups - Require a non-popup window.
 | |
|    * @param {boolean} params.forceNonPrivate - Require a non-private window.
 | |
|    * @returns {Window | null} A matching browser window or null if none matched.
 | |
|    */
 | |
|   getTargetWindow(window, { skipPopups, forceNonPrivate } = {}) {
 | |
|     let { top } = window;
 | |
|     // If this is called in a browser window, use that window regardless of
 | |
|     // whether it's the frontmost window, since commands can be executed in
 | |
|     // background windows (bug 626148).
 | |
|     if (
 | |
|       top.document.documentElement.getAttribute("windowtype") ==
 | |
|         "navigator:browser" &&
 | |
|       (!skipPopups || top.toolbar.visible) &&
 | |
|       (!forceNonPrivate || !PrivateBrowsingUtils.isWindowPrivate(top))
 | |
|     ) {
 | |
|       return top;
 | |
|     }
 | |
| 
 | |
|     return lazy.BrowserWindowTracker.getTopWindow({
 | |
|       private: !forceNonPrivate && PrivateBrowsingUtils.isWindowPrivate(window),
 | |
|       allowPopups: !skipPopups,
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * openUILink handles clicks on UI elements that cause URLs to load.
 | |
|    *
 | |
|    * @param {string} url
 | |
|    * @param {Event | Object} event Event or JSON object representing an Event
 | |
|    * @param {Boolean | Object} aIgnoreButton
 | |
|    *                           Boolean or object with the same properties as
 | |
|    *                           accepted by openLinkIn, plus "ignoreButton"
 | |
|    *                           and "ignoreAlt".
 | |
|    * @param {Boolean} aIgnoreAlt
 | |
|    * @param {Boolean} aAllowThirdPartyFixup
 | |
|    * @param {Object} aPostData
 | |
|    * @param {Object} aReferrerInfo
 | |
|    */
 | |
|   openUILink(
 | |
|     window,
 | |
|     url,
 | |
|     event,
 | |
|     aIgnoreButton,
 | |
|     aIgnoreAlt,
 | |
|     aAllowThirdPartyFixup,
 | |
|     aPostData,
 | |
|     aReferrerInfo
 | |
|   ) {
 | |
|     event = BrowserUtils.getRootEvent(event);
 | |
|     let params;
 | |
| 
 | |
|     if (aIgnoreButton && typeof aIgnoreButton == "object") {
 | |
|       params = aIgnoreButton;
 | |
| 
 | |
|       // don't forward "ignoreButton" and "ignoreAlt" to openLinkIn
 | |
|       aIgnoreButton = params.ignoreButton;
 | |
|       aIgnoreAlt = params.ignoreAlt;
 | |
|       delete params.ignoreButton;
 | |
|       delete params.ignoreAlt;
 | |
|     } else {
 | |
|       params = {
 | |
|         allowThirdPartyFixup: aAllowThirdPartyFixup,
 | |
|         postData: aPostData,
 | |
|         referrerInfo: aReferrerInfo,
 | |
|         initiatingDoc: event ? event.target.ownerDocument : null,
 | |
|       };
 | |
|     }
 | |
| 
 | |
|     if (!params.triggeringPrincipal) {
 | |
|       throw new Error(
 | |
|         "Required argument triggeringPrincipal missing within openUILink"
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     let where = BrowserUtils.whereToOpenLink(event, aIgnoreButton, aIgnoreAlt);
 | |
|     params.forceForeground ??= true;
 | |
|     this.openLinkIn(window, url, where, params);
 | |
|   },
 | |
| 
 | |
|   /* openTrustedLinkIn will attempt to open the given URI using the SystemPrincipal
 | |
|    * as the trigeringPrincipal, unless a more specific Principal is provided.
 | |
|    *
 | |
|    * Otherwise, parameters are the same as openLinkIn, but we will set `forceForeground`
 | |
|    * to true.
 | |
|    */
 | |
|   openTrustedLinkIn(window, url, where, params = {}) {
 | |
|     if (!params.triggeringPrincipal) {
 | |
|       params.triggeringPrincipal =
 | |
|         Services.scriptSecurityManager.getSystemPrincipal();
 | |
|     }
 | |
| 
 | |
|     params.forceForeground ??= true;
 | |
|     this.openLinkIn(window, url, where, params);
 | |
|   },
 | |
| 
 | |
|   /* openWebLinkIn will attempt to open the given URI using the NullPrincipal
 | |
|    * as the triggeringPrincipal, unless a more specific Principal is provided.
 | |
|    *
 | |
|    * Otherwise, parameters are the same as openLinkIn, but we will set `forceForeground`
 | |
|    * to true.
 | |
|    */
 | |
|   openWebLinkIn(window, url, where, params = {}) {
 | |
|     if (!params.triggeringPrincipal) {
 | |
|       params.triggeringPrincipal =
 | |
|         Services.scriptSecurityManager.createNullPrincipal({});
 | |
|     }
 | |
|     if (params.triggeringPrincipal.isSystemPrincipal) {
 | |
|       throw new Error(
 | |
|         "System principal should never be passed into openWebLinkIn()"
 | |
|       );
 | |
|     }
 | |
|     params.forceForeground ??= true;
 | |
|     this.openLinkIn(window, url, where, params);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Given a URI, guess which container to use to open it. This is used for external
 | |
|    * openers as a quality of life improvement (e.g. to open a document into the container
 | |
|    * where you are logged in to the service that hosts it).
 | |
|    * matches will be returned.
 | |
|    * For now this can only use currently-open tabs, until history is tagged with the
 | |
|    * container id (https://bugzilla.mozilla.org/show_bug.cgi?id=1283320).
 | |
|    *
 | |
|    * @param {nsIURI} aURI - The URI being opened.
 | |
|    * @returns {number | null} The guessed userContextId, or null if none.
 | |
|    */
 | |
|   guessUserContextId(aURI) {
 | |
|     let host;
 | |
|     try {
 | |
|       host = aURI.host;
 | |
|     } catch (e) {}
 | |
|     if (!host) {
 | |
|       return null;
 | |
|     }
 | |
|     const containerScores = new Map();
 | |
|     let guessedUserContextId = null;
 | |
|     let maxCount = 0;
 | |
|     for (let win of lazy.BrowserWindowTracker.orderedWindows) {
 | |
|       for (let tab of win.gBrowser.visibleTabs) {
 | |
|         let { userContextId } = tab;
 | |
|         let currentURIHost = null;
 | |
|         try {
 | |
|           currentURIHost = tab.linkedBrowser.currentURI.host;
 | |
|         } catch (e) {}
 | |
| 
 | |
|         if (currentURIHost == host) {
 | |
|           let count = (containerScores.get(userContextId) ?? 0) + 1;
 | |
|           containerScores.set(userContextId, count);
 | |
|           if (count > maxCount) {
 | |
|             guessedUserContextId = userContextId;
 | |
|             maxCount = count;
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return guessedUserContextId;
 | |
|   },
 | |
| };
 | 
