Bug 518666 - Fix for bad nsBrowserGlue.js import. r=dao.
[mozilla-central.git] / browser / components / nsBrowserGlue.js
blob90ae96096893c3ad28df2311dddced2a6d606e5b
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 Browser Search Service.
16 # The Initial Developer of the Original Code is
17 # Giorgio Maone
18 # Portions created by the Initial Developer are Copyright (C) 2005
19 # the Initial Developer. All Rights Reserved.
21 # Contributor(s):
22 #   Giorgio Maone <g.maone@informaction.com>
23 #   Seth Spitzer <sspitzer@mozilla.com>
24 #   Asaf Romano <mano@mozilla.com>
25 #   Marco Bonardo <mak77@bonardo.net>
26 #   Dietrich Ayala <dietrich@mozilla.com>
27 #   Ehsan Akhgari <ehsan.akhgari@gmail.com>
28 #   Nils Maier <maierman@web.de>
30 # Alternatively, the contents of this file may be used under the terms of
31 # either the GNU General Public License Version 2 or later (the "GPL"), or
32 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 # in which case the provisions of the GPL or the LGPL are applicable instead
34 # of those above. If you wish to allow use of your version of this file only
35 # under the terms of either the GPL or the LGPL, and not to allow others to
36 # use your version of this file under the terms of the MPL, indicate your
37 # decision by deleting the provisions above and replace them with the notice
38 # and other provisions required by the GPL or the LGPL. If you do not delete
39 # the provisions above, a recipient may use your version of this file under
40 # the terms of any one of the MPL, the GPL or the LGPL.
42 # ***** END LICENSE BLOCK *****
44 const Ci = Components.interfaces;
45 const Cc = Components.classes;
46 const Cr = Components.results;
47 const Cu = Components.utils;
49 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
51 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
52 Cu.import("resource:///modules/distribution.js");
54 const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
55 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
56 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
58 // We try to backup bookmarks at idle times, to avoid doing that at shutdown.
59 // Number of idle seconds before trying to backup bookmarks.  15 minutes.
60 const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60;
61 // Minimum interval in milliseconds between backups.
62 const BOOKMARKS_BACKUP_INTERVAL = 86400 * 1000;
63 // Maximum number of backups to create.  Old ones will be purged.
64 const BOOKMARKS_BACKUP_MAX_BACKUPS = 10;
66 // Factory object
67 const BrowserGlueServiceFactory = {
68   _instance: null,
69   createInstance: function (outer, iid) 
70   {
71     if (outer != null)
72       throw Components.results.NS_ERROR_NO_AGGREGATION;
73     return this._instance == null ?
74       this._instance = new BrowserGlue() : this._instance;
75   }
78 // Constructor
80 function BrowserGlue() {
82   XPCOMUtils.defineLazyServiceGetter(this, "_prefs",
83                                      "@mozilla.org/preferences-service;1",
84                                      "nsIPrefBranch");
86   XPCOMUtils.defineLazyServiceGetter(this, "_bundleService",
87                                      "@mozilla.org/intl/stringbundle;1",
88                                      "nsIStringBundleService");
90   XPCOMUtils.defineLazyServiceGetter(this, "_idleService",
91                                      "@mozilla.org/widget/idleservice;1",
92                                      "nsIIdleService");
94   XPCOMUtils.defineLazyServiceGetter(this, "_observerService",
95                                      "@mozilla.org/observer-service;1",
96                                      "nsIObserverService");
98   XPCOMUtils.defineLazyGetter(this, "_distributionCustomizer", function() {
99                                 return new DistributionCustomizer();
100                               });
102   this._init();
105 #ifndef XP_MACOSX
106 # OS X has the concept of zero-window sessions and therefore ignores the
107 # browser-lastwindow-close-* topics.
108 #define OBSERVE_LASTWINDOW_CLOSE_TOPICS 1
109 #endif
111 BrowserGlue.prototype = {
112   
113   _saveSession: false,
114   _isIdleObserver: false,
115   _isPlacesInitObserver: false,
116   _isPlacesLockedObserver: false,
117   _isPlacesDatabaseLocked: false,
119   _setPrefToSaveSession: function()
120   {
121     this._prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
123     // This method can be called via [NSApplication terminate:] on Mac, which
124     // ends up causing prefs not to be flushed to disk, so we need to do that
125     // explicitly here. See bug 497652.
126     this._prefs.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
127   },
129   // nsIObserver implementation 
130   observe: function(subject, topic, data) 
131   {
132     switch(topic) {
133       case "xpcom-shutdown":
134         this._dispose();
135         break;
136       case "prefservice:after-app-defaults":
137         this._onAppDefaults();
138         break;
139       case "final-ui-startup":
140         this._onProfileStartup();
141         break;
142       case "sessionstore-windows-restored":
143         this._onBrowserStartup();
144         break;
145       case "browser:purge-session-history":
146         // reset the console service's error buffer
147         const cs = Cc["@mozilla.org/consoleservice;1"].
148                    getService(Ci.nsIConsoleService);
149         cs.logStringMessage(null); // clear the console (in case it's open)
150         cs.reset();
151         break;
152       case "quit-application-requested":
153         this._onQuitRequest(subject, data);
154         break;
155       case "quit-application-granted":
156         if (this._saveSession) {
157           this._setPrefToSaveSession();
158         }
159         // Everything that uses Places during shutdown should be here, since
160         // on quit-application Places database connection will be closed
161         // and history synchronization could fail.
162         this._onProfileShutdown();
163         break;
164 #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
165       case "browser-lastwindow-close-requested":
166         // The application is not actually quitting, but the last full browser
167         // window is about to be closed.
168         this._onQuitRequest(subject, "lastwindow");
169         break;
170       case "browser-lastwindow-close-granted":
171         if (this._saveSession)
172           this._setPrefToSaveSession();
173         break;
174 #endif
175       case "session-save":
176         this._setPrefToSaveSession();
177         subject.QueryInterface(Ci.nsISupportsPRBool);
178         subject.data = true;
179         break;
180       case "places-init-complete":
181         this._initPlaces();
182         this._observerService.removeObserver(this, "places-init-complete");
183         this._isPlacesInitObserver = false;
184         // no longer needed, since history was initialized completely.
185         this._observerService.removeObserver(this, "places-database-locked");
186         this._isPlacesLockedObserver = false;
188         // Now apply distribution customized bookmarks.
189         // This should always run after Places initialization.
190         this._distributionCustomizer.applyBookmarks();
191         break;
192       case "places-database-locked":
193         this._isPlacesDatabaseLocked = true;
194         // stop observing, so further attempts to load history service
195         // do not show the prompt.
196         this._observerService.removeObserver(this, "places-database-locked");
197         this._isPlacesLockedObserver = false;
198         break;
199       case "idle":
200         if (this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000)
201           this._backupBookmarks();
202         break;
203       case "distribution-customization-complete":
204         this._observerService
205             .removeObserver(this, "distribution-customization-complete");
206         // Customization has finished, we don't need the customizer anymore.
207         delete this._distributionCustomizer;
208         break;
209     }
210   }, 
212   // initialization (called on application startup) 
213   _init: function() 
214   {
215     // observer registration
216     const osvr = this._observerService;
217     osvr.addObserver(this, "xpcom-shutdown", false);
218     osvr.addObserver(this, "prefservice:after-app-defaults", false);
219     osvr.addObserver(this, "final-ui-startup", false);
220     osvr.addObserver(this, "sessionstore-windows-restored", false);
221     osvr.addObserver(this, "browser:purge-session-history", false);
222     osvr.addObserver(this, "quit-application-requested", false);
223     osvr.addObserver(this, "quit-application-granted", false);
224 #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
225     osvr.addObserver(this, "browser-lastwindow-close-requested", false);
226     osvr.addObserver(this, "browser-lastwindow-close-granted", false);
227 #endif
228     osvr.addObserver(this, "session-save", false);
229     osvr.addObserver(this, "places-init-complete", false);
230     this._isPlacesInitObserver = true;
231     osvr.addObserver(this, "places-database-locked", false);
232     this._isPlacesLockedObserver = true;
233     osvr.addObserver(this, "distribution-customization-complete", false);
234   },
236   // cleanup (called on application shutdown)
237   _dispose: function() 
238   {
239     // observer removal 
240     const osvr = this._observerService;
241     osvr.removeObserver(this, "xpcom-shutdown");
242     osvr.removeObserver(this, "prefservice:after-app-defaults");
243     osvr.removeObserver(this, "final-ui-startup");
244     osvr.removeObserver(this, "sessionstore-windows-restored");
245     osvr.removeObserver(this, "browser:purge-session-history");
246     osvr.removeObserver(this, "quit-application-requested");
247     osvr.removeObserver(this, "quit-application-granted");
248 #ifdef OBSERVE_LASTWINDOW_CLOSE_TOPICS
249     osvr.removeObserver(this, "browser-lastwindow-close-requested");
250     osvr.removeObserver(this, "browser-lastwindow-close-granted");
251 #endif
252     osvr.removeObserver(this, "session-save");
253     if (this._isIdleObserver)
254       this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
255     if (this._isPlacesInitObserver)
256       osvr.removeObserver(this, "places-init-complete");
257     if (this._isPlacesLockedObserver)
258       osvr.removeObserver(this, "places-database-locked");
259   },
261   _onAppDefaults: function()
262   {
263     // apply distribution customizations (prefs)
264     // other customizations are applied in _onProfileStartup()
265     this._distributionCustomizer.applyPrefDefaults();
266   },
268   // profile startup handler (contains profile initialization routines)
269   _onProfileStartup: function() 
270   {
271     this.Sanitizer.onStartup();
272     // check if we're in safe mode
273     var app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
274               QueryInterface(Ci.nsIXULRuntime);
275     if (app.inSafeMode) {
276       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
277                getService(Ci.nsIWindowWatcher);
278       ww.openWindow(null, "chrome://browser/content/safeMode.xul", 
279                     "_blank", "chrome,centerscreen,modal,resizable=no", null);
280     }
282     // apply distribution customizations
283     // prefs are applied in _onAppDefaults()
284     this._distributionCustomizer.applyCustomizations();
286     // handle any UI migration
287     this._migrateUI();
289     var ioService = Cc["@mozilla.org/network/io-service;1"].
290                     getService(Ci.nsIIOService2);
292     // if ioService is managing the offline status, then ioservice.offline
293     // is already set correctly. We will continue to allow the ioService
294     // to manage its offline state until the user uses the "Work Offline" UI.
295     if (!ioService.manageOfflineStatus) {
296       // set the initial state
297       try {
298         ioService.offline = this._prefs.getBoolPref("browser.offline");
299       }
300       catch (e) {
301         ioService.offline = false;
302       }
303     }
305     this._observerService.notifyObservers(null, "browser-ui-startup-complete", "");
306   },
308   // profile shutdown handler (contains profile cleanup routines)
309   _onProfileShutdown: function() 
310   {
311     this._shutdownPlaces();
312     this._idleService.removeIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
313     this._isIdleObserver = false;
314     this.Sanitizer.onShutdown();
315   },
317   // Browser startup complete. All initial windows have opened.
318   _onBrowserStartup: function()
319   {
320     // Show about:rights notification, if needed.
321     if (this._shouldShowRights())
322       this._showRightsNotification();
324     // If new add-ons were installed during startup open the add-ons manager.
325     if (this._prefs.prefHasUserValue(PREF_EM_NEW_ADDONS_LIST)) {
326       var args = Cc["@mozilla.org/supports-array;1"].
327                  createInstance(Ci.nsISupportsArray);
328       var str = Cc["@mozilla.org/supports-string;1"].
329                 createInstance(Ci.nsISupportsString);
330       str.data = "";
331       args.AppendElement(str);
332       var str = Cc["@mozilla.org/supports-string;1"].
333                 createInstance(Ci.nsISupportsString);
334       str.data = this._prefs.getCharPref(PREF_EM_NEW_ADDONS_LIST);
335       args.AppendElement(str);
336       const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
337       const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
338       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
339                getService(Ci.nsIWindowWatcher);
340       ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
341       this._prefs.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
342     }
344     // Load the "more info" page for a locked places.sqlite
345     // This property is set earlier in the startup process:
346     // nsPlacesDBFlush loads after profile-after-change and initializes
347     // the history service, which sends out places-database-locked
348     // which sets this property.
349     if (this._isPlacesDatabaseLocked) {
350       this._showPlacesLockedNotificationBox();
351     }
353     // If there are plugins installed that are outdated, and the user hasn't
354     // been warned about them yet, open the plugins update page.
355     if (this._prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
356       this._showPluginUpdatePage();
358 #ifdef XP_WIN
359 #ifndef WINCE
360     // For windows seven, initialize the jump list module.
361     let temp = {};
362     Cu.import("resource://gre/modules/wintaskbar/jumpLists.jsm", temp);
363     temp.WinTaskbarJumpList.startup();
364 #endif
365 #endif
366   },
368   _onQuitRequest: function(aCancelQuit, aQuitType)
369   {
370     // If user has already dismissed quit request, then do nothing
371     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
372       return;
374     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
375              getService(Ci.nsIWindowMediator);
377     var windowcount = 0;
378     var pagecount = 0;
379     var browserEnum = wm.getEnumerator("navigator:browser");
380     while (browserEnum.hasMoreElements()) {
381       windowcount++;
383       var browser = browserEnum.getNext();
384       var tabbrowser = browser.document.getElementById("content");
385       if (tabbrowser)
386         pagecount += tabbrowser.browsers.length;
387     }
389     this._saveSession = false;
390     if (pagecount < 2)
391       return;
393     if (aQuitType != "restart")
394       aQuitType = "quit";
396     var showPrompt = true;
397     try {
398       // browser.warnOnQuit is a hidden global boolean to override all quit prompts
399       // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
400       // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
402       var sessionWillBeSaved = this._prefs.getIntPref("browser.startup.page") == 3 ||
403                                this._prefs.getBoolPref("browser.sessionstore.resume_session_once");
404       if (sessionWillBeSaved || !this._prefs.getBoolPref("browser.warnOnQuit"))
405         showPrompt = false;
406       else if (aQuitType == "restart")
407         showPrompt = this._prefs.getBoolPref("browser.warnOnRestart");
408       else
409         showPrompt = this._prefs.getBoolPref("browser.tabs.warnOnClose");
410     } catch (ex) {}
412     // Never show a prompt inside the private browsing mode
413     var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
414                             getService(Ci.nsIPrivateBrowsingService).
415                             privateBrowsingEnabled;
416     if (!showPrompt || inPrivateBrowsing)
417       return false;
419     var quitBundle = this._bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
420     var brandBundle = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
422     var appName = brandBundle.GetStringFromName("brandShortName");
423     var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
424                                                           [appName], 1);
426     var message;
427     if (aQuitType == "restart")
428       message = quitBundle.formatStringFromName("messageRestart",
429                                                 [appName], 1);
430     else if (windowcount == 1)
431       message = quitBundle.formatStringFromName("messageNoWindows",
432                                                 [appName], 1);
433     else
434       message = quitBundle.formatStringFromName("message",
435                                                 [appName], 1);
437     var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
438                         getService(Ci.nsIPromptService);
440     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
441                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
442                 promptService.BUTTON_POS_0_DEFAULT;
444     var neverAsk = {value:false};
445     var button0Title, button2Title;
446     var button1Title = quitBundle.GetStringFromName("cancelTitle");
447     var neverAskText = quitBundle.GetStringFromName("neverAsk");
449     if (aQuitType == "restart")
450       button0Title = quitBundle.GetStringFromName("restartTitle");
451     else {
452       flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
453       button0Title = quitBundle.GetStringFromName("saveTitle");
454       button2Title = quitBundle.GetStringFromName("quitTitle");
455     }
457     var mostRecentBrowserWindow = wm.getMostRecentWindow("navigator:browser");
458     var buttonChoice =
459       promptService.confirmEx(mostRecentBrowserWindow, quitDialogTitle, message,
460                               flags, button0Title, button1Title, button2Title,
461                               neverAskText, neverAsk);
463     switch (buttonChoice) {
464     case 2: // Quit
465       if (neverAsk.value)
466         this._prefs.setBoolPref("browser.tabs.warnOnClose", false);
467       break;
468     case 1: // Cancel
469       aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
470       aCancelQuit.data = true;
471       break;
472     case 0: // Save & Quit
473       this._saveSession = true;
474       if (neverAsk.value) {
475         if (aQuitType == "restart")
476           this._prefs.setBoolPref("browser.warnOnRestart", false);
477         else {
478           // always save state when shutting down
479           this._prefs.setIntPref("browser.startup.page", 3);
480         }
481       }
482       break;
483     }
484   },
486   /*
487    * _shouldShowRights - Determines if the user should be shown the
488    * about:rights notification. The notification should *not* be shown if
489    * we've already shown the current version, or if the override pref says to
490    * never show it. The notification *should* be shown if it's never been seen
491    * before, if a newer version is available, or if the override pref says to
492    * always show it.
493    */
494   _shouldShowRights : function () {
495     // Look for an unconditional override pref. If set, do what it says.
496     // (true --> never show, false --> always show)
497     try {
498       return !this._prefs.getBoolPref("browser.rights.override");
499     } catch (e) { }
500     // Ditto, for the legacy EULA pref.
501     try {
502       return !this._prefs.getBoolPref("browser.EULA.override");
503     } catch (e) { }
505 #ifndef OFFICIAL_BUILD
506     // Non-official builds shouldn't shouldn't show the notification.
507     return false;
508 #endif
510     // Look to see if the user has seen the current version or not.
511     var currentVersion = this._prefs.getIntPref("browser.rights.version");
512     try {
513       return !this._prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
514     } catch (e) { }
516     // Legacy: If the user accepted a EULA, we won't annoy them with the
517     // equivalent about:rights page until the version changes.
518     try {
519       return !this._prefs.getBoolPref("browser.EULA." + currentVersion + ".accepted");
520     } catch (e) { }
522     // We haven't shown the notification before, so do so now.
523     return true;
524   },
526   _showRightsNotification : function () {
527     // Stick the notification onto the selected tab of the active browser window.
528     var win = this.getMostRecentBrowserWindow();
529     var browser = win.gBrowser; // for closure in notification bar callback
530     var notifyBox = browser.getNotificationBox();
532     var brandBundle  = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
533     var rightsBundle = this._bundleService.createBundle("chrome://global/locale/aboutRights.properties");
535     var buttonLabel      = rightsBundle.GetStringFromName("buttonLabel");
536     var buttonAccessKey  = rightsBundle.GetStringFromName("buttonAccessKey");
537     var productName      = brandBundle.GetStringFromName("brandFullName");
538     var notifyRightsText = rightsBundle.formatStringFromName("notifyRightsText", [productName], 1);
539     
540     var buttons = [
541                     {
542                       label:     buttonLabel,
543                       accessKey: buttonAccessKey,
544                       popup:     null,
545                       callback: function(aNotificationBar, aButton) {
546                         browser.selectedTab = browser.addTab("about:rights");
547                       }
548                     }
549                   ];
551     // Set pref to indicate we've shown the notification.
552     var currentVersion = this._prefs.getIntPref("browser.rights.version");
553     this._prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
555     var box = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
556     box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
557   },
558   
559   _showPluginUpdatePage : function () {
560     this._prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);
562     var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
563                     getService(Ci.nsIURLFormatter);
564     var updateUrl = formatter.formatURLPref(PREF_PLUGINS_UPDATEURL);
566     var win = this.getMostRecentBrowserWindow();
567     var browser = win.gBrowser;
568     browser.selectedTab = browser.addTab(updateUrl);
569   },
571   // returns the (cached) Sanitizer constructor
572   get Sanitizer() 
573   {
574     if(typeof(Sanitizer) != "function") { // we should dynamically load the script
575       Cc["@mozilla.org/moz/jssubscript-loader;1"].
576       getService(Ci.mozIJSSubScriptLoader).
577       loadSubScript("chrome://browser/content/sanitize.js", null);
578     }
579     return Sanitizer;
580   },
582   /**
583    * Initialize Places
584    * - imports the bookmarks html file if bookmarks database is empty, try to
585    *   restore bookmarks from a JSON backup if the backend indicates that the
586    *   database was corrupt.
587    *
588    * These prefs can be set up by the frontend:
589    *
590    * WARNING: setting these preferences to true will overwite existing bookmarks
591    *
592    * - browser.places.importBookmarksHTML
593    *   Set to true will import the bookmarks.html file from the profile folder.
594    * - browser.places.smartBookmarksVersion
595    *   Set during HTML import to indicate that Smart Bookmarks were created.
596    *   Set to -1 to disable Smart Bookmarks creation.
597    *   Set to 0 to restore current Smart Bookmarks.
598    * - browser.bookmarks.restore_default_bookmarks
599    *   Set to true by safe-mode dialog to indicate we must restore default
600    *   bookmarks.
601    */
602   _initPlaces: function bg__initPlaces() {
603     // We must instantiate the history service since it will tell us if we
604     // need to import or restore bookmarks due to first-run, corruption or
605     // forced migration (due to a major schema change).
606     var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
607                   getService(Ci.nsINavHistoryService);
609     // If the database is corrupt or has been newly created we should
610     // import bookmarks.
611     var databaseStatus = histsvc.databaseStatus;
612     var importBookmarks = databaseStatus == histsvc.DATABASE_STATUS_CREATE ||
613                           databaseStatus == histsvc.DATABASE_STATUS_CORRUPT;
615     if (databaseStatus == histsvc.DATABASE_STATUS_CREATE) {
616       // If the database has just been created, but we already have any
617       // bookmark, this is not the initial import.  This can happen after a
618       // migration from a different browser since migrators run before us.
619       // In such a case we should not import, unless some pref has been set.
620       var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
621                   getService(Ci.nsINavBookmarksService);
622       if (bmsvc.getIdForItemAt(bmsvc.bookmarksMenuFolder, 0) != -1 ||
623           bmsvc.getIdForItemAt(bmsvc.toolbarFolder, 0) != -1)
624         importBookmarks = false;
625     }
627     // Check if user or an extension has required to import bookmarks.html
628     var importBookmarksHTML = false;
629     try {
630       importBookmarksHTML =
631         this._prefs.getBoolPref("browser.places.importBookmarksHTML");
632       if (importBookmarksHTML)
633         importBookmarks = true;
634     } catch(ex) {}
636     // Check if Safe Mode or the user has required to restore bookmarks from
637     // default profile's bookmarks.html
638     var restoreDefaultBookmarks = false;
639     try {
640       restoreDefaultBookmarks =
641         this._prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
642       if (restoreDefaultBookmarks) {
643         // Ensure that we already have a bookmarks backup for today.
644         this._backupBookmarks();
645         importBookmarks = true;
646       }
647     } catch(ex) {}
649     // If the user did not require to restore default bookmarks, or import
650     // from bookmarks.html, we will try to restore from JSON
651     if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
652       // get latest JSON backup
653       Cu.import("resource://gre/modules/utils.js");
654       var bookmarksBackupFile = PlacesUtils.backups.getMostRecent("json");
655       if (bookmarksBackupFile) {
656         // restore from JSON backup
657         PlacesUtils.restoreBookmarksFromJSONFile(bookmarksBackupFile);
658         importBookmarks = false;
659       }
660       else {
661         // We have created a new database but we don't have any backup available
662         importBookmarks = true;
663         var dirService = Cc["@mozilla.org/file/directory_service;1"].
664                          getService(Ci.nsIProperties);
665         var bookmarksHTMLFile = dirService.get("BMarks", Ci.nsILocalFile);
666         if (bookmarksHTMLFile.exists()) {
667           // If bookmarks.html is available in current profile import it...
668           importBookmarksHTML = true;
669         }
670         else {
671           // ...otherwise we will restore defaults
672           restoreDefaultBookmarks = true;
673         }
674       }
675     }
677     if (!importBookmarks) {
678       // Call it here for Fx3 profiles created before the Places folder
679       // has been added, otherwise it's called during import.
680       this.ensurePlacesDefaultQueriesInitialized();
681     }
682     else {
683       // ensurePlacesDefaultQueriesInitialized() is called by import.
684       // Don't try to recreate smart bookmarks if autoExportHTML is true or
685       // smart bookmarks are disabled.
686       var autoExportHTML = false;
687       try {
688         autoExportHTML = this._prefs.getBoolPref("browser.bookmarks.autoExportHTML");
689       } catch(ex) {}
690       var smartBookmarksVersion = 0;
691       try {
692         smartBookmarksVersion = this._prefs.getIntPref("browser.places.smartBookmarksVersion");
693       } catch(ex) {}
694       if (!autoExportHTML && smartBookmarksVersion != -1)
695         this._prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
697       // Get bookmarks.html file location
698       var dirService = Cc["@mozilla.org/file/directory_service;1"].
699                        getService(Ci.nsIProperties);
701       var bookmarksFile = null;
702       if (restoreDefaultBookmarks) {
703         // User wants to restore bookmarks.html file from default profile folder
704         bookmarksFile = dirService.get("profDef", Ci.nsILocalFile);
705         bookmarksFile.append("bookmarks.html");
706       }
707       else
708         bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
710       if (bookmarksFile.exists()) {
711         // import the file
712         try {
713           var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
714                          getService(Ci.nsIPlacesImportExportService);
715           importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */);
716         } catch (err) {
717           // Report the error, but ignore it.
718           Cu.reportError("Bookmarks.html file could be corrupt. " + err);
719         }
720       }
721       else
722         Cu.reportError("Unable to find bookmarks.html file.");
724       // Reset preferences, so we won't try to import again at next run
725       if (importBookmarksHTML)
726         this._prefs.setBoolPref("browser.places.importBookmarksHTML", false);
727       if (restoreDefaultBookmarks)
728         this._prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
729                                 false);
730     }
732     // Initialize bookmark archiving on idle.
733     // Once a day, either on idle or shutdown, bookmarks are backed up.
734     if (!this._isIdleObserver) {
735       this._idleService.addIdleObserver(this, BOOKMARKS_BACKUP_IDLE_TIME);
736       this._isIdleObserver = true;
737     }
738   },
740   /**
741    * Places shut-down tasks
742    * - back up bookmarks if needed.
743    * - export bookmarks as HTML, if so configured.
744    *
745    * Note: quit-application-granted notification is received twice
746    *       so replace this method with a no-op when first called.
747    */
748   _shutdownPlaces: function bg__shutdownPlaces() {
749     this._backupBookmarks();
751     // Backup bookmarks to bookmarks.html to support apps that depend
752     // on the legacy format.
753     var autoExportHTML = false;
754     try {
755       autoExportHTML = this._prefs.getBoolPref("browser.bookmarks.autoExportHTML");
756     } catch(ex) { /* Don't export */ }
758     if (autoExportHTML) {
759       Cc["@mozilla.org/browser/places/import-export-service;1"].
760         getService(Ci.nsIPlacesImportExportService).
761         backupBookmarksFile();
762     }
763   },
765   /**
766    * Backup bookmarks if needed.
767    */
768   _backupBookmarks: function nsBrowserGlue__backupBookmarks() {
769     Cu.import("resource://gre/modules/utils.js");
771     let lastBackupFile = PlacesUtils.backups.getMostRecent();
773     // Backup bookmarks if there are no backups or the maximum interval between
774     // backups elapsed.
775     if (!lastBackupFile ||
776         new Date() - PlacesUtils.backups.getDateForFile(lastBackupFile) > BOOKMARKS_BACKUP_INTERVAL) {
777       let maxBackups = BOOKMARKS_BACKUP_MAX_BACKUPS;
778       try {
779         maxBackups = this._prefs.getIntPref("browser.bookmarks.max_backups");
780       }
781       catch(ex) { /* Use default. */ }
783       PlacesUtils.backups.create(maxBackups); // Don't force creation.
784     }
785   },
787   /**
788    * Show the notificationBox for a locked places database.
789    */
790   _showPlacesLockedNotificationBox: function nsBrowserGlue__showPlacesLockedNotificationBox() {
791     var brandBundle  = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
792     var applicationName = brandBundle.GetStringFromName("brandShortName");
793     var placesBundle = this._bundleService.createBundle("chrome://browser/locale/places/places.properties");
794     var title = placesBundle.GetStringFromName("lockPrompt.title");
795     var text = placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1);
796     var buttonText = placesBundle.GetStringFromName("lockPromptInfoButton.label");
797     var accessKey = placesBundle.GetStringFromName("lockPromptInfoButton.accessKey");
799     var helpTopic = "places-locked";
800     var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
801               getService(Components.interfaces.nsIURLFormatter).
802               formatURLPref("app.support.baseURL");
803     url += helpTopic;
805     var browser = this.getMostRecentBrowserWindow().gBrowser;
807     var buttons = [
808                     {
809                       label:     buttonText,
810                       accessKey: accessKey,
811                       popup:     null,
812                       callback:  function(aNotificationBar, aButton) {
813                         browser.selectedTab = browser.addTab(url);
814                       }
815                     }
816                   ];
818     var notifyBox = browser.getNotificationBox();
819     var box = notifyBox.appendNotification(text, title, null,
820                                            notifyBox.PRIORITY_CRITICAL_MEDIUM,
821                                            buttons);
822     box.persistence = -1; // Until user closes it
823   },
825   _migrateUI: function bg__migrateUI() {
826     var migration = 0;
827     try {
828       migration = this._prefs.getIntPref("browser.migration.version");
829     } catch(ex) {}
831     if (migration == 0) {
832       // this code should always migrate pre-FF3 profiles to the current UI state
834       // grab the localstore.rdf and make changes needed for new UI
835       this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
836       this._dataSource = this._rdf.GetDataSource("rdf:local-store");
837       this._dirty = false;
839       let currentsetResource = this._rdf.GetResource("currentset");
840       let toolbars = ["nav-bar", "toolbar-menubar", "PersonalToolbar"];
841       for (let i = 0; i < toolbars.length; i++) {
842         let toolbar = this._rdf.GetResource("chrome://browser/content/browser.xul#" + toolbars[i]);
843         let currentset = this._getPersist(toolbar, currentsetResource);
844         if (!currentset) {
845           // toolbar isn't customized
846           if (i == 0)
847             // new button is in the defaultset, nothing to migrate
848             break;
849           continue;
850         }
851         if (/(?:^|,)unified-back-forward-button(?:$|,)/.test(currentset))
852           // new button is already there, nothing to migrate
853           break;
854         if (/(?:^|,)back-button(?:$|,)/.test(currentset)) {
855           let newset = currentset.replace(/(^|,)back-button($|,)/,
856                                           "$1unified-back-forward-button,back-button$2")
857           this._setPersist(toolbar, currentsetResource, newset);
858           // done migrating
859           break;
860         }
861       }
863       // force the RDF to be saved
864       if (this._dirty)
865         this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
867       // free up the RDF service
868       this._rdf = null;
869       this._dataSource = null;
871       // update the migration version
872       this._prefs.setIntPref("browser.migration.version", 1);
873     }
874   },
876   _getPersist: function bg__getPersist(aSource, aProperty) {
877     var target = this._dataSource.GetTarget(aSource, aProperty, true);
878     if (target instanceof Ci.nsIRDFLiteral)
879       return target.Value;
880     return null;
881   },
883   _setPersist: function bg__setPersist(aSource, aProperty, aTarget) {
884     this._dirty = true;
885     try {
886       var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
887       if (oldTarget) {
888         if (aTarget)
889           this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
890         else
891           this._dataSource.Unassert(aSource, aProperty, oldTarget);
892       }
893       else {
894         this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
895       }
896     }
897     catch(ex) {}
898   },
900   // ------------------------------
901   // public nsIBrowserGlue members
902   // ------------------------------
903   
904   sanitize: function(aParentWindow) 
905   {
906     this.Sanitizer.sanitize(aParentWindow);
907   },
909   ensurePlacesDefaultQueriesInitialized: function() {
910     // This is actual version of the smart bookmarks, must be increased every
911     // time smart bookmarks change.
912     // When adding a new smart bookmark below, its newInVersion property must
913     // be set to the version it has been added in, we will compare its value
914     // to users' smartBookmarksVersion and add new smart bookmarks without
915     // recreating old deleted ones.
916     const SMART_BOOKMARKS_VERSION = 2;
917     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
918     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
920     // XXX should this be a pref?  see bug #399268
921     const MAX_RESULTS = 10;
923     // get current smart bookmarks version
924     // By default, if the pref is not set up, we must create Smart Bookmarks
925     var smartBookmarksCurrentVersion = 0;
926     try {
927       smartBookmarksCurrentVersion = this._prefs.getIntPref(SMART_BOOKMARKS_PREF);
928     } catch(ex) { /* no version set, new profile */ }
930     // bail out if we don't have to create or update Smart Bookmarks
931     if (smartBookmarksCurrentVersion == -1 ||
932         smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION)
933       return;
935     var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
936                 getService(Ci.nsINavBookmarksService);
937     var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
938                   getService(Ci.nsIAnnotationService);
940     var callback = {
941       _uri: function(aSpec) {
942         return Cc["@mozilla.org/network/io-service;1"].
943                getService(Ci.nsIIOService).
944                newURI(aSpec, null, null);
945       },
947       runBatched: function() {
948         var smartBookmarks = [];
949         var bookmarksMenuIndex = 0;
950         var bookmarksToolbarIndex = 0;
952         var placesBundle = Cc["@mozilla.org/intl/stringbundle;1"].
953                            getService(Ci.nsIStringBundleService).
954                            createBundle("chrome://browser/locale/places/places.properties");
956         // MOST VISITED
957         var smart = {queryId: "MostVisited", // don't change this
958                      itemId: null,
959                      title: placesBundle.GetStringFromName("mostVisitedTitle"),
960                      uri: this._uri("place:redirectsMode=" +
961                                     Ci.nsINavHistoryQueryOptions.REDIRECTS_MODE_TARGET +
962                                     "&sort=" +
963                                     Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
964                                     "&maxResults=" + MAX_RESULTS),
965                      parent: bmsvc.toolbarFolder,
966                      position: bookmarksToolbarIndex++,
967                      newInVersion: 1 };
968         smartBookmarks.push(smart);
970         // RECENTLY BOOKMARKED
971         smart = {queryId: "RecentlyBookmarked", // don't change this
972                  itemId: null,
973                  title: placesBundle.GetStringFromName("recentlyBookmarkedTitle"),
974                  uri: this._uri("place:folder=BOOKMARKS_MENU" +
975                                 "&folder=UNFILED_BOOKMARKS" +
976                                 "&folder=TOOLBAR" +
977                                 "&queryType=" +
978                                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
979                                 "&sort=" +
980                                 Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
981                                 "&excludeItemIfParentHasAnnotation=livemark%2FfeedURI" +
982                                 "&maxResults=" + MAX_RESULTS +
983                                 "&excludeQueries=1"),
984                  parent: bmsvc.bookmarksMenuFolder,
985                  position: bookmarksMenuIndex++,
986                  newInVersion: 1 };
987         smartBookmarks.push(smart);
989         // RECENT TAGS
990         smart = {queryId: "RecentTags", // don't change this
991                  itemId: null,
992                  title: placesBundle.GetStringFromName("recentTagsTitle"),
993                  uri: this._uri("place:"+
994                     "type=" +
995                     Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
996                     "&sort=" +
997                     Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
998                     "&maxResults=" + MAX_RESULTS),
999                  parent: bmsvc.bookmarksMenuFolder,
1000                  position: bookmarksMenuIndex++,
1001                  newInVersion: 1 };
1002         smartBookmarks.push(smart);
1004         var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, {});
1005         // Set current itemId, parent and position if Smart Bookmark exists,
1006         // we will use these informations to create the new version at the same
1007         // position.
1008         for each(var itemId in smartBookmarkItemIds) {
1009           var queryId = annosvc.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
1010           for (var i = 0; i < smartBookmarks.length; i++){
1011             if (smartBookmarks[i].queryId == queryId) {
1012               smartBookmarks[i].found = true;
1013               smartBookmarks[i].itemId = itemId;
1014               smartBookmarks[i].parent = bmsvc.getFolderIdForItem(itemId);
1015               smartBookmarks[i].position = bmsvc.getItemIndex(itemId);
1016               // remove current item, since it will be replaced
1017               bmsvc.removeItem(itemId);
1018               break;
1019             }
1020             // We don't remove old Smart Bookmarks because user could still
1021             // find them useful, or could have personalized them.
1022             // Instead we remove the Smart Bookmark annotation.
1023             if (i == smartBookmarks.length - 1)
1024               annosvc.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
1025           }
1026         }
1028         // create smart bookmarks
1029         for each(var smartBookmark in smartBookmarks) {
1030           // We update or create only changed or new smart bookmarks.
1031           // Also we respect user choices, so we won't try to create a smart
1032           // bookmark if it has been removed.
1033           if (smartBookmarksCurrentVersion > 0 &&
1034               smartBookmark.newInVersion <= smartBookmarksCurrentVersion &&
1035               !smartBookmark.found)
1036             continue;
1038           smartBookmark.itemId = bmsvc.insertBookmark(smartBookmark.parent,
1039                                                       smartBookmark.uri,
1040                                                       smartBookmark.position,
1041                                                       smartBookmark.title);
1042           annosvc.setItemAnnotation(smartBookmark.itemId,
1043                                     SMART_BOOKMARKS_ANNO, smartBookmark.queryId,
1044                                     0, annosvc.EXPIRE_NEVER);
1045         }
1046         
1047         // If we are creating all Smart Bookmarks from ground up, add a
1048         // separator below them in the bookmarks menu.
1049         if (smartBookmarksCurrentVersion == 0 &&
1050             smartBookmarkItemIds.length == 0)
1051           bmsvc.insertSeparator(bmsvc.bookmarksMenuFolder, bookmarksMenuIndex);
1052       }
1053     };
1055     try {
1056       bmsvc.runInBatchMode(callback, null);
1057     }
1058     catch(ex) {
1059       Components.utils.reportError(ex);
1060     }
1061     finally {
1062       this._prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
1063       this._prefs.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
1064     }
1065   },
1067 #ifndef XP_WIN
1068 #define BROKEN_WM_Z_ORDER
1069 #endif
1071   // this returns the most recent non-popup browser window
1072   getMostRecentBrowserWindow : function ()
1073   {
1074     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
1075              getService(Components.interfaces.nsIWindowMediator);
1077 #ifdef BROKEN_WM_Z_ORDER
1078     var win = wm.getMostRecentWindow("navigator:browser", true);
1080     // if we're lucky, this isn't a popup, and we can just return this
1081     if (win && win.document.documentElement.getAttribute("chromehidden")) {
1082       win = null;
1083       var windowList = wm.getEnumerator("navigator:browser", true);
1084       // this is oldest to newest, so this gets a bit ugly
1085       while (windowList.hasMoreElements()) {
1086         var nextWin = windowList.getNext();
1087         if (!nextWin.document.documentElement.getAttribute("chromehidden"))
1088           win = nextWin;
1089       }
1090     }
1091 #else
1092     var windowList = wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
1093     if (!windowList.hasMoreElements())
1094       return null;
1096     var win = windowList.getNext();
1097     while (win.document.documentElement.getAttribute("chromehidden")) {
1098       if (!windowList.hasMoreElements())
1099         return null;
1101       win = windowList.getNext();
1102     }
1103 #endif
1105     return win;
1106   },
1109   // for XPCOM
1110   classDescription: "Firefox Browser Glue Service",
1111   classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),
1112   contractID:       "@mozilla.org/browser/browserglue;1",
1114   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
1115                                          Ci.nsISupportsWeakReference,
1116                                          Ci.nsIBrowserGlue]),
1118   // redefine the default factory for XPCOMUtils
1119   _xpcom_factory: BrowserGlueServiceFactory,
1121   // get this contractID registered for certain categories via XPCOMUtils
1122   _xpcom_categories: [
1123     // make BrowserGlue a startup observer
1124     { category: "app-startup", service: true }
1125   ]
1128 function GeolocationPrompt() {}
1130 GeolocationPrompt.prototype = {
1131   classDescription: "Geolocation Prompting Component",
1132   classID:          Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"),
1133   contractID:       "@mozilla.org/geolocation/prompt;1",
1135   QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationPrompt]),
1137   prompt: function(request) {
1138     var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
1140     var result = pm.testExactPermission(request.requestingURI, "geo");
1142     if (result == Ci.nsIPermissionManager.ALLOW_ACTION) {
1143       request.allow();
1144       return;
1145     }
1146     
1147     if (result == Ci.nsIPermissionManager.DENY_ACTION) {
1148       request.cancel();
1149       return;
1150     }
1152     function setPagePermission(uri, allow) {
1153       if (allow == true)
1154         pm.add(uri, "geo", Ci.nsIPermissionManager.ALLOW_ACTION);
1155       else
1156         pm.add(uri, "geo", Ci.nsIPermissionManager.DENY_ACTION);
1157     }
1159     function getChromeWindow(aWindow) {
1160       var chromeWin = aWindow 
1161         .QueryInterface(Ci.nsIInterfaceRequestor)
1162         .getInterface(Ci.nsIWebNavigation)
1163         .QueryInterface(Ci.nsIDocShellTreeItem)
1164         .rootTreeItem
1165         .QueryInterface(Ci.nsIInterfaceRequestor)
1166         .getInterface(Ci.nsIDOMWindow)
1167         .QueryInterface(Ci.nsIDOMChromeWindow);
1168       return chromeWin;
1169     }
1171     var requestingWindow = request.requestingWindow.top;
1172     var chromeWindowObject = getChromeWindow(requestingWindow).wrappedJSObject;
1173     var tabbrowser = chromeWindowObject.gBrowser;
1174     var browser = tabbrowser.getBrowserForDocument(requestingWindow.document);
1175     var notificationBox = tabbrowser.getNotificationBox(browser);
1177     var notification = notificationBox.getNotificationWithValue("geolocation");
1178     if (!notification) {
1179       var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
1180       var browserBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
1182       var buttons = [{
1183               label: browserBundle.GetStringFromName("geolocation.shareLocation"),
1184               accessKey: browserBundle.GetStringFromName("geolocation.shareLocation.accesskey"),
1185               callback: function(notification) {
1186                   var elements = notification.getElementsByClassName("rememberChoice");
1187                   if (elements.length && elements[0].checked)
1188                       setPagePermission(request.requestingURI, true);
1189                   request.allow(); 
1190               },
1191           },
1192           {
1193               label: browserBundle.GetStringFromName("geolocation.dontShareLocation"),
1194               accessKey: browserBundle.GetStringFromName("geolocation.dontShareLocation.accesskey"),
1195               callback: function(notification) {
1196                   var elements = notification.getElementsByClassName("rememberChoice");
1197                   if (elements.length && elements[0].checked)
1198                       setPagePermission(request.requestingURI, false);
1199                   request.cancel();
1200               },
1201           }];
1202       
1203       var message = browserBundle.formatStringFromName("geolocation.siteWantsToKnow",
1204                                                        [request.requestingURI.host], 1);      
1206       var newBar = notificationBox.appendNotification(message,
1207                                                       "geolocation",
1208                                                       "chrome://browser/skin/Geo.png",
1209                                                       notificationBox.PRIORITY_INFO_HIGH,
1210                                                       buttons);
1212       // For whatever reason, if we do this immediately
1213       // (eg, without the setTimeout), the "link"
1214       // element does not show up in the notification
1215       // bar.
1216       function geolocation_hacks_to_notification () {
1218         // Never show a remember checkbox inside the private browsing mode
1219         var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
1220                                 getService(Ci.nsIPrivateBrowsingService).
1221                                 privateBrowsingEnabled;
1222         if (!inPrivateBrowsing) {
1223           var checkbox = newBar.ownerDocument.createElementNS(XULNS, "checkbox");
1224           checkbox.className = "rememberChoice";
1225           checkbox.setAttribute("label", browserBundle.GetStringFromName("geolocation.remember"));
1226           checkbox.setAttribute("accesskey", browserBundle.GetStringFromName("geolocation.remember.accesskey"));
1227           newBar.appendChild(checkbox);
1228         }
1230         var link = newBar.ownerDocument.createElementNS(XULNS, "label");
1231         link.className = "text-link";
1232         link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore"));
1234         var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
1235         link.href = formatter.formatURLPref("browser.geolocation.warning.infoURL");
1237         var description = newBar.ownerDocument.getAnonymousElementByAttribute(newBar, "anonid", "messageText");
1238         description.appendChild(link);
1239       };
1241       chromeWindowObject.setTimeout(geolocation_hacks_to_notification, 0);
1243     }
1244   },
1248 //module initialization
1249 function NSGetModule(aCompMgr, aFileSpec) {
1250   return XPCOMUtils.generateModule([BrowserGlue, GeolocationPrompt]);