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 var EXPORTED_SYMBOLS = [
6 "nsBrowserContentHandler",
7 "nsDefaultCommandLineHandler",
10 const { XPCOMUtils } = ChromeUtils.import(
11 "resource://gre/modules/XPCOMUtils.jsm"
13 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
14 const { AppConstants } = ChromeUtils.import(
15 "resource://gre/modules/AppConstants.jsm"
20 XPCOMUtils.defineLazyModuleGetters(lazy, {
21 BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
22 HeadlessShell: "resource:///modules/HeadlessShell.jsm",
23 HomePage: "resource:///modules/HomePage.jsm",
24 FirstStartup: "resource://gre/modules/FirstStartup.jsm",
25 LaterRun: "resource:///modules/LaterRun.jsm",
26 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
27 SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
28 ShellService: "resource:///modules/ShellService.jsm",
29 UpdatePing: "resource://gre/modules/UpdatePing.jsm",
31 XPCOMUtils.defineLazyServiceGetters(lazy, {
32 UpdateManager: ["@mozilla.org/updates/update-manager;1", "nsIUpdateManager"],
33 WinTaskbar: ["@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"],
34 WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
37 XPCOMUtils.defineLazyGetter(lazy, "gSystemPrincipal", () =>
38 Services.scriptSecurityManager.getSystemPrincipal()
41 // One-time startup homepage override configurations
42 const ONCE_DOMAINS = ["mozilla.org", "firefox.com"];
43 const ONCE_PREF = "browser.startup.homepage_override.once";
45 // Index of Private Browsing icon in firefox.exe
46 // Must line up with the one in nsNativeAppSupportWin.h.
47 const PRIVATE_BROWSING_ICON_INDEX = 5;
48 const PRIVACY_SEGMENTATION_PREF = "browser.privacySegmentation.enabled";
50 function shouldLoadURI(aURI) {
51 if (aURI && !aURI.schemeIs("chrome")) {
55 dump("*** Preventing external load of chrome: URI into browser window\n");
56 dump(" Use --chrome <uri> instead\n");
60 function resolveURIInternal(aCmdLine, aArgument) {
61 var uri = aCmdLine.resolveURI(aArgument);
62 var uriFixup = Services.uriFixup;
64 if (!(uri instanceof Ci.nsIFileURL)) {
65 return Services.uriFixup.getFixupURIInfo(
67 uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
72 if (uri.file.exists()) {
79 // We have interpreted the argument as a relative file URI, but the file
80 // doesn't exist. Try URI fixup heuristics: see bug 290782.
83 uri = Services.uriFixup.getFixupURIInfo(aArgument).preferredURI;
92 let gMajorUpgrade = false;
93 var gFirstWindow = false;
95 const OVERRIDE_NONE = 0;
96 const OVERRIDE_NEW_PROFILE = 1;
97 const OVERRIDE_NEW_MSTONE = 2;
98 const OVERRIDE_NEW_BUILD_ID = 3;
100 * Determines whether a home page override is needed.
102 * OVERRIDE_NEW_PROFILE if this is the first run with a new profile.
103 * OVERRIDE_NEW_MSTONE if this is the first run with a build with a different
104 * Gecko milestone (i.e. right after an upgrade).
105 * OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the
106 * same Gecko milestone (i.e. after a nightly upgrade).
107 * OVERRIDE_NONE otherwise.
109 function needHomepageOverride(prefb) {
110 var savedmstone = prefb.getCharPref(
111 "browser.startup.homepage_override.mstone",
115 if (savedmstone == "ignore") {
116 return OVERRIDE_NONE;
119 var mstone = Services.appinfo.platformVersion;
121 var savedBuildID = prefb.getCharPref(
122 "browser.startup.homepage_override.buildID",
126 var buildID = Services.appinfo.platformBuildID;
128 if (mstone != savedmstone) {
129 // Bug 462254. Previous releases had a default pref to suppress the EULA
130 // agreement if the platform's installer had already shown one. Now with
131 // about:rights we've removed the EULA stuff and default pref, but we need
132 // a way to make existing profiles retain the default that we removed.
134 prefb.setBoolPref("browser.rights.3.shown", true);
136 // Remember that we saw a major version change.
137 gMajorUpgrade = true;
140 prefb.setCharPref("browser.startup.homepage_override.mstone", mstone);
141 prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
142 return savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE;
145 if (buildID != savedBuildID) {
146 prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
147 return OVERRIDE_NEW_BUILD_ID;
150 return OVERRIDE_NONE;
154 * Gets the override page for the first run after the application has been
157 * The nsIUpdate for the update that has been applied.
158 * @param defaultOverridePage
159 * The default override page.
160 * @return The override page.
162 function getPostUpdateOverridePage(update, defaultOverridePage) {
163 update = update.QueryInterface(Ci.nsIWritablePropertyBag);
164 let actions = update.getProperty("actions");
165 // When the update doesn't specify actions fallback to the original behavior
166 // of displaying the default override page.
168 return defaultOverridePage;
171 // The existence of silent or the non-existence of showURL in the actions both
172 // mean that an override page should not be displayed.
173 if (actions.includes("silent") || !actions.includes("showURL")) {
177 // If a policy was set to not allow the update.xml-provided
178 // URL to be used, use the default fallback (which will also
179 // be provided by the policy).
180 if (!Services.policies.isAllowed("postUpdateCustomPage")) {
181 return defaultOverridePage;
184 return update.getProperty("openURL") || defaultOverridePage;
188 * Open a browser window. If this is the initial launch, this function will
189 * attempt to use the navigator:blank window opened by BrowserGlue.jsm during
193 * The nsICommandLine object given to nsICommandLineHandler's handle
195 * Used to check if we are processing the command line for the initial launch.
196 * @param triggeringPrincipal
197 * The nsIPrincipal to use as triggering principal for the page load(s).
198 * @param urlOrUrlList (optional)
199 * When omitted, the browser window will be opened with the default
200 * arguments, which will usually load the homepage.
201 * This can be a JS array of urls provided as strings, each url will be
202 * loaded in a tab. postData will be ignored in this case.
203 * This can be a single url to load in the new window, provided as a string.
204 * postData will be used in this case if provided.
205 * @param postData (optional)
206 * An nsIInputStream object to use as POST data when loading the provided
208 * @param forcePrivate (optional)
209 * Boolean. If set to true, the new window will be a private browsing one.
211 * @returns {ChromeWindow}
212 * Returns the top level window opened.
214 function openBrowserWindow(
221 let chromeURL = AppConstants.BROWSER_CHROME_URL;
223 cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
227 // Just pass in the defaultArgs directly. We'll use system principal on the other end.
228 args = [gBrowserContentHandler.getArgs(isStartup)];
229 } else if (Array.isArray(urlOrUrlList)) {
230 // There isn't an explicit way to pass a principal here, so we load multiple URLs
231 // with system principal when we get to actually loading them.
233 !triggeringPrincipal ||
234 !triggeringPrincipal.equals(lazy.gSystemPrincipal)
237 "Can't open multiple URLs with something other than system principal."
240 // Passing an nsIArray for the url disables the "|"-splitting behavior.
241 let uriArray = Cc["@mozilla.org/array;1"].createInstance(
244 urlOrUrlList.forEach(function(uri) {
245 var sstring = Cc["@mozilla.org/supports-string;1"].createInstance(
249 uriArray.appendElement(sstring);
253 let extraOptions = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
254 Ci.nsIWritablePropertyBag2
256 extraOptions.setPropertyAsBool("fromExternal", true);
258 // Always pass at least 3 arguments to avoid the "|"-splitting behavior,
259 // ie. avoid the loadOneOrMoreURIs function.
260 // Also, we need to pass the triggering principal.
266 undefined, // allowThirdPartyFixup; this would be `false` but that
267 // needs a conversion. Hopefully bug 1485961 will fix.
268 undefined, // user context id
269 null, // origin principal
270 null, // origin storage principal
276 let win = Services.wm.getMostRecentWindow("navigator:blank");
278 // Remove the windowtype of our blank window so that we don't close it
279 // later on when seeing cmdLine.preventDefault is true.
280 win.document.documentElement.removeAttribute("windowtype");
283 win.docShell.QueryInterface(
285 ).usePrivateBrowsing = true;
286 if (Services.prefs.getBoolPref(PRIVACY_SEGMENTATION_PREF)) {
287 // TODO: Changing this after the Window has been painted causes it to
288 // change Taskbar icons if the original one had a different AUMID.
289 // This must stay pref'ed off until this is resolved.
290 // https://bugzilla.mozilla.org/show_bug.cgi?id=1751010
291 lazy.WinTaskbar.setGroupIdForWindow(
293 lazy.WinTaskbar.defaultPrivateGroupId
295 lazy.WindowsUIUtils.setWindowIconFromExe(
297 Services.dirsvc.get("XREExeF", Ci.nsIFile).path,
298 // This corresponds to the definitions in
299 // nsNativeAppSupportWin.h
300 PRIVATE_BROWSING_ICON_INDEX
305 let openTime = win.openTime;
306 win.location = chromeURL;
307 win.arguments = args; // <-- needs to be a plain JS array here.
309 ChromeUtils.addProfilerMarker("earlyBlankWindowVisible", openTime);
314 // We can't provide arguments to openWindow as a JS array.
316 // If we have a single string guaranteed to not contain '|' we can simply
317 // wrap it in an nsISupportsString object.
319 args = Cc["@mozilla.org/supports-string;1"].createInstance(
324 // Otherwise, pass an nsIArray.
325 if (args.length > 1) {
326 let string = Cc["@mozilla.org/supports-string;1"].createInstance(
329 string.data = args[0];
332 let array = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
334 array.appendElement(a);
340 "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine);
342 features += ",private";
345 return Services.ww.openWindow(null, chromeURL, "_blank", features, args);
348 function openPreferences(cmdLine, extraArgs) {
349 openBrowserWindow(cmdLine, lazy.gSystemPrincipal, "about:preferences");
352 async function doSearch(searchTerm, cmdLine) {
353 // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
354 // preferences, but need nsIBrowserDOMWindow extensions
355 // Open the window immediately as BrowserContentHandler needs to
356 // be handled synchronously. Then load the search URI when the
357 // SearchService has loaded.
358 let win = openBrowserWindow(cmdLine, lazy.gSystemPrincipal, "about:blank");
359 await new Promise(resolve => {
360 Services.obs.addObserver(function observe(subject) {
361 if (subject == win) {
362 Services.obs.removeObserver(
364 "browser-delayed-startup-finished"
368 }, "browser-delayed-startup-finished");
371 win.BrowserSearch.loadSearchFromCommandLine(
373 lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode ||
374 lazy.PrivateBrowsingUtils.isWindowPrivate(win),
375 lazy.gSystemPrincipal,
376 win.gBrowser.selectedBrowser.csp
377 ).catch(Cu.reportError);
380 function nsBrowserContentHandler() {
381 if (!gBrowserContentHandler) {
382 gBrowserContentHandler = this;
384 return gBrowserContentHandler;
386 nsBrowserContentHandler.prototype = {
388 QueryInterface: ChromeUtils.generateQI([
389 "nsICommandLineHandler",
392 "nsICommandLineValidator",
395 /* nsICommandLineHandler */
396 handle: function bch_handle(cmdLine) {
397 if (cmdLine.handleFlag("kiosk", false)) {
400 if (cmdLine.handleFlag("disable-pinch", false)) {
401 let defaults = Services.prefs.getDefaultBranch(null);
402 defaults.setBoolPref("apz.allow_zooming", false);
403 Services.prefs.lockPref("apz.allow_zooming");
404 defaults.setCharPref("browser.gesture.pinch.in", "");
405 Services.prefs.lockPref("browser.gesture.pinch.in");
406 defaults.setCharPref("browser.gesture.pinch.in.shift", "");
407 Services.prefs.lockPref("browser.gesture.pinch.in.shift");
408 defaults.setCharPref("browser.gesture.pinch.out", "");
409 Services.prefs.lockPref("browser.gesture.pinch.out");
410 defaults.setCharPref("browser.gesture.pinch.out.shift", "");
411 Services.prefs.lockPref("browser.gesture.pinch.out.shift");
413 if (cmdLine.handleFlag("browser", false)) {
414 openBrowserWindow(cmdLine, lazy.gSystemPrincipal);
415 cmdLine.preventDefault = true;
418 // In the past, when an instance was not already running, the -remote
419 // option returned an error code. Any script or application invoking the
420 // -remote option is expected to be handling this case, otherwise they
421 // wouldn't be doing anything when there is no Firefox already running.
422 // Making the -remote option always return an error code makes those
423 // scripts or applications handle the situation as if Firefox was not
425 if (cmdLine.handleFlag("remote", true)) {
426 throw Components.Exception("", Cr.NS_ERROR_ABORT);
431 while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
432 let uri = resolveURIInternal(cmdLine, uriparam);
433 if (!shouldLoadURI(uri)) {
436 openBrowserWindow(cmdLine, lazy.gSystemPrincipal, uri.spec);
437 cmdLine.preventDefault = true;
444 while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) {
445 let uri = resolveURIInternal(cmdLine, uriparam);
446 handURIToExistingBrowser(
448 Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
451 lazy.gSystemPrincipal
453 cmdLine.preventDefault = true;
459 var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
461 // Handle old preference dialog URLs.
463 chromeParam == "chrome://browser/content/pref/pref.xul" ||
464 chromeParam == "chrome://browser/content/preferences/preferences.xul"
466 openPreferences(cmdLine);
467 cmdLine.preventDefault = true;
470 let resolvedURI = resolveURIInternal(cmdLine, chromeParam);
471 let isLocal = uri => {
472 let localSchemes = new Set(["chrome", "file", "resource"]);
473 if (uri instanceof Ci.nsINestedURI) {
474 uri = uri.QueryInterface(Ci.nsINestedURI).innerMostURI;
476 return localSchemes.has(uri.scheme);
478 if (isLocal(resolvedURI)) {
479 // If the URI is local, we are sure it won't wrongly inherit chrome privs
480 let features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
481 // Provide 1 null argument, as openWindow has a different behavior
482 // when the arg count is 0.
483 let argArray = Cc["@mozilla.org/array;1"].createInstance(
486 argArray.appendElement(null);
487 Services.ww.openWindow(
494 cmdLine.preventDefault = true;
496 dump("*** Preventing load of web URI as chrome\n");
498 " If you're trying to load a webpage, do not pass --chrome.\n"
506 if (cmdLine.handleFlag("preferences", false)) {
507 openPreferences(cmdLine);
508 cmdLine.preventDefault = true;
510 if (cmdLine.handleFlag("silent", false)) {
511 cmdLine.preventDefault = true;
515 var privateWindowParam = cmdLine.handleFlagWithParam(
519 if (privateWindowParam) {
520 let forcePrivate = true;
522 if (!lazy.PrivateBrowsingUtils.enabled) {
523 // Load about:privatebrowsing in a normal tab, which will display an error indicating
524 // access to private browsing has been disabled.
525 forcePrivate = false;
526 resolvedURI = Services.io.newURI("about:privatebrowsing");
528 resolvedURI = resolveURIInternal(cmdLine, privateWindowParam);
530 handURIToExistingBrowser(
532 Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
535 lazy.gSystemPrincipal
537 cmdLine.preventDefault = true;
540 if (e.result != Cr.NS_ERROR_INVALID_ARG) {
543 // NS_ERROR_INVALID_ARG is thrown when flag exists, but has no param.
544 if (cmdLine.handleFlag("private-window", false)) {
547 lazy.gSystemPrincipal,
548 "about:privatebrowsing",
550 lazy.PrivateBrowsingUtils.enabled
552 cmdLine.preventDefault = true;
556 var searchParam = cmdLine.handleFlagWithParam("search", false);
558 doSearch(searchParam, cmdLine);
559 cmdLine.preventDefault = true;
562 // The global PB Service consumes this flag, so only eat it in per-window
565 cmdLine.handleFlag("private", false) &&
566 lazy.PrivateBrowsingUtils.enabled
568 lazy.PrivateBrowsingUtils.enterTemporaryAutoStartMode();
569 if (cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH) {
570 let win = Services.wm.getMostRecentWindow("navigator:blank");
572 win.docShell.QueryInterface(
574 ).usePrivateBrowsing = true;
578 if (cmdLine.handleFlag("setDefaultBrowser", false)) {
579 lazy.ShellService.setDefaultBrowser(true, true);
582 if (cmdLine.handleFlag("first-startup", false)) {
583 lazy.FirstStartup.init();
586 var fileParam = cmdLine.handleFlagWithParam("file", false);
588 var file = cmdLine.resolveFile(fileParam);
589 var fileURI = Services.io.newFileURI(file);
590 openBrowserWindow(cmdLine, lazy.gSystemPrincipal, fileURI.spec);
591 cmdLine.preventDefault = true;
594 if (AppConstants.platform == "win") {
595 // Handle "? searchterm" for Windows Vista start menu integration
596 for (var i = cmdLine.length - 1; i >= 0; --i) {
597 var param = cmdLine.getArgument(i);
598 if (param.match(/^\? /)) {
599 cmdLine.removeArguments(i, i);
600 cmdLine.preventDefault = true;
602 searchParam = param.substr(2);
603 doSearch(searchParam, cmdLine);
611 " --browser Open a browser window.\n" +
612 " --new-window <url> Open <url> in a new window.\n" +
613 " --new-tab <url> Open <url> in a new tab.\n" +
614 " --private-window <url> Open <url> in a new private window.\n";
615 if (AppConstants.platform == "win") {
616 info += " --preferences Open Options dialog.\n";
618 info += " --preferences Open Preferences dialog.\n";
621 " --screenshot [<path>] Save screenshot to <path> or in working directory.\n";
623 " --window-size width[,height] Width and optionally height of screenshot.\n";
625 " --search <term> Search <term> with your default search engine.\n";
626 info += " --setDefaultBrowser Set this app as the default browser.\n";
628 " --first-startup Run post-install actions before opening a new window.\n";
629 info += " --kiosk Start the browser in kiosk mode.\n";
631 " --disable-pinch Disable touch-screen and touch-pad pinch gestures.\n";
635 /* nsIBrowserHandler */
638 return this.getArgs();
641 getArgs(isStartup = false) {
642 var prefb = Services.prefs;
646 if (lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
647 return "about:privatebrowsing";
652 var overridePage = "";
653 var additionalPage = "";
654 var willRestoreSession = false;
656 // Read the old value of homepage_override.mstone before
657 // needHomepageOverride updates it, so that we can later add it to the
658 // URL if we do end up showing an overridePage. This makes it possible
659 // to have the overridePage's content vary depending on the version we're
661 let old_mstone = Services.prefs.getCharPref(
662 "browser.startup.homepage_override.mstone",
665 let old_buildId = Services.prefs.getCharPref(
666 "browser.startup.homepage_override.buildID",
669 override = needHomepageOverride(prefb);
670 if (override != OVERRIDE_NONE) {
672 case OVERRIDE_NEW_PROFILE:
674 overridePage = Services.urlFormatter.formatURLPref(
675 "startup.homepage_welcome_url"
677 additionalPage = Services.urlFormatter.formatURLPref(
678 "startup.homepage_welcome_url.additional"
680 // Turn on 'later run' pages for new profiles.
681 lazy.LaterRun.enabled = true;
683 case OVERRIDE_NEW_MSTONE:
684 // Check whether we will restore a session. If we will, we assume
685 // that this is an "update" session. This does not take crashes
686 // into account because that requires waiting for the session file
687 // to be read. If a crash occurs after updating, before restarting,
688 // we may open the startPage in addition to restoring the session.
689 willRestoreSession = lazy.SessionStartup.isAutomaticRestoreEnabled();
691 overridePage = Services.urlFormatter.formatURLPref(
692 "startup.homepage_override_url"
694 let update = lazy.UpdateManager.readyUpdate;
697 Services.vc.compare(update.appVersion, old_mstone) > 0
699 overridePage = getPostUpdateOverridePage(update, overridePage);
700 // Send the update ping to signal that the update was successful.
701 lazy.UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
704 overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
706 case OVERRIDE_NEW_BUILD_ID:
707 if (lazy.UpdateManager.readyUpdate) {
708 // Send the update ping to signal that the update was successful.
709 lazy.UpdatePing.handleUpdateSuccess(old_mstone, old_buildId);
716 // formatURLPref might return "about:blank" if getting the pref fails
717 if (overridePage == "about:blank") {
721 // Allow showing a one-time startup override if we're not showing one
722 if (isStartup && overridePage == "" && prefb.prefHasUserValue(ONCE_PREF)) {
724 // Show if we haven't passed the expiration or there's no expiration
725 const { expire, url } = JSON.parse(
726 Services.urlFormatter.formatURLPref(ONCE_PREF)
728 if (!(Date.now() > expire)) {
729 // Only set allowed urls as override pages
736 // Invalid URL, so filter out below
737 Cu.reportError(`Invalid once url: ${ex}`);
744 parsed.protocol == "https:" &&
745 // Only accept exact hostname or subdomain; without port
746 ONCE_DOMAINS.includes(
747 Services.eTLD.getBaseDomainFromHost(parsed.host)
752 // Be noisy as properly configured urls should be unchanged
753 if (overridePage != url) {
754 Cu.reportError(`Mismatched once urls: ${url}`);
758 // Invalid json pref, so ignore (and clear below)
759 Cu.reportError(`Invalid once pref: ${ex}`);
761 prefb.clearUserPref(ONCE_PREF);
765 if (!additionalPage) {
766 additionalPage = lazy.LaterRun.getURL() || "";
769 if (additionalPage && additionalPage != "about:blank") {
771 overridePage += "|" + additionalPage;
773 overridePage = additionalPage;
779 var choice = prefb.getIntPref("browser.startup.page");
780 if (choice == 1 || choice == 3) {
781 startPage = lazy.HomePage.get();
787 if (startPage == "about:blank") {
792 override == OVERRIDE_NEW_PROFILE &&
793 prefb.getBoolPref("browser.startup.firstrunSkipsHomepage");
794 // Only show the startPage if we're not restoring an update session and are
795 // not set to skip the start page on this profile
796 if (overridePage && startPage && !willRestoreSession && !skipStartPage) {
797 return overridePage + "|" + startPage;
800 return overridePage || startPage || "about:blank";
805 getFeatures: function bch_features(cmdLine) {
806 if (this.mFeatures === null) {
811 var width = cmdLine.handleFlagWithParam("width", false);
812 var height = cmdLine.handleFlagWithParam("height", false);
813 var left = cmdLine.handleFlagWithParam("left", false);
814 var top = cmdLine.handleFlagWithParam("top", false);
817 this.mFeatures += ",width=" + width;
820 this.mFeatures += ",height=" + height;
823 this.mFeatures += ",left=" + left;
826 this.mFeatures += ",top=" + top;
831 // The global PB Service consumes this flag, so only eat it in per-window
833 if (lazy.PrivateBrowsingUtils.isInTemporaryAutoStartMode) {
834 this.mFeatures += ",private";
838 Services.prefs.getBoolPref("browser.suppress_first_window_animation") &&
839 !Services.wm.getMostRecentWindow("navigator:browser")
841 this.mFeatures += ",suppressanimation";
845 return this.mFeatures;
853 return gMajorUpgrade;
856 set majorUpgrade(val) {
860 /* nsIContentHandler */
862 handleContent: function bch_handleContent(contentType, context, request) {
863 const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
866 var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"].getService(
867 Ci.nsIWebNavigationInfo
869 if (!webNavInfo.isTypeSupported(contentType)) {
870 throw NS_ERROR_WONT_HANDLE_CONTENT;
873 throw NS_ERROR_WONT_HANDLE_CONTENT;
876 request.QueryInterface(Ci.nsIChannel);
877 handURIToExistingBrowser(
879 Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
882 request.loadInfo.triggeringPrincipal
884 request.cancel(Cr.NS_BINDING_ABORTED);
887 /* nsICommandLineValidator */
888 validate: function bch_validate(cmdLine) {
889 var urlFlagIdx = cmdLine.findFlag("url", false);
892 cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_EXPLICIT
894 var urlParam = cmdLine.getArgument(urlFlagIdx + 1);
896 cmdLine.length != urlFlagIdx + 2 ||
897 /firefoxurl(-[a-f0-9]+)?:/i.test(urlParam)
899 throw Components.Exception("", Cr.NS_ERROR_ABORT);
901 var isDefault = false;
904 Services.urlFormatter.formatURLPref("app.support.baseURL") +
905 "win10-default-browser";
906 if (urlParam == url) {
907 isDefault = lazy.ShellService.isDefaultBrowser(false, false);
911 // Firefox is already the default HTTP handler.
912 // We don't have to show the instruction page.
913 throw Components.Exception("", Cr.NS_ERROR_ABORT);
918 var gBrowserContentHandler = new nsBrowserContentHandler();
920 function handURIToExistingBrowser(
927 if (!shouldLoadURI(uri)) {
931 // Unless using a private window is forced, open external links in private
932 // windows only if we're in perma-private mode.
934 forcePrivate || lazy.PrivateBrowsingUtils.permanentPrivateBrowsing;
935 var navWin = lazy.BrowserWindowTracker.getTopWindow({
936 private: allowPrivate,
939 // if we couldn't load it in an existing window, open a new one
950 var bwin = navWin.browserDOMWindow;
955 Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL,
961 * If given URI is a file type or a protocol, record telemetry that
962 * Firefox was invoked or launched (if `isLaunch` is truth-y). If the
963 * file type or protocol is not registered by default, record it as
964 * ".<other extension>" or "<other protocol>".
967 * The URI Firefox was asked to handle.
969 * truth-y if Firefox was launched/started rather than running and invoked.
971 function maybeRecordToHandleTelemetry(uri, isLaunch) {
972 let scalar = isLaunch
973 ? "os.environment.launched_to_handle"
974 : "os.environment.invoked_to_handle";
976 if (uri instanceof Ci.nsIFileURL) {
977 let extension = "." + uri.fileExtension.toLowerCase();
978 // Keep synchronized with https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/shared.nsh
979 // and https://searchfox.org/mozilla-central/source/browser/installer/windows/msix/AppxManifest.xml.in.
980 let registeredExtensions = new Set([
991 if (registeredExtensions.has(extension)) {
992 Services.telemetry.keyedScalarAdd(scalar, extension, 1);
994 Services.telemetry.keyedScalarAdd(scalar, ".<other extension>", 1);
997 let scheme = uri.scheme.toLowerCase();
998 let registeredSchemes = new Set(["about", "http", "https", "mailto"]);
999 if (registeredSchemes.has(scheme)) {
1000 Services.telemetry.keyedScalarAdd(scalar, scheme, 1);
1002 Services.telemetry.keyedScalarAdd(scalar, "<other protocol>", 1);
1007 function nsDefaultCommandLineHandler() {}
1009 nsDefaultCommandLineHandler.prototype = {
1011 QueryInterface: ChromeUtils.generateQI(["nsICommandLineHandler"]),
1013 _haveProfile: false,
1015 /* nsICommandLineHandler */
1016 handle: function dch_handle(cmdLine) {
1020 cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1021 Services.startup.wasSilentlyStarted
1023 // If we are starting up in silent mode, don't open a window. We also need
1024 // to make sure that the application doesn't immediately exit, so stay in
1025 // a LastWindowClosingSurvivalArea until a window opens.
1026 Services.startup.enterLastWindowClosingSurvivalArea();
1027 Services.obs.addObserver(function windowOpenObserver() {
1028 Services.startup.exitLastWindowClosingSurvivalArea();
1029 Services.obs.removeObserver(windowOpenObserver, "domwindowopened");
1030 }, "domwindowopened");
1034 if (AppConstants.platform == "win") {
1035 // If we don't have a profile selected yet (e.g. the Profile Manager is
1036 // displayed) we will crash if we open an url and then select a profile. To
1037 // prevent this handle all url command line flags and set the command line's
1038 // preventDefault to true to prevent the display of the ui. The initial
1039 // command line will be retained when nsAppRunner calls LaunchChild though
1040 // urls launched after the initial launch will be lost.
1041 if (!this._haveProfile) {
1043 // This will throw when a profile has not been selected.
1044 Services.dirsvc.get("ProfD", Ci.nsIFile);
1045 this._haveProfile = true;
1047 // eslint-disable-next-line no-empty
1048 while ((ar = cmdLine.handleFlagWithParam("url", false))) {}
1049 cmdLine.preventDefault = true;
1054 // `-osint` and handling registered file types and protocols is Windows-only.
1055 let launchedWithArg_osint =
1056 AppConstants.platform == "win" && cmdLine.findFlag("osint", false) == 0;
1057 if (launchedWithArg_osint) {
1058 cmdLine.handleFlag("osint", false);
1063 while ((ar = cmdLine.handleFlagWithParam("url", false))) {
1064 var uri = resolveURIInternal(cmdLine, ar);
1067 if (launchedWithArg_osint) {
1068 launchedWithArg_osint = false;
1070 // We use the resolved URI here, even though it can produce
1071 // surprising results where-by `-osint -url test.pdf` resolves to
1072 // a query with search parameter "test.pdf". But that shouldn't
1073 // happen when Firefox is launched by Windows itself: files should
1074 // exist and be resolved to file URLs.
1076 cmdLine && cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH;
1078 maybeRecordToHandleTelemetry(uri, isLaunch);
1085 if (cmdLine.findFlag("screenshot", true) != -1) {
1086 lazy.HeadlessShell.handleCmdLineArgs(
1088 urilist.filter(shouldLoadURI).map(u => u.spec)
1093 for (let i = 0; i < cmdLine.length; ++i) {
1094 var curarg = cmdLine.getArgument(i);
1095 if (curarg.match(/^-/)) {
1097 "Warning: unrecognized command line flag " + curarg + "\n"
1099 // To emulate the pre-nsICommandLine behavior, we ignore
1100 // the argument after an unrecognized flag.
1104 urilist.push(resolveURIInternal(cmdLine, curarg));
1107 "Error opening URI '" +
1109 "' from the command line: " +
1117 if (urilist.length) {
1119 cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1122 // Try to find an existing window and load our URI into the
1123 // current tab, new tab, or new window as prefs determine.
1125 handURIToExistingBrowser(
1127 Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW,
1130 lazy.gSystemPrincipal
1136 var URLlist = urilist.filter(shouldLoadURI).map(u => u.spec);
1137 if (URLlist.length) {
1138 openBrowserWindow(cmdLine, lazy.gSystemPrincipal, URLlist);
1140 } else if (!cmdLine.preventDefault) {
1142 AppConstants.isPlatformAndVersionAtLeast("win", "10") &&
1143 cmdLine.state != Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
1144 lazy.WindowsUIUtils.inTabletMode
1146 // In windows 10 tablet mode, do not create a new window, but reuse the existing one.
1147 let win = lazy.BrowserWindowTracker.getTopWindow();
1153 openBrowserWindow(cmdLine, lazy.gSystemPrincipal);
1155 // Need a better solution in the future to avoid opening the blank window
1156 // when command line parameters say we are not going to show a browser
1157 // window, but for now the blank window getting closed quickly (and
1158 // causing only a slight flicker) is better than leaving it open.
1159 let win = Services.wm.getMostRecentWindow("navigator:blank");