Follow up fix for bug 623435. (r=brendan)
[mozilla-central.git] / browser / components / nsBrowserContentHandler.js
blobd5c6fe4678866093aecf2f885969546d6d269be2
1 # ***** BEGIN LICENSE BLOCK *****
2 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 # The contents of this file are subject to the Mozilla Public License Version
5 # 1.1 (the "License"); you may not use this file except in compliance with
6 # the License. You may obtain a copy of the License at
7 # http://www.mozilla.org/MPL/
9 # Software distributed under the License is distributed on an "AS IS" basis,
10 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 # for the specific language governing rights and limitations under the
12 # License.
14 # The Original Code is the Mozilla Firefox browser.
16 # The Initial Developer of the Original Code is
17 # Benjamin Smedberg <benjamin@smedbergs.us>
19 # Portions created by the Initial Developer are Copyright (C) 2004
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 #   Robert Strong <robert.bugzilla@gmail.com>
25 # Alternatively, the contents of this file may be used under the terms of
26 # either the GNU General Public License Version 2 or later (the "GPL"), or
27 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 # in which case the provisions of the GPL or the LGPL are applicable instead
29 # of those above. If you wish to allow use of your version of this file only
30 # under the terms of either the GPL or the LGPL, and not to allow others to
31 # use your version of this file under the terms of the MPL, indicate your
32 # decision by deleting the provisions above and replace them with the notice
33 # and other provisions required by the GPL or the LGPL. If you do not delete
34 # the provisions above, a recipient may use your version of this file under
35 # the terms of any one of the MPL, the GPL or the LGPL.
37 # ***** END LICENSE BLOCK *****
39 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
40 Components.utils.import("resource://gre/modules/Services.jsm");
42 const nsISupports            = Components.interfaces.nsISupports;
44 const nsIBrowserDOMWindow    = Components.interfaces.nsIBrowserDOMWindow;
45 const nsIBrowserHandler      = Components.interfaces.nsIBrowserHandler;
46 const nsIBrowserHistory      = Components.interfaces.nsIBrowserHistory;
47 const nsIChannel             = Components.interfaces.nsIChannel;
48 const nsICommandLine         = Components.interfaces.nsICommandLine;
49 const nsICommandLineHandler  = Components.interfaces.nsICommandLineHandler;
50 const nsIContentHandler      = Components.interfaces.nsIContentHandler;
51 const nsIDocShellTreeItem    = Components.interfaces.nsIDocShellTreeItem;
52 const nsIDOMChromeWindow     = Components.interfaces.nsIDOMChromeWindow;
53 const nsIDOMWindow           = Components.interfaces.nsIDOMWindow;
54 const nsIFileURL             = Components.interfaces.nsIFileURL;
55 const nsIHttpProtocolHandler = Components.interfaces.nsIHttpProtocolHandler;
56 const nsIInterfaceRequestor  = Components.interfaces.nsIInterfaceRequestor;
57 const nsINetUtil             = Components.interfaces.nsINetUtil;
58 const nsIPrefBranch          = Components.interfaces.nsIPrefBranch;
59 const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString;
60 const nsISupportsString      = Components.interfaces.nsISupportsString;
61 const nsIURIFixup            = Components.interfaces.nsIURIFixup;
62 const nsIWebNavigation       = Components.interfaces.nsIWebNavigation;
63 const nsIWindowMediator      = Components.interfaces.nsIWindowMediator;
64 const nsIWindowWatcher       = Components.interfaces.nsIWindowWatcher;
65 const nsICategoryManager     = Components.interfaces.nsICategoryManager;
66 const nsIWebNavigationInfo   = Components.interfaces.nsIWebNavigationInfo;
67 const nsIBrowserSearchService = Components.interfaces.nsIBrowserSearchService;
68 const nsICommandLineValidator = Components.interfaces.nsICommandLineValidator;
69 const nsIXULAppInfo          = Components.interfaces.nsIXULAppInfo;
71 const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED;
72 const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
73 const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT;
75 const URI_INHERITS_SECURITY_CONTEXT = nsIHttpProtocolHandler
76                                         .URI_INHERITS_SECURITY_CONTEXT;
78 function shouldLoadURI(aURI) {
79   if (aURI && !aURI.schemeIs("chrome"))
80     return true;
82   dump("*** Preventing external load of chrome: URI into browser window\n");
83   dump("    Use -chrome <uri> instead\n");
84   return false;
87 function resolveURIInternal(aCmdLine, aArgument) {
88   var uri = aCmdLine.resolveURI(aArgument);
90   if (!(uri instanceof nsIFileURL)) {
91     return uri;
92   }
94   try {
95     if (uri.file.exists())
96       return uri;
97   }
98   catch (e) {
99     Components.utils.reportError(e);
100   }
102   // We have interpreted the argument as a relative file URI, but the file
103   // doesn't exist. Try URI fixup heuristics: see bug 290782.
105   try {
106     var urifixup = Components.classes["@mozilla.org/docshell/urifixup;1"]
107                              .getService(nsIURIFixup);
109     uri = urifixup.createFixupURI(aArgument, 0);
110   }
111   catch (e) {
112     Components.utils.reportError(e);
113   }
115   return uri;
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 = null;
134   try {
135     savedmstone = prefb.getCharPref("browser.startup.homepage_override.mstone");
136   } catch (e) {}
138   if (savedmstone == "ignore")
139     return OVERRIDE_NONE;
141   var mstone = Components.classes["@mozilla.org/network/protocol;1?name=http"]
142                          .getService(nsIHttpProtocolHandler).misc;
144   var savedBuildID = null;
145   try {
146     savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID");
147   } catch (e) {}
149   var buildID =  Components.classes["@mozilla.org/xre/app-info;1"]
150                            .getService(nsIXULAppInfo).platformBuildID;
152   if (mstone != savedmstone) {
153     // Bug 462254. Previous releases had a default pref to suppress the EULA
154     // agreement if the platform's installer had already shown one. Now with
155     // about:rights we've removed the EULA stuff and default pref, but we need
156     // a way to make existing profiles retain the default that we removed.
157     if (savedmstone)
158       prefb.setBoolPref("browser.rights.3.shown", true);
159     
160     prefb.setCharPref("browser.startup.homepage_override.mstone", mstone);
161     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
162     return (savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE);
163   }
165   if (buildID != savedBuildID) {
166     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
167     return OVERRIDE_NEW_BUILD_ID;
168   }
170   return OVERRIDE_NONE;
174  * Gets the override page for the first run after the application has been
175  * updated.
176  * @param  defaultOverridePage
177  *         The default override page.
178  * @return The override page.
179  */
180 function getPostUpdateOverridePage(defaultOverridePage) {
181   var um = Components.classes["@mozilla.org/updates/update-manager;1"]
182                      .getService(Components.interfaces.nsIUpdateManager);
183   try {
184     // If the updates.xml file is deleted then getUpdateAt will throw.
185     var update = um.getUpdateAt(0)
186                    .QueryInterface(Components.interfaces.nsIPropertyBag);
187   } catch (e) {
188     // This should never happen.
189     Components.utils.reportError("Unable to find update: " + e);
190     return defaultOverridePage;
191   }
193   let actions = update.getProperty("actions");
194   // When the update doesn't specify actions fallback to the original behavior
195   // of displaying the default override page.
196   if (!actions)
197     return defaultOverridePage;
199   // The existence of silent or the non-existence of showURL in the actions both
200   // mean that an override page should not be displayed.
201   if (actions.indexOf("silent") != -1 || actions.indexOf("showURL") == -1)
202     return "";
204   return update.getProperty("openURL") || defaultOverridePage;
207 // Copies a pref override file into the user's profile pref-override folder,
208 // and then tells the pref service to reload its default prefs.
209 function copyPrefOverride() {
210   try {
211     var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]
212                                 .getService(Components.interfaces.nsIProperties);
213     const NS_APP_EXISTING_PREF_OVERRIDE = "ExistingPrefOverride";
214     var prefOverride = fileLocator.get(NS_APP_EXISTING_PREF_OVERRIDE,
215                                        Components.interfaces.nsIFile);
216     if (!prefOverride.exists())
217       return; // nothing to do
219     const NS_APP_PREFS_OVERRIDE_DIR     = "PrefDOverride";
220     var prefOverridesDir = fileLocator.get(NS_APP_PREFS_OVERRIDE_DIR,
221                                            Components.interfaces.nsIFile);
223     // Check for any existing pref overrides, and remove them if present
224     var existingPrefOverridesFile = prefOverridesDir.clone();
225     existingPrefOverridesFile.append(prefOverride.leafName);
226     if (existingPrefOverridesFile.exists())
227       existingPrefOverridesFile.remove(false);
229     prefOverride.copyTo(prefOverridesDir, null);
231     // Now that we've installed the new-profile pref override file,
232     // re-read the default prefs.
233     var prefSvcObs = Components.classes["@mozilla.org/preferences-service;1"]
234                                .getService(Components.interfaces.nsIObserver);
235     prefSvcObs.observe(null, "reload-default-prefs", null);
236   } catch (ex) {
237     Components.utils.reportError(ex);
238   }
241 // Flag used to indicate that the arguments to openWindow can be passed directly.
242 const NO_EXTERNAL_URIS = 1;
244 function openWindow(parent, url, target, features, args, noExternalArgs) {
245   var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
246                          .getService(nsIWindowWatcher);
248   if (noExternalArgs == NO_EXTERNAL_URIS) {
249     // Just pass in the defaultArgs directly
250     var argstring;
251     if (args) {
252       argstring = Components.classes["@mozilla.org/supports-string;1"]
253                             .createInstance(nsISupportsString);
254       argstring.data = args;
255     }
257     return wwatch.openWindow(parent, url, target, features, argstring);
258   }
259   
260   // Pass an array to avoid the browser "|"-splitting behavior.
261   var argArray = Components.classes["@mozilla.org/supports-array;1"]
262                     .createInstance(Components.interfaces.nsISupportsArray);
264   // add args to the arguments array
265   var stringArgs = null;
266   if (args instanceof Array) // array
267     stringArgs = args;
268   else if (args) // string
269     stringArgs = [args];
271   if (stringArgs) {
272     // put the URIs into argArray
273     var uriArray = Components.classes["@mozilla.org/supports-array;1"]
274                        .createInstance(Components.interfaces.nsISupportsArray);
275     stringArgs.forEach(function (uri) {
276       var sstring = Components.classes["@mozilla.org/supports-string;1"]
277                               .createInstance(nsISupportsString);
278       sstring.data = uri;
279       uriArray.AppendElement(sstring);
280     });
281     argArray.AppendElement(uriArray);
282   } else {
283     argArray.AppendElement(null);
284   }
286   // Pass these as null to ensure that we always trigger the "single URL"
287   // behavior in browser.js's BrowserStartup (which handles the window
288   // arguments)
289   argArray.AppendElement(null); // charset
290   argArray.AppendElement(null); // referer
291   argArray.AppendElement(null); // postData
292   argArray.AppendElement(null); // allowThirdPartyFixup
294   return wwatch.openWindow(parent, url, target, features, argArray);
297 function openPreferences() {
298   var features = "chrome,titlebar,toolbar,centerscreen,dialog=no";
299   var url = "chrome://browser/content/preferences/preferences.xul";
301   var win = getMostRecentWindow("Browser:Preferences");
302   if (win) {
303     win.focus();
304   } else {
305     openWindow(null, url, "_blank", features);
306   }
309 function getMostRecentWindow(aType) {
310   var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
311                      .getService(nsIWindowMediator);
312   return wm.getMostRecentWindow(aType);
315 // this returns the most recent non-popup browser window
316 function getMostRecentBrowserWindow() {
317   var browserGlue = Components.classes["@mozilla.org/browser/browserglue;1"]
318                               .getService(Components.interfaces.nsIBrowserGlue);
319   return browserGlue.getMostRecentBrowserWindow();
322 function doSearch(searchTerm, cmdLine) {
323   var ss = Components.classes["@mozilla.org/browser/search-service;1"]
324                      .getService(nsIBrowserSearchService);
326   var submission = ss.defaultEngine.getSubmission(searchTerm);
328   // fill our nsISupportsArray with uri-as-wstring, null, null, postData
329   var sa = Components.classes["@mozilla.org/supports-array;1"]
330                      .createInstance(Components.interfaces.nsISupportsArray);
332   var wuri = Components.classes["@mozilla.org/supports-string;1"]
333                        .createInstance(Components.interfaces.nsISupportsString);
334   wuri.data = submission.uri.spec;
336   sa.AppendElement(wuri);
337   sa.AppendElement(null);
338   sa.AppendElement(null);
339   sa.AppendElement(submission.postData);
341   // XXXbsmedberg: use handURIToExistingBrowser to obey tabbed-browsing
342   // preferences, but need nsIBrowserDOMWindow extensions
344   var wwatch = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
345                          .getService(nsIWindowWatcher);
347   return wwatch.openWindow(null, gBrowserContentHandler.chromeURL,
348                            "_blank",
349                            "chrome,dialog=no,all" +
350                            gBrowserContentHandler.getFeatures(cmdLine),
351                            sa);
354 function nsBrowserContentHandler() {
356 nsBrowserContentHandler.prototype = {
357   classID: Components.ID("{5d0ce354-df01-421a-83fb-7ead0990c24e}"),
359   _xpcom_factory: {
360     createInstance: function bch_factory_ci(outer, iid) {
361       if (outer)
362         throw Components.results.NS_ERROR_NO_AGGREGATION;
363       return gBrowserContentHandler.QueryInterface(iid);
364     }
365   },
367   /* helper functions */
369   mChromeURL : null,
371   get chromeURL() {
372     if (this.mChromeURL) {
373       return this.mChromeURL;
374     }
376     var prefb = Components.classes["@mozilla.org/preferences-service;1"]
377                           .getService(nsIPrefBranch);
378     this.mChromeURL = prefb.getCharPref("browser.chromeURL");
380     return this.mChromeURL;
381   },
383   /* nsISupports */
384   QueryInterface : XPCOMUtils.generateQI([nsICommandLineHandler,
385                                           nsIBrowserHandler,
386                                           nsIContentHandler,
387                                           nsICommandLineValidator]),
389   /* nsICommandLineHandler */
390   handle : function bch_handle(cmdLine) {
391     if (cmdLine.handleFlag("browser", false)) {
392       // Passing defaultArgs, so use NO_EXTERNAL_URIS
393       openWindow(null, this.chromeURL, "_blank",
394                  "chrome,dialog=no,all" + this.getFeatures(cmdLine),
395                  this.defaultArgs, NO_EXTERNAL_URIS);
396       cmdLine.preventDefault = true;
397     }
399     try {
400       var remoteCommand = cmdLine.handleFlagWithParam("remote", true);
401     }
402     catch (e) {
403       throw NS_ERROR_ABORT;
404     }
406     if (remoteCommand != null) {
407       try {
408         var a = /^\s*(\w+)\(([^\)]*)\)\s*$/.exec(remoteCommand);
409         var remoteVerb;
410         if (a) {
411           remoteVerb = a[1].toLowerCase();
412           var remoteParams = [];
413           var sepIndex = a[2].lastIndexOf(",");
414           if (sepIndex == -1)
415             remoteParams[0] = a[2];
416           else {
417             remoteParams[0] = a[2].substring(0, sepIndex);
418             remoteParams[1] = a[2].substring(sepIndex + 1);
419           }
420         }
422         switch (remoteVerb) {
423         case "openurl":
424         case "openfile":
425           // openURL(<url>)
426           // openURL(<url>,new-window)
427           // openURL(<url>,new-tab)
429           // First param is the URL, second param (if present) is the "target"
430           // (tab, window)
431           var url = remoteParams[0];
432           var target = nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW;
433           if (remoteParams[1]) {
434             var targetParam = remoteParams[1].toLowerCase()
435                                              .replace(/^\s*|\s*$/g, "");
436             if (targetParam == "new-tab")
437               target = nsIBrowserDOMWindow.OPEN_NEWTAB;
438             else if (targetParam == "new-window")
439               target = nsIBrowserDOMWindow.OPEN_NEWWINDOW;
440             else {
441               // The "target" param isn't one of our supported values, so
442               // assume it's part of a URL that contains commas.
443               url += "," + remoteParams[1];
444             }
445           }
447           var uri = resolveURIInternal(cmdLine, url);
448           handURIToExistingBrowser(uri, target, cmdLine);
449           break;
451         case "xfedocommand":
452           // xfeDoCommand(openBrowser)
453           if (remoteParams[0].toLowerCase() != "openbrowser")
454             throw NS_ERROR_ABORT;
456           // Passing defaultArgs, so use NO_EXTERNAL_URIS
457           openWindow(null, this.chromeURL, "_blank",
458                      "chrome,dialog=no,all" + this.getFeatures(cmdLine),
459                      this.defaultArgs, NO_EXTERNAL_URIS);
460           break;
462         default:
463           // Somebody sent us a remote command we don't know how to process:
464           // just abort.
465           throw "Unknown remote command.";
466         }
468         cmdLine.preventDefault = true;
469       }
470       catch (e) {
471         Components.utils.reportError(e);
472         // If we had a -remote flag but failed to process it, throw
473         // NS_ERROR_ABORT so that the xremote code knows to return a failure
474         // back to the handling code.
475         throw NS_ERROR_ABORT;
476       }
477     }
479     var uriparam;
480     try {
481       while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
482         var uri = resolveURIInternal(cmdLine, uriparam);
483         if (!shouldLoadURI(uri))
484           continue;
485         openWindow(null, this.chromeURL, "_blank",
486                    "chrome,dialog=no,all" + this.getFeatures(cmdLine),
487                    uri.spec);
488         cmdLine.preventDefault = true;
489       }
490     }
491     catch (e) {
492       Components.utils.reportError(e);
493     }
495     try {
496       while ((uriparam = cmdLine.handleFlagWithParam("new-tab", false))) {
497         var uri = resolveURIInternal(cmdLine, uriparam);
498         handURIToExistingBrowser(uri, nsIBrowserDOMWindow.OPEN_NEWTAB, cmdLine);
499         cmdLine.preventDefault = true;
500       }
501     }
502     catch (e) {
503       Components.utils.reportError(e);
504     }
506     var chromeParam = cmdLine.handleFlagWithParam("chrome", false);
507     if (chromeParam) {
509       // Handle the old preference dialog URL separately (bug 285416)
510       if (chromeParam == "chrome://browser/content/pref/pref.xul") {
511         openPreferences();
512         cmdLine.preventDefault = true;
513       } else try {
514         // only load URIs which do not inherit chrome privs
515         var features = "chrome,dialog=no,all" + this.getFeatures(cmdLine);
516         var uri = resolveURIInternal(cmdLine, chromeParam);
517         var netutil = Components.classes["@mozilla.org/network/util;1"]
518                                 .getService(nsINetUtil);
519         if (!netutil.URIChainHasFlags(uri, URI_INHERITS_SECURITY_CONTEXT)) {
520           openWindow(null, uri.spec, "_blank", features);
521           cmdLine.preventDefault = true;
522         }
523       }
524       catch (e) {
525         Components.utils.reportError(e);
526       }
527     }
528     if (cmdLine.handleFlag("preferences", false)) {
529       openPreferences();
530       cmdLine.preventDefault = true;
531     }
532     if (cmdLine.handleFlag("silent", false))
533       cmdLine.preventDefault = true;
534     if (cmdLine.findFlag("private-toggle", false) >= 0)
535       cmdLine.preventDefault = true;
537     var searchParam = cmdLine.handleFlagWithParam("search", false);
538     if (searchParam) {
539       doSearch(searchParam, cmdLine);
540       cmdLine.preventDefault = true;
541     }
543     var fileParam = cmdLine.handleFlagWithParam("file", false);
544     if (fileParam) {
545       var file = cmdLine.resolveFile(fileParam);
546       var ios = Components.classes["@mozilla.org/network/io-service;1"]
547                           .getService(Components.interfaces.nsIIOService);
548       var uri = ios.newFileURI(file);
549       openWindow(null, this.chromeURL, "_blank", 
550                  "chrome,dialog=no,all" + this.getFeatures(cmdLine),
551                  uri.spec);
552       cmdLine.preventDefault = true;
553     }
555 #ifdef XP_WIN
556     // Handle "? searchterm" for Windows Vista start menu integration
557     for (var i = cmdLine.length - 1; i >= 0; --i) {
558       var param = cmdLine.getArgument(i);
559       if (param.match(/^\? /)) {
560         cmdLine.removeArguments(i, i);
561         cmdLine.preventDefault = true;
563         searchParam = param.substr(2);
564         doSearch(searchParam, cmdLine);
565       }
566     }
567 #endif
568   },
570   helpInfo : "  -browser           Open a browser window.\n",
572   /* nsIBrowserHandler */
574   get defaultArgs() {
575     var prefb = Components.classes["@mozilla.org/preferences-service;1"]
576                           .getService(nsIPrefBranch);
578     var overridePage = "";
579     var haveUpdateSession = false;
580     try {
581       let override = needHomepageOverride(prefb);
582       if (override != OVERRIDE_NONE) {
583         // Setup the default search engine to about:home page.
584         AboutHomeUtils.loadDefaultSearchEngine();
585         AboutHomeUtils.loadSnippetsURL();
587         switch (override) {
588           case OVERRIDE_NEW_PROFILE:
589             // New profile.
590             overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
591             break;
592           case OVERRIDE_NEW_MSTONE:
593             // Existing profile, new milestone build.
594             copyPrefOverride();
596             // Check whether we have a session to restore. If we do, we assume
597             // that this is an "update" session.
598             var ss = Components.classes["@mozilla.org/browser/sessionstartup;1"]
599                                .getService(Components.interfaces.nsISessionStartup);
600             haveUpdateSession = ss.doRestore();
601             overridePage = Services.urlFormatter.formatURLPref("startup.homepage_override_url");
602             if (prefb.prefHasUserValue("app.update.postupdate"))
603               overridePage = getPostUpdateOverridePage(overridePage);
604             break;
605         }
606       }
607       else {
608         // No need to override homepage, but update snippets url if the pref has
609         // been manually changed.
610         if (Services.prefs.prefHasUserValue(AboutHomeUtils.SNIPPETS_URL_PREF)) {
611           AboutHomeUtils.loadSnippetsURL();
612         }
613       }
614     } catch (ex) {}
616     // formatURLPref might return "about:blank" if getting the pref fails
617     if (overridePage == "about:blank")
618       overridePage = "";
620     var startPage = "";
621     try {
622       var choice = prefb.getIntPref("browser.startup.page");
623       if (choice == 1 || choice == 3)
624         startPage = this.startPage;
626       if (choice == 2)
627         startPage = Components.classes["@mozilla.org/browser/global-history;2"]
628                               .getService(nsIBrowserHistory).lastPageVisited;
629     } catch (e) {
630       Components.utils.reportError(e);
631     }
633     if (startPage == "about:blank")
634       startPage = "";
636     // Only show the startPage if we're not restoring an update session.
637     if (overridePage && startPage && !haveUpdateSession)
638       return overridePage + "|" + startPage;
640     return overridePage || startPage || "about:blank";
641   },
643   get startPage() {
644     var prefb = Components.classes["@mozilla.org/preferences-service;1"]
645                           .getService(nsIPrefBranch);
647     var uri = prefb.getComplexValue("browser.startup.homepage",
648                                     nsIPrefLocalizedString).data;
650     if (!uri) {
651       prefb.clearUserPref("browser.startup.homepage");
652       uri = prefb.getComplexValue("browser.startup.homepage",
653                                   nsIPrefLocalizedString).data;
654     }
655                                 
656     var count;
657     try {
658       count = prefb.getIntPref("browser.startup.homepage.count");
659     }
660     catch (e) {
661       return uri;
662     }
664     for (var i = 1; i < count; ++i) {
665       try {
666         var page = prefb.getComplexValue("browser.startup.homepage." + i,
667                                          nsIPrefLocalizedString).data;
668         uri += "\n" + page;
669       }
670       catch (e) {
671       }
672     }
674     return uri;
675   },
677   mFeatures : null,
679   getFeatures : function bch_features(cmdLine) {
680     if (this.mFeatures === null) {
681       this.mFeatures = "";
683       try {
684         var width = cmdLine.handleFlagWithParam("width", false);
685         var height = cmdLine.handleFlagWithParam("height", false);
687         if (width)
688           this.mFeatures += ",width=" + width;
689         if (height)
690           this.mFeatures += ",height=" + height;
691       }
692       catch (e) {
693       }
694     }
696     return this.mFeatures;
697   },
699   /* nsIContentHandler */
701   handleContent : function bch_handleContent(contentType, context, request) {
702     try {
703       var webNavInfo = Components.classes["@mozilla.org/webnavigation-info;1"]
704                                  .getService(nsIWebNavigationInfo);
705       if (!webNavInfo.isTypeSupported(contentType, null)) {
706         throw NS_ERROR_WONT_HANDLE_CONTENT;
707       }
708     } catch (e) {
709       throw NS_ERROR_WONT_HANDLE_CONTENT;
710     }
712     request.QueryInterface(nsIChannel);
713     handURIToExistingBrowser(request.URI,
714       nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, null);
715     request.cancel(NS_BINDING_ABORTED);
716   },
718   /* nsICommandLineValidator */
719   validate : function bch_validate(cmdLine) {
720     // Other handlers may use osint so only handle the osint flag if the url
721     // flag is also present and the command line is valid.
722     var osintFlagIdx = cmdLine.findFlag("osint", false);
723     var urlFlagIdx = cmdLine.findFlag("url", false);
724     if (urlFlagIdx > -1 && (osintFlagIdx > -1 ||
725         cmdLine.state == nsICommandLine.STATE_REMOTE_EXPLICIT)) {
726       var urlParam = cmdLine.getArgument(urlFlagIdx + 1);
727       if (cmdLine.length != urlFlagIdx + 2 || /firefoxurl:/.test(urlParam))
728         throw NS_ERROR_ABORT;
729       cmdLine.handleFlag("osint", false)
730     }
731   },
733 var gBrowserContentHandler = new nsBrowserContentHandler();
735 const CONTRACTID_PREFIX = "@mozilla.org/uriloader/content-handler;1?type=";
737 function handURIToExistingBrowser(uri, location, cmdLine)
739   if (!shouldLoadURI(uri))
740     return;
742   var navWin = getMostRecentBrowserWindow();
743   if (!navWin) {
744     // if we couldn't load it in an existing window, open a new one
745     openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
746                "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
747                uri.spec);
748     return;
749   }
751   var navNav = navWin.QueryInterface(nsIInterfaceRequestor)
752                      .getInterface(nsIWebNavigation);
753   var rootItem = navNav.QueryInterface(nsIDocShellTreeItem).rootTreeItem;
754   var rootWin = rootItem.QueryInterface(nsIInterfaceRequestor)
755                         .getInterface(nsIDOMWindow);
756   var bwin = rootWin.QueryInterface(nsIDOMChromeWindow).browserDOMWindow;
757   bwin.openURI(uri, null, location,
758                nsIBrowserDOMWindow.OPEN_EXTERNAL);
761 function nsDefaultCommandLineHandler() {
764 nsDefaultCommandLineHandler.prototype = {
765   classID: Components.ID("{47cd0651-b1be-4a0f-b5c4-10e5a573ef71}"),
767   /* nsISupports */
768   QueryInterface : function dch_QI(iid) {
769     if (!iid.equals(nsISupports) &&
770         !iid.equals(nsICommandLineHandler))
771       throw Components.results.NS_ERROR_NO_INTERFACE;
773     return this;
774   },
776   // List of uri's that were passed via the command line without the app
777   // running and have already been handled. This is compared against uri's
778   // opened using DDE on Win32 so we only open one of the requests.
779   _handledURIs: [ ],
780 #ifdef XP_WIN
781   _haveProfile: false,
782 #endif
784   /* nsICommandLineHandler */
785   handle : function dch_handle(cmdLine) {
786     var urilist = [];
788 #ifdef XP_WIN
789     // If we don't have a profile selected yet (e.g. the Profile Manager is
790     // displayed) we will crash if we open an url and then select a profile. To
791     // prevent this handle all url command line flags and set the command line's
792     // preventDefault to true to prevent the display of the ui. The initial
793     // command line will be retained when nsAppRunner calls LaunchChild though
794     // urls launched after the initial launch will be lost.
795     if (!this._haveProfile) {
796       try {
797         // This will throw when a profile has not been selected.
798         var fl = Components.classes["@mozilla.org/file/directory_service;1"]
799                            .getService(Components.interfaces.nsIProperties);
800         var dir = fl.get("ProfD", Components.interfaces.nsILocalFile);
801         this._haveProfile = true;
802       }
803       catch (e) {
804         while ((ar = cmdLine.handleFlagWithParam("url", false))) { }
805         cmdLine.preventDefault = true;
806       }
807     }
808 #endif
810     try {
811       var ar;
812       while ((ar = cmdLine.handleFlagWithParam("url", false))) {
813         var found = false;
814         var uri = resolveURIInternal(cmdLine, ar);
815         // count will never be greater than zero except on Win32.
816         var count = this._handledURIs.length;
817         for (var i = 0; i < count; ++i) {
818           if (this._handledURIs[i].spec == uri.spec) {
819             this._handledURIs.splice(i, 1);
820             found = true;
821             cmdLine.preventDefault = true;
822             break;
823           }
824         }
825         if (!found) {
826           urilist.push(uri);
827           // The requestpending command line flag is only used on Win32.
828           if (cmdLine.handleFlag("requestpending", false) &&
829               cmdLine.state == nsICommandLine.STATE_INITIAL_LAUNCH)
830             this._handledURIs.push(uri)
831         }
832       }
833     }
834     catch (e) {
835       Components.utils.reportError(e);
836     }
838     count = cmdLine.length;
840     for (i = 0; i < count; ++i) {
841       var curarg = cmdLine.getArgument(i);
842       if (curarg.match(/^-/)) {
843         Components.utils.reportError("Warning: unrecognized command line flag " + curarg + "\n");
844         // To emulate the pre-nsICommandLine behavior, we ignore
845         // the argument after an unrecognized flag.
846         ++i;
847       } else {
848         try {
849           urilist.push(resolveURIInternal(cmdLine, curarg));
850         }
851         catch (e) {
852           Components.utils.reportError("Error opening URI '" + curarg + "' from the command line: " + e + "\n");
853         }
854       }
855     }
857     if (urilist.length) {
858       if (cmdLine.state != nsICommandLine.STATE_INITIAL_LAUNCH &&
859           urilist.length == 1) {
860         // Try to find an existing window and load our URI into the
861         // current tab, new tab, or new window as prefs determine.
862         try {
863           handURIToExistingBrowser(urilist[0], nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW, cmdLine);
864           return;
865         }
866         catch (e) {
867         }
868       }
870       var URLlist = urilist.filter(shouldLoadURI).map(function (u) u.spec);
871       if (URLlist.length) {
872         openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
873                    "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
874                    URLlist);
875       }
877     }
878     else if (!cmdLine.preventDefault) {
879       // Passing defaultArgs, so use NO_EXTERNAL_URIS
880       openWindow(null, gBrowserContentHandler.chromeURL, "_blank",
881                  "chrome,dialog=no,all" + gBrowserContentHandler.getFeatures(cmdLine),
882                  gBrowserContentHandler.defaultArgs, NO_EXTERNAL_URIS);
883     }
884   },
886   helpInfo : "",
889 let AboutHomeUtils = {
890   SNIPPETS_URL_PREF: "browser.aboutHomeSnippets.updateUrl",
891   get _storage() {
892     let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null);
893     let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"].
894                     getService(Components.interfaces.nsIScriptSecurityManager).
895                     getCodebasePrincipal(aboutHomeURI);
896     let dsm = Components.classes["@mozilla.org/dom/storagemanager;1"].
897               getService(Components.interfaces.nsIDOMStorageManager);
898     return dsm.getLocalStorageForPrincipal(principal, "");
899   },
901   loadDefaultSearchEngine: function AHU_loadDefaultSearchEngine()
902   {
903     let defaultEngine = Services.search.originalDefaultEngine;
904     let submission = defaultEngine.getSubmission("_searchTerms_");
905     if (submission.postData)
906       throw new Error("Home page does not support POST search engines.");
907     let engine = {
908       name: defaultEngine.name
909     , searchUrl: submission.uri.spec
910     }
911     this._storage.setItem("search-engine", JSON.stringify(engine));
912   },
914   loadSnippetsURL: function AHU_loadSnippetsURL()
915   {
916     const STARTPAGE_VERSION = 1;
917     let updateURL = Services.prefs
918                             .getCharPref(this.SNIPPETS_URL_PREF)
919                             .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
920     updateURL = Services.urlFormatter.formatURL(updateURL);
921     this._storage.setItem("snippets-update-url", updateURL);
922   },
925 var components = [nsBrowserContentHandler, nsDefaultCommandLineHandler];
926 var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);