Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / nsBrowserGlue.js
blob24ff06f945beda66d0ad61a52f0adc11e7872f9d
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>
27 # Alternatively, the contents of this file may be used under the terms of
28 # either the GNU General Public License Version 2 or later (the "GPL"), or
29 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 # in which case the provisions of the GPL or the LGPL are applicable instead
31 # of those above. If you wish to allow use of your version of this file only
32 # under the terms of either the GPL or the LGPL, and not to allow others to
33 # use your version of this file under the terms of the MPL, indicate your
34 # decision by deleting the provisions above and replace them with the notice
35 # and other provisions required by the GPL or the LGPL. If you do not delete
36 # the provisions above, a recipient may use your version of this file under
37 # the terms of any one of the MPL, the GPL or the LGPL.
39 # ***** END LICENSE BLOCK *****
41 const Ci = Components.interfaces;
42 const Cc = Components.classes;
43 const Cr = Components.results;
44 const Cu = Components.utils;
46 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
47 Cu.import("resource:///modules/distribution.js");
49 const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
51 // Check to see if bookmarks need backing up once per
52 // day on 1 hour idle.
53 const BOOKMARKS_ARCHIVE_IDLE_TIME = 60 * 60;
55 // Backup bookmarks once every 24 hours.
56 const BOOKMARKS_ARCHIVE_INTERVAL = 86400 * 1000;
58 // Factory object
59 const BrowserGlueServiceFactory = {
60   _instance: null,
61   createInstance: function (outer, iid) 
62   {
63     if (outer != null)
64       throw Components.results.NS_ERROR_NO_AGGREGATION;
65     return this._instance == null ?
66       this._instance = new BrowserGlue() : this._instance;
67   }
70 // Constructor
72 function BrowserGlue() {
73   this._init();
76 BrowserGlue.prototype = {
77   __prefs: null,
78   get _prefs() {
79     if (!this.__prefs)
80       this.__prefs = Cc["@mozilla.org/preferences-service;1"].
81                      getService(Ci.nsIPrefBranch);
82     return this.__prefs;
83   },
85   _saveSession: false,
87   _setPrefToSaveSession: function()
88   {
89     this._prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
90   },
92   // nsIObserver implementation 
93   observe: function(subject, topic, data) 
94   {
95     switch(topic) {
96       case "xpcom-shutdown":
97         this._dispose();
98         break;
99       case "quit-application": 
100         this._onProfileShutdown();
101         break;
102       case "prefservice:after-app-defaults":
103         this._onAppDefaults();
104         break;
105       case "final-ui-startup":
106         this._onProfileStartup();
107         break;
108       case "sessionstore-windows-restored":
109         this._onBrowserStartup();
110         break;
111       case "browser:purge-session-history":
112         // reset the console service's error buffer
113         const cs = Cc["@mozilla.org/consoleservice;1"].
114                    getService(Ci.nsIConsoleService);
115         cs.logStringMessage(null); // clear the console (in case it's open)
116         cs.reset();
117         break;
118       case "quit-application-requested":
119         this._onQuitRequest(subject, data);
120         break;
121       case "quit-application-granted":
122         if (this._saveSession) {
123           this._setPrefToSaveSession();
124         }
125         break;
126       case "session-save":
127         this._setPrefToSaveSession();
128         subject.QueryInterface(Ci.nsISupportsPRBool);
129         subject.data = true;
130         break;
131       case "places-init-complete":
132         this._initPlaces();
133         break;
134       case "idle":
135         if (this.idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
136           // Back up bookmarks.
137           this._archiveBookmarks();
138         }
139         break;
140     }
141   }, 
143   // initialization (called on application startup) 
144   _init: function() 
145   {
146     // observer registration
147     const osvr = Cc['@mozilla.org/observer-service;1'].
148                  getService(Ci.nsIObserverService);
149     osvr.addObserver(this, "quit-application", false);
150     osvr.addObserver(this, "xpcom-shutdown", false);
151     osvr.addObserver(this, "prefservice:after-app-defaults", false);
152     osvr.addObserver(this, "final-ui-startup", false);
153     osvr.addObserver(this, "sessionstore-windows-restored", false);
154     osvr.addObserver(this, "browser:purge-session-history", false);
155     osvr.addObserver(this, "quit-application-requested", false);
156     osvr.addObserver(this, "quit-application-granted", false);
157     osvr.addObserver(this, "session-save", false);
158     osvr.addObserver(this, "places-init-complete", false);
159   },
161   // cleanup (called on application shutdown)
162   _dispose: function() 
163   {
164     // observer removal 
165     const osvr = Cc['@mozilla.org/observer-service;1'].
166                  getService(Ci.nsIObserverService);
167     osvr.removeObserver(this, "quit-application");
168     osvr.removeObserver(this, "xpcom-shutdown");
169     osvr.removeObserver(this, "prefservice:after-app-defaults");
170     osvr.removeObserver(this, "final-ui-startup");
171     osvr.removeObserver(this, "sessionstore-windows-restored");
172     osvr.removeObserver(this, "browser:purge-session-history");
173     osvr.removeObserver(this, "quit-application-requested");
174     osvr.removeObserver(this, "quit-application-granted");
175     osvr.removeObserver(this, "session-save");
176     osvr.removeObserver(this, "places-init-complete");
177   },
179   _onAppDefaults: function()
180   {
181     // apply distribution customizations (prefs)
182     // other customizations are applied in _onProfileStartup()
183     var distro = new DistributionCustomizer();
184     distro.applyPrefDefaults();
185   },
187   // profile startup handler (contains profile initialization routines)
188   _onProfileStartup: function() 
189   {
190     this.Sanitizer.onStartup();
191     // check if we're in safe mode
192     var app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
193               QueryInterface(Ci.nsIXULRuntime);
194     if (app.inSafeMode) {
195       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
196                getService(Ci.nsIWindowWatcher);
197       ww.openWindow(null, "chrome://browser/content/safeMode.xul", 
198                     "_blank", "chrome,centerscreen,modal,resizable=no", null);
199     }
201     // apply distribution customizations
202     // prefs are applied in _onAppDefaults()
203     var distro = new DistributionCustomizer();
204     distro.applyCustomizations();
206     // handle any UI migration
207     this._migrateUI();
209     const osvr = Cc['@mozilla.org/observer-service;1'].
210                  getService(Ci.nsIObserverService);
211     osvr.notifyObservers(null, "browser-ui-startup-complete", "");
212   },
214   // profile shutdown handler (contains profile cleanup routines)
215   _onProfileShutdown: function() 
216   {
217     this._shutdownPlaces();
218     this.idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
219     this.Sanitizer.onShutdown();
220   },
222   // Browser startup complete. All initial windows have opened.
223   _onBrowserStartup: function()
224   {
225     // Show about:rights notification, if needed.
226     if (this._shouldShowRights())
227       this._showRightsNotification();
229     // If new add-ons were installed during startup open the add-ons manager.
230     if (this._prefs.prefHasUserValue(PREF_EM_NEW_ADDONS_LIST)) {
231       var args = Cc["@mozilla.org/supports-array;1"].
232                  createInstance(Ci.nsISupportsArray);
233       var str = Cc["@mozilla.org/supports-string;1"].
234                 createInstance(Ci.nsISupportsString);
235       str.data = "";
236       args.AppendElement(str);
237       var str = Cc["@mozilla.org/supports-string;1"].
238                 createInstance(Ci.nsISupportsString);
239       str.data = this._prefs.getCharPref(PREF_EM_NEW_ADDONS_LIST);
240       args.AppendElement(str);
241       const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
242       const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
243       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
244                getService(Ci.nsIWindowWatcher);
245       ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
246       this._prefs.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
247     }
248   },
250   _onQuitRequest: function(aCancelQuit, aQuitType)
251   {
252     // If user has already dismissed quit request, then do nothing
253     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
254       return;
256     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
257              getService(Ci.nsIWindowMediator);
259     var windowcount = 0;
260     var pagecount = 0;
261     var browserEnum = wm.getEnumerator("navigator:browser");
262     while (browserEnum.hasMoreElements()) {
263       windowcount++;
265       var browser = browserEnum.getNext();
266       var tabbrowser = browser.document.getElementById("content");
267       if (tabbrowser)
268         pagecount += tabbrowser.browsers.length;
269     }
271     this._saveSession = false;
272     if (pagecount < 2)
273       return;
275     if (aQuitType != "restart")
276       aQuitType = "quit";
278     var showPrompt = true;
279     try {
280       // browser.warnOnQuit is a hidden global boolean to override all quit prompts
281       // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
282       // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
284       var sessionWillBeSaved = this._prefs.getIntPref("browser.startup.page") == 3 ||
285                                this._prefs.getBoolPref("browser.sessionstore.resume_session_once");
286       if (sessionWillBeSaved || !this._prefs.getBoolPref("browser.warnOnQuit"))
287         showPrompt = false;
288       else if (aQuitType == "restart")
289         showPrompt = this._prefs.getBoolPref("browser.warnOnRestart");
290       else
291         showPrompt = this._prefs.getBoolPref("browser.tabs.warnOnClose");
292     } catch (ex) {}
294     if (!showPrompt)
295       return false;
297     var buttonChoice = 0;
298     var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
299                         getService(Ci.nsIStringBundleService);
300     var quitBundle = bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
301     var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
303     var appName = brandBundle.GetStringFromName("brandShortName");
304     var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
305                                                             [appName], 1);
307     var message;
308     if (aQuitType == "restart")
309       message = quitBundle.formatStringFromName("messageRestart",
310                                                 [appName], 1);
311     else if (windowcount == 1)
312       message = quitBundle.formatStringFromName("messageNoWindows",
313                                                 [appName], 1);
314     else
315       message = quitBundle.formatStringFromName("message",
316                                                 [appName], 1);
318     var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
319                         getService(Ci.nsIPromptService);
321     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
322                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
323                 promptService.BUTTON_POS_0_DEFAULT;
325     var neverAsk = {value:false};
326     var button0Title, button2Title;
327     var button1Title = quitBundle.GetStringFromName("cancelTitle");
328     var neverAskText = quitBundle.GetStringFromName("neverAsk");
330     if (aQuitType == "restart")
331       button0Title = quitBundle.GetStringFromName("restartTitle");
332     else {
333       flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
334       button0Title = quitBundle.GetStringFromName("saveTitle");
335       button2Title = quitBundle.GetStringFromName("quitTitle");
336     }
338     buttonChoice = promptService.confirmEx(null, quitDialogTitle, message,
339                                  flags, button0Title, button1Title, button2Title,
340                                  neverAskText, neverAsk);
342     switch (buttonChoice) {
343     case 2: // Quit
344       if (neverAsk.value)
345         this._prefs.setBoolPref("browser.tabs.warnOnClose", false);
346       break;
347     case 1: // Cancel
348       aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
349       aCancelQuit.data = true;
350       break;
351     case 0: // Save & Quit
352       this._saveSession = true;
353       if (neverAsk.value) {
354         if (aQuitType == "restart")
355           this._prefs.setBoolPref("browser.warnOnRestart", false);
356         else {
357           // always save state when shutting down
358           this._prefs.setIntPref("browser.startup.page", 3);
359         }
360       }
361       break;
362     }
363   },
365   /*
366    * _shouldShowRights - Determines if the user should be shown the
367    * about:rights notification. The notification should *not* be shown if
368    * we've already shown the current version, or if the override pref says to
369    * never show it. The notification *should* be shown if it's never been seen
370    * before, if a newer version is available, or if the override pref says to
371    * always show it.
372    */
373   _shouldShowRights : function () {
374     // Look for an unconditional override pref. If set, do what it says.
375     // (true --> never show, false --> always show)
376     try {
377       return !this._prefs.getBoolPref("browser.rights.override");
378     } catch (e) { }
379     // Ditto, for the legacy EULA pref.
380     try {
381       return !this._prefs.getBoolPref("browser.EULA.override");
382     } catch (e) { }
384 #ifndef OFFICIAL_BUILD
385     // Non-official builds shouldn't shouldn't show the notification.
386     return false;
387 #endif
389     // Look to see if the user has seen the current version or not.
390     var currentVersion = this._prefs.getIntPref("browser.rights.version");
391     try {
392       return !this._prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
393     } catch (e) { }
395     // Legacy: If the user accepted a EULA, we won't annoy them with the
396     // equivalent about:rights page until the version changes.
397     try {
398       return !this._prefs.getBoolPref("browser.EULA." + currentVersion + ".accepted");
399     } catch (e) { }
401     // We haven't shown the notification before, so do so now.
402     return true;
403   },
405   _showRightsNotification : function () {
406     // Stick the notification onto the selected tab of the active browser window.
407     var win = this.getMostRecentBrowserWindow();
408     var browser = win.gBrowser; // for closure in notification bar callback
409     var notifyBox = browser.getNotificationBox();
411     var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
412                         getService(Ci.nsIStringBundleService);
413     var brandBundle  = bundleService.createBundle("chrome://branding/locale/brand.properties");
414     var rightsBundle = bundleService.createBundle("chrome://browser/locale/aboutRights.properties");
416     var buttonLabel     = rightsBundle.GetStringFromName("buttonLabel");
417     var buttonAccessKey = rightsBundle.GetStringFromName("buttonAccessKey");
418     var productName     = brandBundle.GetStringFromName("brandFullName");
419     var notifyText      = rightsBundle.formatStringFromName("notifyText", [productName], 1);
420     
421     var buttons = [
422                     {
423                       label:     buttonLabel,
424                       accessKey: buttonAccessKey,
425                       popup:     null,
426                       callback: function(aNotificationBar, aButton) {
427                         browser.selectedTab = browser.addTab("about:rights");
428                       }
429                     }
430                   ];
432     // Set pref to indicate we've shown the notification.
433     var currentVersion = this._prefs.getIntPref("browser.rights.version");
434     this._prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
436     var box = notifyBox.appendNotification(notifyText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
437     box.persistence = 3; // arbitrary number, just so bar sticks around for a bit
438   },
440   // returns the (cached) Sanitizer constructor
441   get Sanitizer() 
442   {
443     if(typeof(Sanitizer) != "function") { // we should dynamically load the script
444       Cc["@mozilla.org/moz/jssubscript-loader;1"].
445       getService(Ci.mozIJSSubScriptLoader).
446       loadSubScript("chrome://browser/content/sanitize.js", null);
447     }
448     return Sanitizer;
449   },
451   _idleService: null,
452   get idleService() {
453     if (!this._idleService)
454       this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
455                           getService(Ci.nsIIdleService);
456     return this._idleService;
457   },
459   /**
460    * Initialize Places
461    * - imports the bookmarks html file if bookmarks database is empty, try to
462    *   restore bookmarks from a JSON backup if the backend indicates that the
463    *   database was corrupt.
464    *
465    * These prefs can be set up by the frontend:
466    *
467    * WARNING: setting these preferences to true will overwite existing bookmarks
468    *
469    * - browser.places.importBookmarksHTML
470    *   Set to true will import the bookmarks.html file from the profile folder.
471    * - browser.places.smartBookmarksVersion
472    *   Set during HTML import to indicate that Smart Bookmarks were created.
473    *   Set to -1 to disable Smart Bookmarks creation.
474    *   Set to 0 to restore current Smart Bookmarks.
475    * - browser.bookmarks.restore_default_bookmarks
476    *   Set to true by safe-mode dialog to indicate we must restore default
477    *   bookmarks.
478    */
479   _initPlaces: function bg__initPlaces() {
480     // We must instantiate the history service since it will tell us if we
481     // need to import or restore bookmarks due to first-run, corruption or
482     // forced migration (due to a major schema change).
483     var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
484                   getService(Ci.nsINavHistoryService);
485     var databaseStatus = histsvc.databaseStatus;
487     // If the database is corrupt or has been newly created we should
488     // import bookmarks.
489     var importBookmarks = databaseStatus != histsvc.DATABASE_STATUS_OK;
491     // Check if user or an extension has required to import bookmarks.html
492     var importBookmarksHTML = false;
493     try {
494       importBookmarksHTML =
495         this._prefs.getBoolPref("browser.places.importBookmarksHTML");
496       if (importBookmarksHTML)
497         importBookmarks = true;
498     } catch(ex) {}
500     // Check if Safe Mode or the user has required to restore bookmarks from
501     // default profile's bookmarks.html
502     var restoreDefaultBookmarks = false;
503     try {
504       restoreDefaultBookmarks =
505         this._prefs.getBoolPref("browser.bookmarks.restore_default_bookmarks");
506       if (restoreDefaultBookmarks) {
507         // Ensure that we already have a bookmarks backup for today
508         this._archiveBookmarks();
509         importBookmarks = true;
510       }
511     } catch(ex) {}
513     // If the user did not require to restore default bookmarks, or import
514     // from bookmarks.html, we will try to restore from JSON
515     if (importBookmarks && !restoreDefaultBookmarks && !importBookmarksHTML) {
516       // get latest JSON backup
517       Cu.import("resource://gre/modules/utils.js");
518       var bookmarksBackupFile = PlacesUtils.getMostRecentBackup();
519       if (bookmarksBackupFile && bookmarksBackupFile.leafName.match("\.json$")) {
520         // restore from JSON backup
521         PlacesUtils.restoreBookmarksFromJSONFile(bookmarksBackupFile);
522         importBookmarks = false;
523       }
524       else {
525         // We have created a new database but we don't have any backup available
526         importBookmarks = true;
527         var dirService = Cc["@mozilla.org/file/directory_service;1"].
528                          getService(Ci.nsIProperties);
529         var bookmarksHTMLFile = dirService.get("BMarks", Ci.nsILocalFile);
530         if (bookmarksHTMLFile.exists()) {
531           // If bookmarks.html is available in current profile import it...
532           importBookmarksHTML = true;
533         }
534         else {
535           // ...otherwise we will restore defaults
536           restoreDefaultBookmarks = true;
537         }
538       }
539     }
541     if (!importBookmarks) {
542       // Call it here for Fx3 profiles created before the Places folder
543       // has been added, otherwise it's called during import.
544       this.ensurePlacesDefaultQueriesInitialized();
545     }
546     else {
547       // ensurePlacesDefaultQueriesInitialized() is called by import.
548       this._prefs.setIntPref("browser.places.smartBookmarksVersion", 0);
550       // Get bookmarks.html file location
551       var dirService = Cc["@mozilla.org/file/directory_service;1"].
552                        getService(Ci.nsIProperties);
554       var bookmarksFile = null;
555       if (restoreDefaultBookmarks) {
556         // User wants to restore bookmarks.html file from default profile folder
557         bookmarksFile = dirService.get("profDef", Ci.nsILocalFile);
558         bookmarksFile.append("bookmarks.html");
559       }
560       else
561         bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
563       if (bookmarksFile.exists()) {
564         // import the file
565         try {
566           var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
567                          getService(Ci.nsIPlacesImportExportService);
568           importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */);
569         } catch (err) {
570           // Report the error, but ignore it.
571           Cu.reportError("Bookmarks.html file could be corrupt. " + err);
572         }
573       }
574       else
575         Cu.reportError("Unable to find bookmarks.html file.");
577       // Reset preferences, so we won't try to import again at next run
578       if (importBookmarksHTML)
579         this._prefs.setBoolPref("browser.places.importBookmarksHTML", false);
580       if (restoreDefaultBookmarks)
581         this._prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
582                                 false);
583     }
585     // Initialize bookmark archiving on idle.
586     // Once a day, either on idle or shutdown, bookmarks are backed up.
587     this.idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
588   },
590   /**
591    * Places shut-down tasks
592    * - back up and archive bookmarks
593    * - export bookmarks as HTML, if so configured
594    *
595    * Note: quit-application-granted notification is received twice
596    *       so replace this method with a no-op when first called.
597    */
598   _shutdownPlaces: function bg__shutdownPlaces() {
599     // Backup and archive Places bookmarks.
600     this._archiveBookmarks();
602     // Backup bookmarks to bookmarks.html to support apps that depend
603     // on the legacy format.
604     var autoExportHTML = false;
605     try {
606       autoExportHTML = this._prefs.getBoolPref("browser.bookmarks.autoExportHTML");
607     } catch(ex) {
608       Components.utils.reportError(ex);
609     }
611     if (autoExportHTML) {
612       Cc["@mozilla.org/browser/places/import-export-service;1"].
613         getService(Ci.nsIPlacesImportExportService).
614         backupBookmarksFile();
615     }
616   },
618   /**
619    * Back up and archive bookmarks
620    */
621   _archiveBookmarks: function nsBrowserGlue__archiveBookmarks() {
622     Cu.import("resource://gre/modules/utils.js");
624     var lastBackup = PlacesUtils.getMostRecentBackup();
626     // Backup bookmarks if there aren't any backups or 
627     // they haven't been backed up in the last 24 hrs.
628     if (!lastBackup ||
629         Date.now() - lastBackup.lastModifiedTime > BOOKMARKS_ARCHIVE_INTERVAL) {
630       var maxBackups = 5;
631       try {
632         maxBackups = this._prefs.getIntPref("browser.bookmarks.max_backups");
633       } catch(ex) {}
635       PlacesUtils.archiveBookmarksFile(maxBackups, false /* don't force */);
636     }
637   },
639   _migrateUI: function bg__migrateUI() {
640     var migration = 0;
641     try {
642       migration = this._prefs.getIntPref("browser.migration.version");
643     } catch(ex) {}
645     if (migration == 0) {
646       // this code should always migrate pre-FF3 profiles to the current UI state
648       // grab the localstore.rdf and make changes needed for new UI
649       this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
650       this._dataSource = this._rdf.GetDataSource("rdf:local-store");
651       this._dirty = false;
653       let currentsetResource = this._rdf.GetResource("currentset");
654       let toolbars = ["nav-bar", "toolbar-menubar", "PersonalToolbar"];
655       for (let i = 0; i < toolbars.length; i++) {
656         let toolbar = this._rdf.GetResource("chrome://browser/content/browser.xul#" + toolbars[i]);
657         let currentset = this._getPersist(toolbar, currentsetResource);
658         if (!currentset) {
659           // toolbar isn't customized
660           if (i == 0)
661             // new button is in the defaultset, nothing to migrate
662             break;
663           continue;
664         }
665         if (/(?:^|,)unified-back-forward-button(?:$|,)/.test(currentset))
666           // new button is already there, nothing to migrate
667           break;
668         if (/(?:^|,)back-button(?:$|,)/.test(currentset)) {
669           let newset = currentset.replace(/(^|,)back-button($|,)/,
670                                           "$1unified-back-forward-button,back-button$2")
671           this._setPersist(toolbar, currentsetResource, newset);
672           // done migrating
673           break;
674         }
675       }
677       // force the RDF to be saved
678       if (this._dirty)
679         this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
681       // free up the RDF service
682       this._rdf = null;
683       this._dataSource = null;
685       // update the migration version
686       this._prefs.setIntPref("browser.migration.version", 1);
687     }
688   },
690   _getPersist: function bg__getPersist(aSource, aProperty) {
691     var target = this._dataSource.GetTarget(aSource, aProperty, true);
692     if (target instanceof Ci.nsIRDFLiteral)
693       return target.Value;
694     return null;
695   },
697   _setPersist: function bg__setPersist(aSource, aProperty, aTarget) {
698     this._dirty = true;
699     try {
700       var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
701       if (oldTarget) {
702         if (aTarget)
703           this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
704         else
705           this._dataSource.Unassert(aSource, aProperty, oldTarget);
706       }
707       else {
708         this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
709       }
710     }
711     catch(ex) {}
712   },
714   // ------------------------------
715   // public nsIBrowserGlue members
716   // ------------------------------
717   
718   sanitize: function(aParentWindow) 
719   {
720     this.Sanitizer.sanitize(aParentWindow);
721   },
723   ensurePlacesDefaultQueriesInitialized: function() {
724     const SMART_BOOKMARKS_VERSION = 1;
725     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
726     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
728     // XXX should this be a pref?  see bug #399268
729     const MAX_RESULTS = 10;
731     // get current smart bookmarks version
732     // By default, if the pref is not set up, we must create Smart Bookmarks
733     var smartBookmarksCurrentVersion = 0;
734     try {
735       smartBookmarksCurrentVersion = this._prefs.getIntPref(SMART_BOOKMARKS_PREF);
736     } catch(ex) {}
738     // bail out if we don't have to create or update Smart Bookmarks
739     if (smartBookmarksCurrentVersion == -1 ||
740         smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION)
741       return;
743     var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
744                 getService(Ci.nsINavBookmarksService);
745     var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
746                   getService(Ci.nsIAnnotationService);
748     var callback = {
749       _placesBundle: Cc["@mozilla.org/intl/stringbundle;1"].
750                      getService(Ci.nsIStringBundleService).
751                      createBundle("chrome://browser/locale/places/places.properties"),
753       _uri: function(aSpec) {
754         return Cc["@mozilla.org/network/io-service;1"].
755                getService(Ci.nsIIOService).
756                newURI(aSpec, null, null);
757       },
759       runBatched: function() {
760         var smartBookmarks = [];
761         var bookmarksMenuIndex = 0;
762         var bookmarksToolbarIndex = 0;
764         // MOST VISITED
765         var smart = {queryId: "MostVisited", // don't change this
766                      itemId: null,
767                      title: this._placesBundle.GetStringFromName("mostVisitedTitle"),
768                      uri: this._uri("place:queryType=" +
769                                     Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
770                                     "&sort=" +
771                                     Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
772                                     "&maxResults=" + MAX_RESULTS),
773                      parent: bmsvc.toolbarFolder,
774                      position: bookmarksToolbarIndex++};
775         smartBookmarks.push(smart);
777         // RECENTLY BOOKMARKED
778         smart = {queryId: "RecentlyBookmarked", // don't change this
779                  itemId: null,
780                  title: this._placesBundle.GetStringFromName("recentlyBookmarkedTitle"),
781                  uri: this._uri("place:folder=BOOKMARKS_MENU" +
782                                 "&folder=UNFILED_BOOKMARKS" +
783                                 "&folder=TOOLBAR" +
784                                 "&queryType=" +
785                                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
786                                 "&sort=" +
787                                 Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
788                                 "&excludeItemIfParentHasAnnotation=livemark%2FfeedURI" +
789                                 "&maxResults=" + MAX_RESULTS +
790                                 "&excludeQueries=1"),
791                  parent: bmsvc.bookmarksMenuFolder,
792                  position: bookmarksMenuIndex++};
793         smartBookmarks.push(smart);
795         // RECENT TAGS
796         smart = {queryId: "RecentTags", // don't change this
797                  itemId: null,
798                  title: this._placesBundle.GetStringFromName("recentTagsTitle"),
799                  uri: this._uri("place:"+
800                     "type=" +
801                     Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
802                     "&sort=" +
803                     Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
804                     "&maxResults=" + MAX_RESULTS),
805                  parent: bmsvc.bookmarksMenuFolder,
806                  position: bookmarksMenuIndex++};
807         smartBookmarks.push(smart);
809         var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, {});
810         // set current itemId, parent and position if Smart Bookmark exists
811         for each(var itemId in smartBookmarkItemIds) {
812           var queryId = annosvc.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
813           for (var i = 0; i < smartBookmarks.length; i++){
814             if (smartBookmarks[i].queryId == queryId) {
815               smartBookmarks[i].itemId = itemId;
816               smartBookmarks[i].parent = bmsvc.getFolderIdForItem(itemId);
817               smartBookmarks[i].position = bmsvc.getItemIndex(itemId);
818               // remove current item, since it will be replaced
819               bmsvc.removeItem(itemId);
820               break;
821             }
822             // We don't remove old Smart Bookmarks because user could still
823             // find them useful, or could have personalized them.
824             // Instead we remove the Smart Bookmark annotation.
825             if (i == smartBookmarks.length - 1)
826               annosvc.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
827           }
828         }
830         // create smart bookmarks
831         for each(var smartBookmark in smartBookmarks) {
832           smartBookmark.itemId = bmsvc.insertBookmark(smartBookmark.parent,
833                                                       smartBookmark.uri,
834                                                       smartBookmark.position,
835                                                       smartBookmark.title);
836           annosvc.setItemAnnotation(smartBookmark.itemId,
837                                     SMART_BOOKMARKS_ANNO, smartBookmark.queryId,
838                                     0, annosvc.EXPIRE_NEVER);
839         }
840         
841         // If we are creating all Smart Bookmarks from ground up, add a
842         // separator below them in the bookmarks menu.
843         if (smartBookmarkItemIds.length == 0)
844           bmsvc.insertSeparator(bmsvc.bookmarksMenuFolder, bookmarksMenuIndex);
845       }
846     };
848     try {
849       bmsvc.runInBatchMode(callback, null);
850     }
851     catch(ex) {
852       Components.utils.reportError(ex);
853     }
854     finally {
855       this._prefs.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
856       this._prefs.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
857     }
858   },
860 #ifdef XP_UNIX
861 #ifndef XP_MACOSX
862 #define BROKEN_WM_Z_ORDER
863 #endif
864 #endif
865 #ifdef XP_OS2
866 #define BROKEN_WM_Z_ORDER
867 #endif
869   // this returns the most recent non-popup browser window
870   getMostRecentBrowserWindow : function ()
871   {
872     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
873              getService(Components.interfaces.nsIWindowMediator);
875 #ifdef BROKEN_WM_Z_ORDER
876     var win = wm.getMostRecentWindow("navigator:browser", true);
878     // if we're lucky, this isn't a popup, and we can just return this
879     if (win && win.document.documentElement.getAttribute("chromehidden")) {
880       win = null;
881       var windowList = wm.getEnumerator("navigator:browser", true);
882       // this is oldest to newest, so this gets a bit ugly
883       while (windowList.hasMoreElements()) {
884         var nextWin = windowList.getNext();
885         if (!nextWin.document.documentElement.getAttribute("chromehidden"))
886           win = nextWin;
887       }
888     }
889 #else
890     var windowList = wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
891     if (!windowList.hasMoreElements())
892       return null;
894     var win = windowList.getNext();
895     while (win.document.documentElement.getAttribute("chromehidden")) {
896       if (!windowList.hasMoreElements())
897         return null;
899       win = windowList.getNext();
900     }
901 #endif
903     return win;
904   },
907   // for XPCOM
908   classDescription: "Firefox Browser Glue Service",
909   classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),
910   contractID:       "@mozilla.org/browser/browserglue;1",
912   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
913                                          Ci.nsISupportsWeakReference,
914                                          Ci.nsIBrowserGlue]),
916   // redefine the default factory for XPCOMUtils
917   _xpcom_factory: BrowserGlueServiceFactory,
919   // get this contractID registered for certain categories via XPCOMUtils
920   _xpcom_categories: [
921     // make BrowserGlue a startup observer
922     { category: "app-startup", service: true }
923   ]
926 function GeolocationPrompt() {}
928 GeolocationPrompt.prototype = {
929   classDescription: "Geolocation Prompting Component",
930   classID:          Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"),
931   contractID:       "@mozilla.org/geolocation/prompt;1",
933   QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationPrompt]),
935   prompt: function(request) {
937     function getChromeWindow(aWindow) {
938       var chromeWin = aWindow 
939         .QueryInterface(Ci.nsIInterfaceRequestor)
940         .getInterface(Ci.nsIWebNavigation)
941         .QueryInterface(Ci.nsIDocShellTreeItem)
942         .rootTreeItem
943         .QueryInterface(Ci.nsIInterfaceRequestor)
944         .getInterface(Ci.nsIDOMWindow)
945         .QueryInterface(Ci.nsIDOMChromeWindow);
946       return chromeWin;
947     }
949     var requestingWindow = request.requestingWindow.top;
950     var tabbrowser = getChromeWindow(requestingWindow).wrappedJSObject.gBrowser;
951     var browser = tabbrowser.getBrowserForDocument(requestingWindow.document);
952     var notificationBox = tabbrowser.getNotificationBox(browser);
954     var notification = notificationBox.getNotificationWithValue("geolocation");
955     if (!notification) {
956       var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
957       var browserBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
959       var buttons = [{
960         label: browserBundle.GetStringFromName("geolocation.exactLocation"),
961         accessKey: browserBundle.GetStringFromName("geolocation.exactLocationKey"),
962         callback: function() request.allow() ,
963         },
964         {
965         label: browserBundle.GetStringFromName("geolocation.nothingLocation"),
966         accessKey: browserBundle.GetStringFromName("geolocation.nothingLocationKey"),
967         callback: function() request.cancel() ,
968         }];
969       
970       var message = browserBundle.formatStringFromName("geolocation.requestMessage",
971                                                        [request.requestingURI.spec], 1);      
972       notificationBox.appendNotification(message,
973                                          "geolocation",
974                                          "chrome://browser/skin/Info.png",
975                                          notificationBox.PRIORITY_INFO_HIGH,
976                                          buttons);
977     }
978   },
982 //module initialization
983 function NSGetModule(aCompMgr, aFileSpec) {
984   return XPCOMUtils.generateModule([BrowserGlue, GeolocationPrompt]);