Backed out changeset 9df2731a0530 (bug 1867644) for causing mochitests failures in...
[gecko.git] / browser / components / BrowserContentHandler.sys.mjs
blobc4387c8af46d2ea7bb005a5bb0942ca0dd6b2085
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 { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
6 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
8 const lazy = {};
10 ChromeUtils.defineESModuleGetters(lazy, {
11   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
12   FirstStartup: "resource://gre/modules/FirstStartup.sys.mjs",
13   HeadlessShell: "resource:///modules/HeadlessShell.sys.mjs",
14   HomePage: "resource:///modules/HomePage.sys.mjs",
15   LaterRun: "resource:///modules/LaterRun.sys.mjs",
16   NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
17   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
18   SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
19   ShellService: "resource:///modules/ShellService.sys.mjs",
20   SpecialMessageActions:
21     "resource://messaging-system/lib/SpecialMessageActions.sys.mjs",
22   UpdatePing: "resource://gre/modules/UpdatePing.sys.mjs",
23 });
25 XPCOMUtils.defineLazyServiceGetters(lazy, {
26   UpdateManager: ["@mozilla.org/updates/update-manager;1", "nsIUpdateManager"],
27   WinTaskbar: ["@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"],
28   WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
29 });
31 ChromeUtils.defineLazyGetter(lazy, "gSystemPrincipal", () =>
32   Services.scriptSecurityManager.getSystemPrincipal()
35 ChromeUtils.defineLazyGetter(lazy, "gWindowsAlertsService", () => {
36   // We might not have the Windows alerts service: e.g., on Windows 7 and Windows 8.
37   if (!("nsIWindowsAlertsService" in Ci)) {
38     return null;
39   }
40   return Cc["@mozilla.org/system-alerts-service;1"]
41     ?.getService(Ci.nsIAlertsService)
42     ?.QueryInterface(Ci.nsIWindowsAlertsService);
43 });
45 // One-time startup homepage override configurations
46 const ONCE_DOMAINS = ["mozilla.org", "firefox.com"];
47 const ONCE_PREF = "browser.startup.homepage_override.once";
49 // Index of Private Browsing icon in firefox.exe
50 // Must line up with the one in nsNativeAppSupportWin.h.
51 const PRIVATE_BROWSING_ICON_INDEX = 5;
53 function shouldLoadURI(aURI) {
54   if (aURI && !aURI.schemeIs("chrome")) {
55     return true;
56   }
58   dump("*** Preventing external load of chrome: URI into browser window\n");
59   dump("    Use --chrome <uri> instead\n");
60   return false;
63 function resolveURIInternal(aCmdLine, aArgument) {
64   // If using Firefox protocol handler remove it from URI
65   // at this stage. This is before we would otherwise
66   // record telemetry so do that here.
67   if (aArgument.startsWith("firefox:")) {
68     aArgument = aArgument.substring("firefox:".length);
69     Services.telemetry.keyedScalarAdd(
70       "os.environment.launched_to_handle",
71       "firefox",
72       1
73     );
74   }
75   if (aArgument.startsWith("firefox-private:")) {
76     aArgument = aArgument.substring("firefox-private:".length);
77     Services.telemetry.keyedScalarAdd(
78       "os.environment.launched_to_handle",
79       "firefox-private",
80       1
81     );
82   }
83   var uri = aCmdLine.resolveURI(aArgument);
84   var uriFixup = Services.uriFixup;
86   if (!(uri instanceof Ci.nsIFileURL)) {
87     return Services.uriFixup.getFixupURIInfo(
88       aArgument,
89       uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
90     ).preferredURI;
91   }
93   try {
94     if (uri.file.exists()) {
95       return uri;
96     }
97   } catch (e) {
98     console.error(e);
99   }
101   // We have interpreted the argument as a relative file URI, but the file
102   // doesn't exist. Try URI fixup heuristics: see bug 290782.
104   try {
105     uri = Services.uriFixup.getFixupURIInfo(aArgument).preferredURI;
106   } catch (e) {
107     console.error(e);
108   }
110   return uri;
113 let gKiosk = false;
114 let gMajorUpgrade = false;
115 let gFirstRunProfile = false;
116 var gFirstWindow = false;
118 const OVERRIDE_NONE = 0;
119 const OVERRIDE_NEW_PROFILE = 1;
120 const OVERRIDE_NEW_MSTONE = 2;
121 const OVERRIDE_NEW_BUILD_ID = 3;
123  * Determines whether a home page override is needed.
124  * Returns:
125  *  OVERRIDE_NEW_PROFILE if this is the first run with a new profile.
126  *  OVERRIDE_NEW_MSTONE if this is the first run with a build with a different
127  *                      Gecko milestone (i.e. right after an upgrade).
128  *  OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the
129  *                        same Gecko milestone (i.e. after a nightly upgrade).
130  *  OVERRIDE_NONE otherwise.
131  */
132 function needHomepageOverride(prefb) {
133   var savedmstone = prefb.getCharPref(
134     "browser.startup.homepage_override.mstone",
135     ""
136   );
138   if (savedmstone == "ignore") {
139     return OVERRIDE_NONE;
140   }
142   var mstone = Services.appinfo.platformVersion;
144   var savedBuildID = prefb.getCharPref(
145     "browser.startup.homepage_override.buildID",
146     ""
147   );
149   var buildID = Services.appinfo.platformBuildID;
151   if (mstone != savedmstone) {
152     // Bug 462254. Previous releases had a default pref to suppress the EULA
153     // agreement if the platform's installer had already shown one. Now with
154     // about:rights we've removed the EULA stuff and default pref, but we need
155     // a way to make existing profiles retain the default that we removed.
156     if (savedmstone) {
157       prefb.setBoolPref("browser.rights.3.shown", true);
159       // Remember that we saw a major version change.
160       gMajorUpgrade = true;
161     }
163     prefb.setCharPref("browser.startup.homepage_override.mstone", mstone);
164     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
165     return savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE;
166   }
168   if (buildID != savedBuildID) {
169     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
170     return OVERRIDE_NEW_BUILD_ID;
171   }
173   return OVERRIDE_NONE;
177  * Gets the override page for the first run after the application has been
178  * updated.
179  * @param  update
180  *         The nsIUpdate for the update that has been applied.
181  * @param  defaultOverridePage
182  *         The default override page.
183  * @return The override page.
184  */
185 function getPostUpdateOverridePage(update, defaultOverridePage) {
186   update = update.QueryInterface(Ci.nsIWritablePropertyBag);
187   let actions = update.getProperty("actions");
188   // When the update doesn't specify actions fallback to the original behavior
189   // of displaying the default override page.
190   if (!actions) {
191     return defaultOverridePage;
192   }
194   // The existence of silent or the non-existence of showURL in the actions both
195   // mean that an override page should not be displayed.
196   if (actions.includes("silent") || !actions.includes("showURL")) {
197     return "";
198   }
200   // If a policy was set to not allow the update.xml-provided
201   // URL to be used, use the default fallback (which will also
202   // be provided by the policy).
203   if (!Services.policies.isAllowed("postUpdateCustomPage")) {
204     return defaultOverridePage;
205   }
207   return update.getProperty("openURL") || defaultOverridePage;
211  * Open a browser window. If this is the initial launch, this function will
212  * attempt to use the navigator:blank window opened by BrowserGlue.sys.mjs during
213  * early startup.
215  * @param cmdLine
216  *        The nsICommandLine object given to nsICommandLineHandler's handle
217  *        method.
218  *        Used to check if we are processing the command line for the initial launch.
219  * @param triggeringPrincipal
220  *        The nsIPrincipal to use as triggering principal for the page load(s).
221  * @param urlOrUrlList (optional)
222  *        When omitted, the browser window will be opened with the default
223  *        arguments, which will usually load the homepage.
224  *        This can be a JS array of urls provided as strings, each url will be
225  *        loaded in a tab. postData will be ignored in this case.
226  *        This can be a single url to load in the new window, provided as a string.
227  *        postData will be used in this case if provided.
228  * @param postData (optional)
229  *        An nsIInputStream object to use as POST data when loading the provided
230  *        url, or null.
231  * @param forcePrivate (optional)
232  *        Boolean. If set to true, the new window will be a private browsing one.
234  * @returns {ChromeWindow}
235  *          Returns the top level window opened.
236  */
237 function openBrowserWindow(
238   cmdLine,
239   triggeringPrincipal,
240   urlOrUrlList,
241   postData = null,
242   forcePrivate = false
243 ) {
244   const isStartup =
245     cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
247   let args;
248   if (!urlOrUrlList) {
249     // Just pass in the defaultArgs directly. We'll use system principal on the other end.
250     args = [gBrowserContentHandler.getArgs(isStartup)];
251   } else if (Array.isArray(urlOrUrlList)) {
252     // There isn't an explicit way to pass a principal here, so we load multiple URLs
253     // with system principal when we get to actually loading them.
254     if (
255       !triggeringPrincipal ||
256       !triggeringPrincipal.equals(lazy.gSystemPrincipal)
257     ) {
258       throw new Error(
259         "Can't open multiple URLs with something other than system principal."
260       );
261     }
262     // Passing an nsIArray for the url disables the "|"-splitting behavior.
263     let uriArray = Cc["@mozilla.org/array;1"].createInstance(
264       Ci.nsIMutableArray
265     );
266     urlOrUrlList.forEach(function (uri) {
267       var sstring = Cc["@mozilla.org/supports-string;1"].createInstance(
268         Ci.nsISupportsString
269       );
270       sstring.data = uri;
271       uriArray.appendElement(sstring);
272     });
273     args = [uriArray];
274   } else {
275     let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
276       Ci.nsIWritablePropertyBag2
277     );
278     extraOptions.setPropertyAsBool("fromExternal", true);
280     // Always pass at least 3 arguments to avoid the "|"-splitting behavior,
281     // ie. avoid the loadOneOrMoreURIs function.
282     // Also, we need to pass the triggering principal.
283     args = [
284       urlOrUrlList,
285       extraOptions,
286       null, // refererInfo
287       postData,
288       undefined, // allowThirdPartyFixup; this would be `false` but that
289       // needs a conversion. Hopefully bug 1485961 will fix.
290       undefined, // user context id
291       null, // origin principal
292       null, // origin storage principal
293       triggeringPrincipal,
294     ];
295   }
297   if (isStartup) {
298     let win = Services.wm.getMostRecentWindow("navigator:blank");
299     if (win) {
300       // Remove the windowtype of our blank window so that we don't close it
301       // later on when seeing cmdLine.preventDefault is true.
302       win.document.documentElement.removeAttribute("windowtype");
304       if (forcePrivate) {
305         win.docShell.QueryInterface(
306           Ci.nsILoadContext
307         ).usePrivateBrowsing = true;
309         if (
310           AppConstants.platform == "win" &&
311           lazy.NimbusFeatures.majorRelease2022.getVariable(
312             "feltPrivacyWindowSeparation"
313           )
314         ) {
315           lazy.WinTaskbar.setGroupIdForWindow(
316             win,
317             lazy.WinTaskbar.defaultPrivateGroupId
318           );
319           lazy.WindowsUIUtils.setWindowIconFromExe(
320             win,
321             Services.dirsvc.get("XREExeF", Ci.nsIFile).path,
322             // This corresponds to the definitions in
323             // nsNativeAppSupportWin.h
324             PRIVATE_BROWSING_ICON_INDEX
325           );
326         }
327       }
329       let openTime = win.openTime;
330       win.location = AppConstants.BROWSER_CHROME_URL;
331       win.arguments = args; // <-- needs to be a plain JS array here.
333       ChromeUtils.addProfilerMarker("earlyBlankWindowVisible", openTime);
334       lazy.BrowserWindowTracker.registerOpeningWindow(win, forcePrivate);
335       return win;
336     }
337   }
339   // We can't provide arguments to openWindow as a JS array.
340   if (!urlOrUrlList) {
341     // If we have a single string guaranteed to not contain '|' we can simply
342     // wrap it in an nsISupportsString object.
343     let [url] = args;
344     args = Cc["@mozilla.org/supports-string;1"].createInstance(
345       Ci.nsISupportsString
346     );
347     args.data = url;
348   } else {
349     // Otherwise, pass an nsIArray.
350     if (args.length > 1) {
351       let string = Cc["@mozilla.org/supports-string;1"].createInstance(
352         Ci.nsISupportsString
353       );
354       string.data = args[0];
355       args[0] = string;
356     }
357     let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
358     args.forEach(a => {
359       array.appendElement(a);
360     });
361     args = array;
362   }
364   return lazy.BrowserWindowTracker.openWindow({
365     args,
366     features: gBrowserContentHandler.getFeatures(cmdLine),
367     private: forcePrivate,
368   });
371 function openPreferences(cmdLine, extraArgs) {
372   openBrowserWindow(cmdLine, lazy.gSystemPrincipal, "about:preferences");
375 async function doSearch(searchTerm, cmdLine) {
376   // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
377   // preferences, but need nsIBrowserDOMWindow extensions
378   // Open the window immediately as BrowserContentHandler needs to
379   // be handled synchronously. Then load the search URI when the
380   // SearchService has loaded.
381   let win = openBrowserWindow(cmdLine, lazy.gSystemPrincipal, "about:blank");
382   await new Promise(resolve => {
383     Services.obs.addObserver(function observe(subject) {
384       if (subject == win) {
385         Services.obs.removeObserver(
386           observe,
387           "browser-delayed-startup-finished"
388         );
389         resolve();
390       }
391     }, "browser-delayed-startup-finished");
392   });
394   win.BrowserSearch.loadSearchFromCommandLine(
395     searchTerm,
396     lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode ||
397       lazy.PrivateBrowsingUtils.isWindowPrivate(win),
398     lazy.gSystemPrincipal,
399     win.gBrowser.selectedBrowser.csp
400   ).catch(console.error);
403 export function nsBrowserContentHandler() {
404   if (!gBrowserContentHandler) {
405     gBrowserContentHandler = this;
406   }
407   return gBrowserContentHandler;
410 nsBrowserContentHandler.prototype = {
411   /* nsISupports */
412   QueryInterface: ChromeUtils.generateQI([
413     "nsICommandLineHandler",
414     "nsIBrowserHandler",
415     "nsIContentHandler",
416     "nsICommandLineValidator",
417   ]),
419   /* nsICommandLineHandler */
420   handle: function bch_handle(cmdLine) {
421     if (
422       cmdLine.handleFlag("kiosk", false) ||
423       cmdLine.handleFlagWithParam("kiosk-monitor", false)
424     ) {
425       gKiosk = true;
426     }
427     if (cmdLine.handleFlag("disable-pinch", false)) {
428       let defaults = Services.prefs.getDefaultBranch(null);
429       defaults.setBoolPref("apz.allow_zooming", false);
430       Services.prefs.lockPref("apz.allow_zooming");
431       defaults.setCharPref("browser.gesture.pinch.in", "");
432       Services.prefs.lockPref("browser.gesture.pinch.in");
433       defaults.setCharPref("browser.gesture.pinch.in.shift", "");
434       Services.prefs.lockPref("browser.gesture.pinch.in.shift");
435       defaults.setCharPref("browser.gesture.pinch.out", "");
436       Services.prefs.lockPref("browser.gesture.pinch.out");
437       defaults.setCharPref("browser.gesture.pinch.out.shift", "");
438       Services.prefs.lockPref("browser.gesture.pinch.out.shift");
439     }
440     if (cmdLine.handleFlag("browser", false)) {
441       openBrowserWindow(cmdLine, lazy.gSystemPrincipal);
442       cmdLine.preventDefault = true;
443     }
445     var uriparam;
446     try {
447       while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
448         let uri = resolveURIInternal(cmdLine, uriparam);
449         if (!shouldLoadURI(uri)) {
450           continue;
451         }
452         openBrowserWindow(cmdLine, lazy.gSystemPrincipal, uri.spec);
453         cmdLine.preventDefault = true;
454       }
455     } catch (e) {
456       console.error(e);
457     }
459     try {
460       while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) {
461         let uri = resolveURIInternal(cmdLine, uriparam);
462         handURIToExistingBrowser(
463           uri,
464           Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
465           cmdLine,
466           false,
467           lazy.gSystemPrincipal
468         );
469         cmdLine.preventDefault = true;
470       }
471     } catch (e) {
472       console.error(e);
473     }
475     var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
476     if (chromeParam) {
477       // Handle old preference dialog URLs.
478       if (
479         chromeParam == "chrome://browser/content/pref/pref.xul" ||
480         chromeParam == "chrome://browser/content/preferences/preferences.xul"
481       ) {
482         openPreferences(cmdLine);
483         cmdLine.preventDefault = true;
484       } else {
485         try {
486           let resolvedURI = resolveURIInternal(cmdLine, chromeParam);
487           let isLocal = uri => {
488             let localSchemes = new Set(["chrome", "file", "resource"]);
489             if (uri instanceof Ci.nsINestedURI) {
490               uri = uri.QueryInterface(Ci.nsINestedURI).innerMostURI;
491             }
492             return localSchemes.has(uri.scheme);
493           };
494           if (isLocal(resolvedURI)) {
495             // If the URI is local, we are sure it won't wrongly inherit chrome privs
496             let features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
497             // Provide 1 null argument, as openWindow has a different behavior
498             // when the arg count is 0.
499             let argArray = Cc["@mozilla.org/array;1"].createInstance(
500               Ci.nsIMutableArray
501             );
502             argArray.appendElement(null);
503             Services.ww.openWindow(
504               null,
505               resolvedURI.spec,
506               "_blank",
507               features,
508               argArray
509             );
510             cmdLine.preventDefault = true;
511           } else {
512             dump("*** Preventing load of web URI as chrome\n");
513             dump(
514               "    If you're trying to load a webpage, do not pass --chrome.\n"
515             );
516           }
517         } catch (e) {
518           console.error(e);
519         }
520       }
521     }
522     if (cmdLine.handleFlag("preferences", false)) {
523       openPreferences(cmdLine);
524       cmdLine.preventDefault = true;
525     }
526     if (cmdLine.handleFlag("silent", false)) {
527       cmdLine.preventDefault = true;
528     }
530     try {
531       var privateWindowParam = cmdLine.handleFlagWithParam(
532         "private-window",
533         false
534       );
535       // Check for Firefox private browsing protocol handler here.
536       let url = null;
537       let urlFlagIdx = cmdLine.findFlag("url", false);
538       if (urlFlagIdx > -1 && cmdLine.length > 1) {
539         url = cmdLine.getArgument(urlFlagIdx + 1);
540       }
541       if (privateWindowParam || url?.startsWith("firefox-private:")) {
542         let forcePrivate = true;
543         let resolvedURI;
544         if (!lazy.PrivateBrowsingUtils.enabled) {
545           // Load about:privatebrowsing in a normal tab, which will display an error indicating
546           // access to private browsing has been disabled.
547           forcePrivate = false;
548           resolvedURI = Services.io.newURI("about:privatebrowsing");
549         } else if (url?.startsWith("firefox-private:")) {
550           // We can safely remove the flag and parameter now.
551           cmdLine.removeArguments(urlFlagIdx, urlFlagIdx + 1);
552           resolvedURI = resolveURIInternal(cmdLine, url);
553         } else {
554           resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
555         }
556         handURIToExistingBrowser(
557           resolvedURI,
558           Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
559           cmdLine,
560           forcePrivate,
561           lazy.gSystemPrincipal
562         );
563         cmdLine.preventDefault = true;
564       }
565     } catch (e) {
566       if (e.result != Cr.NS_ERROR_INVALID_ARG) {
567         throw e;
568       }
569       // NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param.
570       if (cmdLine.handleFlag("private-window", false)) {
571         openBrowserWindow(
572           cmdLine,
573           lazy.gSystemPrincipal,
574           "about:privatebrowsing",
575           null,
576           lazy.PrivateBrowsingUtils.enabled
577         );
578         cmdLine.preventDefault = true;
579       }
580     }
582     var searchParam = cmdLine.handleFlagWithParam("search", false);
583     if (searchParam) {
584       doSearch(searchParam, cmdLine);
585       cmdLine.preventDefault = true;
586     }
588     // The global PB Service consumes this flag, so only eat it in per-window
589     // PB builds.
590     if (
591       cmdLine.handleFlag("private", false) &&
592       lazy.PrivateBrowsingUtils.enabled
593     ) {
594       lazy.PrivateBrowsingUtils.enterTemporaryAutoStartMode();
595       if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
596         let win = Services.wm.getMostRecentWindow("navigator:blank");
597         if (win) {
598           win.docShell.QueryInterface(
599             Ci.nsILoadContext
600           ).usePrivateBrowsing = true;
601         }
602       }
603     }
604     if (cmdLine.handleFlag("setDefaultBrowser", false)) {
605       // Note that setDefaultBrowser is an async function, but "handle" (the method being executed)
606       // is an implementation of an interface method and changing it to be async would be complicated
607       // and ultimately nothing here needs the result of setDefaultBrowser, so we do not bother doing
608       // an await.
609       lazy.ShellService.setDefaultBrowser(true).catch(e => {
610         console.error("setDefaultBrowser failed:", e);
611       });
612     }
614     if (cmdLine.handleFlag("first-startup", false)) {
615       lazy.FirstStartup.init();
616     }
618     var fileParam = cmdLine.handleFlagWithParam("file", false);
619     if (fileParam) {
620       var file = cmdLine.resolveFile(fileParam);
621       var fileURI = Services.io.newFileURI(file);
622       openBrowserWindow(cmdLine, lazy.gSystemPrincipal, fileURI.spec);
623       cmdLine.preventDefault = true;
624     }
626     if (AppConstants.platform == "win") {
627       // Handle "? searchterm" for Windows Vista start menu integration
628       for (var i = cmdLine.length - 1; i >= 0; --i) {
629         var param = cmdLine.getArgument(i);
630         if (param.match(/^\? /)) {
631           cmdLine.removeArguments(i, i);
632           cmdLine.preventDefault = true;
634           searchParam = param.substr(2);
635           doSearch(searchParam, cmdLine);
636         }
637       }
638     }
639   },
641   get helpInfo() {
642     let info =
643       "  --browser          Open a browser window.\n" +
644       "  --new-window <url> Open <url> in a new window.\n" +
645       "  --new-tab <url>    Open <url> in a new tab.\n" +
646       "  --private-window <url> Open <url> in a new private window.\n";
647     if (AppConstants.platform == "win") {
648       info += "  --preferences      Open Options dialog.\n";
649     } else {
650       info += "  --preferences      Open Preferences dialog.\n";
651     }
652     info +=
653       "  --screenshot [<path>] Save screenshot to <path> or in working directory.\n";
654     info +=
655       "  --window-size width[,height] Width and optionally height of screenshot.\n";
656     info +=
657       "  --search <term>    Search <term> with your default search engine.\n";
658     info += "  --setDefaultBrowser Set this app as the default browser.\n";
659     info +=
660       "  --first-startup    Run post-install actions before opening a new window.\n";
661     info += "  --kiosk            Start the browser in kiosk mode.\n";
662     info +=
663       "  --kiosk-monitor <num> Place kiosk browser window on given monitor.\n";
664     info +=
665       "  --disable-pinch    Disable touch-screen and touch-pad pinch gestures.\n";
666     return info;
667   },
669   /* nsIBrowserHandler */
671   get defaultArgs() {
672     return this.getArgs();
673   },
675   getArgs(isStartup = false) {
676     var prefb = Services.prefs;
678     if (!gFirstWindow) {
679       gFirstWindow = true;
680       if (lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
681         return "about:privatebrowsing";
682       }
683     }
685     var override;
686     var overridePage = "";
687     var additionalPage = "";
688     var willRestoreSession = false;
689     try {
690       // Read the old value of homepage_override.mstone before
691       // needHomepageOverride updates it, so that we can later add it to the
692       // URL if we do end up showing an overridePage. This makes it possible
693       // to have the overridePage's content vary depending on the version we're
694       // upgrading from.
695       let old_mstone = Services.prefs.getCharPref(
696         "browser.startup.homepage_override.mstone",
697         "unknown"
698       );
699       let old_buildId = Services.prefs.getCharPref(
700         "browser.startup.homepage_override.buildID",
701         "unknown"
702       );
703       override = needHomepageOverride(prefb);
704       if (override != OVERRIDE_NONE) {
705         switch (override) {
706           case OVERRIDE_NEW_PROFILE:
707             // New profile.
708             gFirstRunProfile = true;
709             if (lazy.NimbusFeatures.aboutwelcome.getVariable("showModal")) {
710               break;
711             }
712             overridePage = Services.urlFormatter.formatURLPref(
713               "startup.homepage_welcome_url"
714             );
715             additionalPage = Services.urlFormatter.formatURLPref(
716               "startup.homepage_welcome_url.additional"
717             );
718             // Turn on 'later run' pages for new profiles.
719             lazy.LaterRun.enable(lazy.LaterRun.ENABLE_REASON_NEW_PROFILE);
720             break;
721           case OVERRIDE_NEW_MSTONE:
722             // Check whether we will restore a session. If we will, we assume
723             // that this is an "update" session. This does not take crashes
724             // into account because that requires waiting for the session file
725             // to be read. If a crash occurs after updating, before restarting,
726             // we may open the startPage in addition to restoring the session.
727             willRestoreSession =
728               lazy.SessionStartup.isAutomaticRestoreEnabled();
730             overridePage = Services.urlFormatter.formatURLPref(
731               "startup.homepage_override_url"
732             );
733             let update = lazy.UpdateManager.readyUpdate;
734             if (
735               update &&
736               Services.vc.compare(update.appVersion, old_mstone) > 0
737             ) {
738               overridePage = getPostUpdateOverridePage(update, overridePage);
739               // Send the update ping to signal that the update was successful.
740               lazy.UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
741               lazy.LaterRun.enable(lazy.LaterRun.ENABLE_REASON_UPDATE_APPLIED);
742             }
744             overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
745             break;
746           case OVERRIDE_NEW_BUILD_ID:
747             if (lazy.UpdateManager.readyUpdate) {
748               // Send the update ping to signal that the update was successful.
749               lazy.UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
750               lazy.LaterRun.enable(lazy.LaterRun.ENABLE_REASON_UPDATE_APPLIED);
751             }
752             break;
753         }
754       }
755     } catch (ex) {}
757     // formatURLPref might return "about:blank" if getting the pref fails
758     if (overridePage == "about:blank") {
759       overridePage = "";
760     }
762     // Allow showing a one-time startup override if we're not showing one
763     if (isStartup && overridePage == "" && prefb.prefHasUserValue(ONCE_PREF)) {
764       try {
765         // Show if we haven't passed the expiration or there's no expiration
766         const { expire, url } = JSON.parse(
767           Services.urlFormatter.formatURLPref(ONCE_PREF)
768         );
769         if (!(Date.now() > expire)) {
770           // Only set allowed urls as override pages
771           overridePage = url
772             .split("|")
773             .map(val => {
774               try {
775                 return new URL(val);
776               } catch (ex) {
777                 // Invalid URL, so filter out below
778                 console.error("Invalid once url:", ex);
779                 return null;
780               }
781             })
782             .filter(
783               parsed =>
784                 parsed &&
785                 parsed.protocol == "https:" &&
786                 // Only accept exact hostname or subdomain; without port
787                 ONCE_DOMAINS.includes(
788                   Services.eTLD.getBaseDomainFromHost(parsed.host)
789                 )
790             )
791             .join("|");
793           // Be noisy as properly configured urls should be unchanged
794           if (overridePage != url) {
795             console.error(`Mismatched once urls: ${url}`);
796           }
797         }
798       } catch (ex) {
799         // Invalid json pref, so ignore (and clear below)
800         console.error("Invalid once pref:", ex);
801       } finally {
802         prefb.clearUserPref(ONCE_PREF);
803       }
804     }
806     if (!additionalPage) {
807       additionalPage = lazy.LaterRun.getURL() || "";
808     }
810     if (additionalPage && additionalPage != "about:blank") {
811       if (overridePage) {
812         overridePage += "|" + additionalPage;
813       } else {
814         overridePage = additionalPage;
815       }
816     }
818     var startPage = "";
819     try {
820       var choice = prefb.getIntPref("browser.startup.page");
821       if (choice == 1 || choice == 3) {
822         startPage = lazy.HomePage.get();
823       }
824     } catch (e) {
825       console.error(e);
826     }
828     if (startPage == "about:blank") {
829       startPage = "";
830     }
832     let skipStartPage =
833       override == OVERRIDE_NEW_PROFILE &&
834       prefb.getBoolPref("browser.startup.firstrunSkipsHomepage");
835     // Only show the startPage if we're not restoring an update session and are
836     // not set to skip the start page on this profile
837     if (overridePage && startPage && !willRestoreSession && !skipStartPage) {
838       return overridePage + "|" + startPage;
839     }
841     return overridePage || startPage || "about:blank";
842   },
844   mFeatures: null,
846   getFeatures: function bch_features(cmdLine) {
847     if (this.mFeatures === null) {
848       this.mFeatures = "";
850       if (cmdLine) {
851         try {
852           var width = cmdLine.handleFlagWithParam("width", false);
853           var height = cmdLine.handleFlagWithParam("height", false);
854           var left = cmdLine.handleFlagWithParam("left", false);
855           var top = cmdLine.handleFlagWithParam("top", false);
857           if (width) {
858             this.mFeatures += ",width=" + width;
859           }
860           if (height) {
861             this.mFeatures += ",height=" + height;
862           }
863           if (left) {
864             this.mFeatures += ",left=" + left;
865           }
866           if (top) {
867             this.mFeatures += ",top=" + top;
868           }
869         } catch (e) {}
870       }
872       // The global PB Service consumes this flag, so only eat it in per-window
873       // PB builds.
874       if (lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
875         this.mFeatures += ",private";
876       }
878       if (
879         Services.prefs.getBoolPref("browser.suppress_first_window_animation") &&
880         !Services.wm.getMostRecentWindow("navigator:browser")
881       ) {
882         this.mFeatures += ",suppressanimation";
883       }
884     }
886     return this.mFeatures;
887   },
889   get kiosk() {
890     return gKiosk;
891   },
893   get majorUpgrade() {
894     return gMajorUpgrade;
895   },
897   set majorUpgrade(val) {
898     gMajorUpgrade = val;
899   },
901   get firstRunProfile() {
902     return gFirstRunProfile;
903   },
905   set firstRunProfile(val) {
906     gFirstRunProfile = val;
907   },
909   /* nsIContentHandler */
911   handleContent: function bch_handleContent(contentType, context, request) {
912     const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
914     try {
915       var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"].getService(
916         Ci.nsIWebNavigationInfo
917       );
918       if (!webNavInfo.isTypeSupported(contentType)) {
919         throw NS_ERROR_WONT_HANDLE_CONTENT;
920       }
921     } catch (e) {
922       throw NS_ERROR_WONT_HANDLE_CONTENT;
923     }
925     request.QueryInterface(Ci.nsIChannel);
926     handURIToExistingBrowser(
927       request.URI,
928       Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
929       null,
930       false,
931       request.loadInfo.triggeringPrincipal
932     );
933     request.cancel(Cr.NS_BINDING_ABORTED);
934   },
936   /* nsICommandLineValidator */
937   validate: function bch_validate(cmdLine) {
938     var urlFlagIdx = cmdLine.findFlag("url", false);
939     if (
940       urlFlagIdx > -1 &&
941       cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_EXPLICIT
942     ) {
943       var urlParam = cmdLine.getArgument(urlFlagIdx + 1);
944       if (
945         cmdLine.length != urlFlagIdx + 2 ||
946         /firefoxurl(-[a-f0-9]+)?:/i.test(urlParam)
947       ) {
948         throw Components.Exception("", Cr.NS_ERROR_ABORT);
949       }
950     }
951   },
953 var gBrowserContentHandler = new nsBrowserContentHandler();
955 function handURIToExistingBrowser(
956   uri,
957   location,
958   cmdLine,
959   forcePrivate,
960   triggeringPrincipal
961 ) {
962   if (!shouldLoadURI(uri)) {
963     return;
964   }
966   let openInWindow = ({ browserDOMWindow }) => {
967     browserDOMWindow.openURI(
968       uri,
969       null,
970       location,
971       Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
972       triggeringPrincipal
973     );
974   };
976   // Unless using a private window is forced, open external links in private
977   // windows only if we're in perma-private mode.
978   let allowPrivate =
979     forcePrivate || lazy.PrivateBrowsingUtils.permanentPrivateBrowsing;
980   let navWin = lazy.BrowserWindowTracker.getTopWindow({
981     private: allowPrivate,
982   });
984   if (navWin) {
985     openInWindow(navWin);
986     return;
987   }
989   let pending = lazy.BrowserWindowTracker.getPendingWindow({
990     private: allowPrivate,
991   });
992   if (pending) {
993     // Note that we cannot make this function async as some callers rely on
994     // catching exceptions it can throw in some cases and some of those callers
995     // cannot be made async.
996     pending.then(openInWindow);
997     return;
998   }
1000   // if we couldn't load it in an existing window, open a new one
1001   openBrowserWindow(cmdLine, triggeringPrincipal, uri.spec, null, forcePrivate);
1005  * If given URI is a file type or a protocol, record telemetry that
1006  * Firefox was invoked or launched (if `isLaunch` is truth-y).  If the
1007  * file type or protocol is not registered by default, record it as
1008  * ".<other extension>" or "<other protocol>".
1010  * @param uri
1011  *        The URI Firefox was asked to handle.
1012  * @param isLaunch
1013  *        truth-y if Firefox was launched/started rather than running and invoked.
1014  */
1015 function maybeRecordToHandleTelemetry(uri, isLaunch) {
1016   let scalar = isLaunch
1017     ? "os.environment.launched_to_handle"
1018     : "os.environment.invoked_to_handle";
1020   if (uri instanceof Ci.nsIFileURL) {
1021     let extension = "." + uri.fileExtension.toLowerCase();
1022     // Keep synchronized with https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/shared.nsh
1023     // and https://searchfox.org/mozilla-central/source/browser/installer/windows/msix/AppxManifest.xml.in.
1024     let registeredExtensions = new Set([
1025       ".avif",
1026       ".htm",
1027       ".html",
1028       ".pdf",
1029       ".shtml",
1030       ".xht",
1031       ".xhtml",
1032       ".svg",
1033       ".webp",
1034     ]);
1035     if (registeredExtensions.has(extension)) {
1036       Services.telemetry.keyedScalarAdd(scalar, extension, 1);
1037     } else {
1038       Services.telemetry.keyedScalarAdd(scalar, ".<other extension>", 1);
1039     }
1040   } else if (uri) {
1041     let scheme = uri.scheme.toLowerCase();
1042     let registeredSchemes = new Set(["about", "http", "https", "mailto"]);
1043     if (registeredSchemes.has(scheme)) {
1044       Services.telemetry.keyedScalarAdd(scalar, scheme, 1);
1045     } else {
1046       Services.telemetry.keyedScalarAdd(scalar, "<other protocol>", 1);
1047     }
1048   }
1051 export function nsDefaultCommandLineHandler() {}
1053 nsDefaultCommandLineHandler.prototype = {
1054   /* nsISupports */
1055   QueryInterface: ChromeUtils.generateQI(["nsICommandLineHandler"]),
1057   _haveProfile: false,
1059   /* nsICommandLineHandler */
1060   handle: function dch_handle(cmdLine) {
1061     var urilist = [];
1063     if (cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
1064       // Since the purpose of this is to record early in startup,
1065       // only record on launches, not already-running invocations.
1066       Services.telemetry.setEventRecordingEnabled("telemetry", true);
1067       Glean.fogValidation.validateEarlyEvent.record();
1068     }
1070     if (AppConstants.platform == "win") {
1071       // Windows itself does disk I/O when the notification service is
1072       // initialized, so make sure that is lazy.
1073       while (true) {
1074         let tag = cmdLine.handleFlagWithParam("notification-windowsTag", false);
1075         if (!tag) {
1076           break;
1077         }
1079         // All notifications will invoke Firefox with an action.  Prior to Bug 1805514,
1080         // this data was extracted from the Windows toast object directly (keyed by the
1081         // notification ID) and not passed over the command line.  This is acceptable
1082         // because the data passed is chrome-controlled, but if we implement the `actions`
1083         // part of the DOM Web Notifications API, this will no longer be true:
1084         // content-controlled data might transit over the command line.  This could lead
1085         // to escaping bugs and overflows.  In the future, we intend to avoid any such
1086         // issue by once again extracting all such data from the Windows toast object.
1087         let notificationData = cmdLine.handleFlagWithParam(
1088           "notification-windowsAction",
1089           false
1090         );
1091         if (!notificationData) {
1092           break;
1093         }
1095         let alertService = lazy.gWindowsAlertsService;
1096         if (!alertService) {
1097           console.error("Windows alert service not available.");
1098           break;
1099         }
1101         async function handleNotification() {
1102           let { tagWasHandled } = await alertService.handleWindowsTag(tag);
1104           // If the tag was not handled via callback, then the notification was
1105           // from a prior instance of the application and we need to handle
1106           // fallback behavior.
1107           if (!tagWasHandled) {
1108             console.info(
1109               `Completing Windows notification (tag=${JSON.stringify(
1110                 tag
1111               )}, notificationData=${notificationData})`
1112             );
1113             try {
1114               notificationData = JSON.parse(notificationData);
1115             } catch (e) {
1116               console.error(
1117                 `Completing Windows notification (tag=${JSON.stringify(
1118                   tag
1119                 )}, failed to parse (notificationData=${notificationData})`
1120               );
1121             }
1122           }
1124           // This is awkward: the relaunch data set by the caller is _wrapped_
1125           // into a compound object that includes additional notification data,
1126           // and everything is exchanged as strings.  Unwrap and parse here.
1127           let opaqueRelaunchData = null;
1128           if (notificationData?.opaqueRelaunchData) {
1129             try {
1130               opaqueRelaunchData = JSON.parse(
1131                 notificationData.opaqueRelaunchData
1132               );
1133             } catch (e) {
1134               console.error(
1135                 `Completing Windows notification (tag=${JSON.stringify(
1136                   tag
1137                 )}, failed to parse (opaqueRelaunchData=${
1138                   notificationData.opaqueRelaunchData
1139                 })`
1140               );
1141             }
1142           }
1144           if (notificationData?.privilegedName) {
1145             Services.telemetry.setEventRecordingEnabled(
1146               "browser.launched_to_handle",
1147               true
1148             );
1149             Glean.browserLaunchedToHandle.systemNotification.record({
1150               name: notificationData.privilegedName,
1151             });
1152           }
1154           // If we have an action in the notification data, this will be the
1155           // window to perform the action in.
1156           let winForAction;
1158           if (notificationData?.launchUrl && !opaqueRelaunchData) {
1159             // Unprivileged Web Notifications contain a launch URL and are handled
1160             // slightly differently than privileged notifications with actions.
1161             let uri = resolveURIInternal(cmdLine, notificationData.launchUrl);
1162             if (cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
1163               // Try to find an existing window and load our URI into the current
1164               // tab, new tab, or new window as prefs determine.
1165               try {
1166                 handURIToExistingBrowser(
1167                   uri,
1168                   Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
1169                   cmdLine,
1170                   false,
1171                   lazy.gSystemPrincipal
1172                 );
1173                 return;
1174               } catch (e) {}
1175             }
1177             if (shouldLoadURI(uri)) {
1178               openBrowserWindow(cmdLine, lazy.gSystemPrincipal, [uri.spec]);
1179             }
1180           } else if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
1181             // No URL provided, but notification was interacted with while the
1182             // application was closed. Fall back to opening the browser without url.
1183             winForAction = openBrowserWindow(cmdLine, lazy.gSystemPrincipal);
1184             await new Promise(resolve => {
1185               Services.obs.addObserver(function observe(subject) {
1186                 if (subject == winForAction) {
1187                   Services.obs.removeObserver(
1188                     observe,
1189                     "browser-delayed-startup-finished"
1190                   );
1191                   resolve();
1192                 }
1193               }, "browser-delayed-startup-finished");
1194             });
1195           } else {
1196             // Relaunch in private windows only if we're in perma-private mode.
1197             let allowPrivate =
1198               lazy.PrivateBrowsingUtils.permanentPrivateBrowsing;
1199             winForAction = lazy.BrowserWindowTracker.getTopWindow({
1200               private: allowPrivate,
1201             });
1202           }
1204           if (opaqueRelaunchData && winForAction) {
1205             // Without dispatch, `OPEN_URL` with `where: "tab"` does not work on relaunch.
1206             Services.tm.dispatchToMainThread(() => {
1207               lazy.SpecialMessageActions.handleAction(
1208                 opaqueRelaunchData,
1209                 winForAction.gBrowser
1210               );
1211             });
1212           }
1213         }
1215         // Notification handling occurs asynchronously to prevent blocking the
1216         // main thread. As a result we won't have the information we need to open
1217         // a new tab in the case of notification fallback handling before
1218         // returning. We call `enterLastWindowClosingSurvivalArea` to prevent
1219         // the browser from exiting in case early blank window is pref'd off.
1220         if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
1221           Services.startup.enterLastWindowClosingSurvivalArea();
1222         }
1223         handleNotification()
1224           .catch(e => {
1225             console.error(
1226               `Error handling Windows notification with tag '${tag}':`,
1227               e
1228             );
1229           })
1230           .finally(() => {
1231             if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
1232               Services.startup.exitLastWindowClosingSurvivalArea();
1233             }
1234           });
1236         return;
1237       }
1238     }
1240     if (
1241       cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1242       Services.startup.wasSilentlyStarted
1243     ) {
1244       // If we are starting up in silent mode, don't open a window. We also need
1245       // to make sure that the application doesn't immediately exit, so stay in
1246       // a LastWindowClosingSurvivalArea until a window opens.
1247       Services.startup.enterLastWindowClosingSurvivalArea();
1248       Services.obs.addObserver(function windowOpenObserver() {
1249         Services.startup.exitLastWindowClosingSurvivalArea();
1250         Services.obs.removeObserver(windowOpenObserver, "domwindowopened");
1251       }, "domwindowopened");
1252       return;
1253     }
1255     if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
1256       // Handle the case where we don't have a profile selected yet (e.g. the
1257       // Profile Manager is displayed).
1258       // On Windows, we will crash if we open an url and then select a profile.
1259       // On macOS, if we open an url we don't experience a crash but a broken
1260       // window is opened.
1261       // To prevent this handle all url command line flags and set the
1262       // command line's preventDefault to true to prevent the display of the ui.
1263       // The initial command line will be retained when nsAppRunner calls
1264       // LaunchChild though urls launched after the initial launch will be lost.
1265       if (!this._haveProfile) {
1266         try {
1267           // This will throw when a profile has not been selected.
1268           Services.dirsvc.get("ProfD", Ci.nsIFile);
1269           this._haveProfile = true;
1270         } catch (e) {
1271           // eslint-disable-next-line no-empty
1272           while ((ar = cmdLine.handleFlagWithParam("url", false))) {}
1273           cmdLine.preventDefault = true;
1274         }
1275       }
1276     }
1278     // `-osint` and handling registered file types and protocols is Windows-only.
1279     let launchedWithArg_osint =
1280       AppConstants.platform == "win" && cmdLine.findFlag("osint", false) == 0;
1281     if (launchedWithArg_osint) {
1282       cmdLine.handleFlag("osint", false);
1283     }
1285     try {
1286       var ar;
1287       while ((ar = cmdLine.handleFlagWithParam("url", false))) {
1288         var uri = resolveURIInternal(cmdLine, ar);
1289         urilist.push(uri);
1291         if (launchedWithArg_osint) {
1292           launchedWithArg_osint = false;
1294           // We use the resolved URI here, even though it can produce
1295           // surprising results where-by `-osint -url test.pdf` resolves to
1296           // a query with search parameter "test.pdf".  But that shouldn't
1297           // happen when Firefox is launched by Windows itself: files should
1298           // exist and be resolved to file URLs.
1299           const isLaunch =
1300             cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
1302           maybeRecordToHandleTelemetry(uri, isLaunch);
1303         }
1304       }
1305     } catch (e) {
1306       console.error(e);
1307     }
1309     if (
1310       AppConstants.platform == "win" &&
1311       cmdLine.handleFlag("to-handle-default-browser-agent", false)
1312     ) {
1313       // The Default Browser Agent launches Firefox in response to a Windows
1314       // native notification, but it does so in a non-standard manner.
1315       Services.telemetry.setEventRecordingEnabled(
1316         "browser.launched_to_handle",
1317         true
1318       );
1319       Glean.browserLaunchedToHandle.systemNotification.record({
1320         name: "default-browser-agent",
1321       });
1323       let thanksURI = Services.io.newURI(
1324         Services.urlFormatter.formatURLPref(
1325           "browser.shell.defaultBrowserAgent.thanksURL"
1326         )
1327       );
1328       urilist.push(thanksURI);
1329     }
1331     if (cmdLine.findFlag("screenshot", true) != -1) {
1332       lazy.HeadlessShell.handleCmdLineArgs(
1333         cmdLine,
1334         urilist.filter(shouldLoadURI).map(u => u.spec)
1335       );
1336       return;
1337     }
1339     for (let i = 0; i < cmdLine.length; ++i) {
1340       var curarg = cmdLine.getArgument(i);
1341       if (curarg.match(/^-/)) {
1342         console.error("Warning: unrecognized command line flag", curarg);
1343         // To emulate the pre-nsICommandLine behavior, we ignore
1344         // the argument after an unrecognized flag.
1345         ++i;
1346       } else {
1347         try {
1348           urilist.push(resolveURIInternal(cmdLine, curarg));
1349         } catch (e) {
1350           console.error(
1351             `Error opening URI ${curarg} from the command line:`,
1352             e
1353           );
1354         }
1355       }
1356     }
1358     if (urilist.length) {
1359       if (
1360         cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1361         urilist.length == 1
1362       ) {
1363         // Try to find an existing window and load our URI into the
1364         // current tab, new tab, or new window as prefs determine.
1365         try {
1366           handURIToExistingBrowser(
1367             urilist[0],
1368             Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
1369             cmdLine,
1370             false,
1371             lazy.gSystemPrincipal
1372           );
1373           return;
1374         } catch (e) {}
1375       }
1377       var URLlist = urilist.filter(shouldLoadURI).map(u => u.spec);
1378       if (URLlist.length) {
1379         openBrowserWindow(cmdLine, lazy.gSystemPrincipal, URLlist);
1380       }
1381     } else if (!cmdLine.preventDefault) {
1382       if (
1383         AppConstants.platform == "win" &&
1384         cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1385         lazy.WindowsUIUtils.inTabletMode
1386       ) {
1387         // In windows 10 tablet mode, do not create a new window, but reuse the existing one.
1388         let win = lazy.BrowserWindowTracker.getTopWindow();
1389         if (win) {
1390           win.focus();
1391           return;
1392         }
1393       }
1394       openBrowserWindow(cmdLine, lazy.gSystemPrincipal);
1395     } else {
1396       // Need a better solution in the future to avoid opening the blank window
1397       // when command line parameters say we are not going to show a browser
1398       // window, but for now the blank window getting closed quickly (and
1399       // causing only a slight flicker) is better than leaving it open.
1400       let win = Services.wm.getMostRecentWindow("navigator:blank");
1401       if (win) {
1402         win.close();
1403       }
1404     }
1405   },
1407   helpInfo: "",