Bug 449371 Firefox/Thunderbird crashes at exit [@ gdk_display_x11_finalize], p=Brian...
[wine-gecko.git] / browser / components / nsBrowserGlue.js
bloba927f787a64c449ec517d807a3b29d1394abbf8b
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>
26 # Alternatively, the contents of this file may be used under the terms of
27 # either the GNU General Public License Version 2 or later (the "GPL"), or
28 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 # in which case the provisions of the GPL or the LGPL are applicable instead
30 # of those above. If you wish to allow use of your version of this file only
31 # under the terms of either the GPL or the LGPL, and not to allow others to
32 # use your version of this file under the terms of the MPL, indicate your
33 # decision by deleting the provisions above and replace them with the notice
34 # and other provisions required by the GPL or the LGPL. If you do not delete
35 # the provisions above, a recipient may use your version of this file under
36 # the terms of any one of the MPL, the GPL or the LGPL.
38 # ***** END LICENSE BLOCK *****
40 const Ci = Components.interfaces;
41 const Cc = Components.classes;
42 const Cr = Components.results;
43 const Cu = Components.utils;
45 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
46 Cu.import("resource:///modules/distribution.js");
48 const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
50 // Check to see if bookmarks need backing up once per
51 // day on 1 hour idle.
52 const BOOKMARKS_ARCHIVE_IDLE_TIME = 60 * 60;
54 // Backup bookmarks once every 24 hours.
55 const BOOKMARKS_ARCHIVE_INTERVAL = 86400 * 1000;
57 // Factory object
58 const BrowserGlueServiceFactory = {
59   _instance: null,
60   createInstance: function (outer, iid) 
61   {
62     if (outer != null)
63       throw Components.results.NS_ERROR_NO_AGGREGATION;
64     return this._instance == null ?
65       this._instance = new BrowserGlue() : this._instance;
66   }
69 // Constructor
71 function BrowserGlue() {
72   this._init();
75 BrowserGlue.prototype = {
76   _saveSession: false,
78   _setPrefToSaveSession: function()
79   {
80     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
81                      getService(Ci.nsIPrefBranch);
82     prefBranch.setBoolPref("browser.sessionstore.resume_session_once", true);
83   },
85   // nsIObserver implementation 
86   observe: function(subject, topic, data) 
87   {
88     switch(topic) {
89       case "xpcom-shutdown":
90         this._dispose();
91         break;
92       case "quit-application": 
93         this._onProfileShutdown();
94         break;
95       case "prefservice:after-app-defaults":
96         this._onAppDefaults();
97         break;
98       case "final-ui-startup":
99         this._onProfileStartup();
100         break;
101       case "sessionstore-windows-restored":
102         this._onBrowserStartup();
103         break;
104       case "browser:purge-session-history":
105         // reset the console service's error buffer
106         const cs = Cc["@mozilla.org/consoleservice;1"].
107                    getService(Ci.nsIConsoleService);
108         cs.logStringMessage(null); // clear the console (in case it's open)
109         cs.reset();
110         break;
111       case "quit-application-requested":
112         this._onQuitRequest(subject, data);
113         break;
114       case "quit-application-granted":
115         if (this._saveSession) {
116           this._setPrefToSaveSession();
117         }
118         break;
119       case "session-save":
120         this._setPrefToSaveSession();
121         subject.QueryInterface(Ci.nsISupportsPRBool);
122         subject.data = true;
123         break;
124       case "idle":
125         if (this.idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
126           // Back up bookmarks.
127           this._archiveBookmarks();
128         }
129         break;
130     }
131   }, 
133   // initialization (called on application startup) 
134   _init: function() 
135   {
136     // observer registration
137     const osvr = Cc['@mozilla.org/observer-service;1'].
138                  getService(Ci.nsIObserverService);
139     osvr.addObserver(this, "quit-application", false);
140     osvr.addObserver(this, "xpcom-shutdown", false);
141     osvr.addObserver(this, "prefservice:after-app-defaults", false);
142     osvr.addObserver(this, "final-ui-startup", false);
143     osvr.addObserver(this, "sessionstore-windows-restored", false);
144     osvr.addObserver(this, "browser:purge-session-history", false);
145     osvr.addObserver(this, "quit-application-requested", false);
146     osvr.addObserver(this, "quit-application-granted", false);
147     osvr.addObserver(this, "session-save", false);
148   },
150   // cleanup (called on application shutdown)
151   _dispose: function() 
152   {
153     // observer removal 
154     const osvr = Cc['@mozilla.org/observer-service;1'].
155                  getService(Ci.nsIObserverService);
156     osvr.removeObserver(this, "quit-application");
157     osvr.removeObserver(this, "xpcom-shutdown");
158     osvr.removeObserver(this, "prefservice:after-app-defaults");
159     osvr.removeObserver(this, "final-ui-startup");
160     osvr.removeObserver(this, "sessionstore-windows-restored");
161     osvr.removeObserver(this, "browser:purge-session-history");
162     osvr.removeObserver(this, "quit-application-requested");
163     osvr.removeObserver(this, "quit-application-granted");
164     osvr.removeObserver(this, "session-save");
165   },
167   _onAppDefaults: function()
168   {
169     // apply distribution customizations (prefs)
170     // other customizations are applied in _onProfileStartup()
171     var distro = new DistributionCustomizer();
172     distro.applyPrefDefaults();
173   },
175   // profile startup handler (contains profile initialization routines)
176   _onProfileStartup: function() 
177   {
178     // Check to see if the EULA must be shown on startup
180     // Global override for tinderbox machines
181     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
182                      getService(Ci.nsIPrefBranch);
183     var mustDisplayEULA = true;
184     try {
185       mustDisplayEULA = !prefBranch.getBoolPref("browser.EULA.override");
186     } catch (e) {
187       // Pref might not exist
188     }
190     // Make sure it hasn't already been accepted
191     if (mustDisplayEULA) {
192       try {
193         var EULAVersion = prefBranch.getIntPref("browser.EULA.version");
194         mustDisplayEULA = !prefBranch.getBoolPref("browser.EULA." + EULAVersion + ".accepted");
195       } catch(ex) {
196       }
197     }
199     if (mustDisplayEULA) {
200       var ww2 = Cc["@mozilla.org/embedcomp/window-watcher;1"].
201                 getService(Ci.nsIWindowWatcher);
202       ww2.openWindow(null, "chrome://browser/content/EULA.xul", 
203                      "_blank", "chrome,centerscreen,modal,resizable=yes", null);
204     }
206     this.Sanitizer.onStartup();
207     // check if we're in safe mode
208     var app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
209               QueryInterface(Ci.nsIXULRuntime);
210     if (app.inSafeMode) {
211       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
212                getService(Ci.nsIWindowWatcher);
213       ww.openWindow(null, "chrome://browser/content/safeMode.xul", 
214                     "_blank", "chrome,centerscreen,modal,resizable=no", null);
215     }
217     // initialize Places
218     this._initPlaces();
220     // apply distribution customizations
221     // prefs are applied in _onAppDefaults()
222     var distro = new DistributionCustomizer();
223     distro.applyCustomizations();
225     // handle any UI migration
226     this._migrateUI();
227   },
229   // profile shutdown handler (contains profile cleanup routines)
230   _onProfileShutdown: function() 
231   {
232     this._shutdownPlaces();
233     this.idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
234     this.Sanitizer.onShutdown();
235   },
237   // Browser startup complete. All initial windows have opened.
238   _onBrowserStartup: function()
239   {
240     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
241                      getService(Ci.nsIPrefBranch);
242     // If new add-ons were installed during startup open the add-ons manager.
243     if (prefBranch.prefHasUserValue(PREF_EM_NEW_ADDONS_LIST)) {
244       var args = Cc["@mozilla.org/supports-array;1"].
245                  createInstance(Ci.nsISupportsArray);
246       var str = Cc["@mozilla.org/supports-string;1"].
247                 createInstance(Ci.nsISupportsString);
248       str.data = "";
249       args.AppendElement(str);
250       var str = Cc["@mozilla.org/supports-string;1"].
251                 createInstance(Ci.nsISupportsString);
252       str.data = prefBranch.getCharPref(PREF_EM_NEW_ADDONS_LIST);
253       args.AppendElement(str);
254       const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
255       const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
256       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
257                getService(Ci.nsIWindowWatcher);
258       ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
259       prefBranch.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
260     }
261   },
263   _onQuitRequest: function(aCancelQuit, aQuitType)
264   {
265     // If user has already dismissed quit request, then do nothing
266     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
267       return;
269     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
270              getService(Ci.nsIWindowMediator);
272     var windowcount = 0;
273     var pagecount = 0;
274     var browserEnum = wm.getEnumerator("navigator:browser");
275     while (browserEnum.hasMoreElements()) {
276       windowcount++;
278       var browser = browserEnum.getNext();
279       var tabbrowser = browser.document.getElementById("content");
280       if (tabbrowser)
281         pagecount += tabbrowser.browsers.length;
282     }
284     this._saveSession = false;
285     if (pagecount < 2)
286       return;
288     if (aQuitType != "restart")
289       aQuitType = "quit";
291     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
292                      getService(Ci.nsIPrefBranch);
293     var showPrompt = true;
294     try {
295       // browser.warnOnQuit is a hidden global boolean to override all quit prompts
296       // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
297       // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
299       var sessionWillBeSaved = prefBranch.getIntPref("browser.startup.page") == 3 ||
300                                prefBranch.getBoolPref("browser.sessionstore.resume_session_once");
301       if (sessionWillBeSaved || !prefBranch.getBoolPref("browser.warnOnQuit"))
302         showPrompt = false;
303       else if (aQuitType == "restart")
304         showPrompt = prefBranch.getBoolPref("browser.warnOnRestart");
305       else
306         showPrompt = prefBranch.getBoolPref("browser.tabs.warnOnClose");
307     } catch (ex) {}
309     if (!showPrompt)
310       return false;
312     var buttonChoice = 0;
313     var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
314                         getService(Ci.nsIStringBundleService);
315     var quitBundle = bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
316     var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
318     var appName = brandBundle.GetStringFromName("brandShortName");
319     var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
320                                                             [appName], 1);
322     var message;
323     if (aQuitType == "restart")
324       message = quitBundle.formatStringFromName("messageRestart",
325                                                 [appName], 1);
326     else if (windowcount == 1)
327       message = quitBundle.formatStringFromName("messageNoWindows",
328                                                 [appName], 1);
329     else
330       message = quitBundle.formatStringFromName("message",
331                                                 [appName], 1);
333     var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
334                         getService(Ci.nsIPromptService);
336     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
337                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
338                 promptService.BUTTON_POS_0_DEFAULT;
340     var neverAsk = {value:false};
341     var button0Title, button2Title;
342     var button1Title = quitBundle.GetStringFromName("cancelTitle");
343     var neverAskText = quitBundle.GetStringFromName("neverAsk");
345     if (aQuitType == "restart")
346       button0Title = quitBundle.GetStringFromName("restartTitle");
347     else {
348       flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
349       button0Title = quitBundle.GetStringFromName("saveTitle");
350       button2Title = quitBundle.GetStringFromName("quitTitle");
351     }
353     buttonChoice = promptService.confirmEx(null, quitDialogTitle, message,
354                                  flags, button0Title, button1Title, button2Title,
355                                  neverAskText, neverAsk);
357     switch (buttonChoice) {
358     case 2: // Quit
359       if (neverAsk.value)
360         prefBranch.setBoolPref("browser.tabs.warnOnClose", false);
361       break;
362     case 1: // Cancel
363       aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
364       aCancelQuit.data = true;
365       break;
366     case 0: // Save & Quit
367       this._saveSession = true;
368       if (neverAsk.value) {
369         if (aQuitType == "restart")
370           prefBranch.setBoolPref("browser.warnOnRestart", false);
371         else {
372           // always save state when shutting down
373           prefBranch.setIntPref("browser.startup.page", 3);
374         }
375       }
376       break;
377     }
378   },
380   // returns the (cached) Sanitizer constructor
381   get Sanitizer() 
382   {
383     if(typeof(Sanitizer) != "function") { // we should dynamically load the script
384       Cc["@mozilla.org/moz/jssubscript-loader;1"].
385       getService(Ci.mozIJSSubScriptLoader).
386       loadSubScript("chrome://browser/content/sanitize.js", null);
387     }
388     return Sanitizer;
389   },
391   _idleService: null,
392   get idleService() {
393     if (!this._idleService)
394       this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
395                           getService(Ci.nsIIdleService);
396     return this._idleService;
397   },
399   /**
400    * Initialize Places
401    * - imports the bookmarks html file if bookmarks datastore is empty
402    *
403    * These prefs are set by the backend services upon creation (or recreation)
404    * of the Places db:
405    * - browser.places.importBookmarksHTML
406    *   Set to false by the history service to indicate we need to re-import.
407    * - browser.places.smartBookmarksVersion
408    *   Set during HTML import to indicate that Smart Bookmarks were created.
409    *   Set to -1 to disable Smart Bookmarks creation.
410    *   Set to 0 to restore current Smart Bookmarks.
411    *
412    * These prefs are set up by the frontend:
413    * - browser.bookmarks.restore_default_bookmarks
414    *   Set to true by safe-mode dialog to indicate we must restore default
415    *   bookmarks.
416    */
417   _initPlaces: function bg__initPlaces() {
418     // we need to instantiate the history service before checking
419     // the browser.places.importBookmarksHTML pref, as
420     // nsNavHistory::ForceMigrateBookmarksDB() will set that pref
421     // if we need to force a migration (due to a schema change)
422     var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
423                   getService(Ci.nsINavHistoryService);
425     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
426                      getService(Ci.nsIPrefBranch);
428     var importBookmarks = false;
429     var restoreDefaultBookmarks = false;
430     try {
431       restoreDefaultBookmarks = prefBranch.getBoolPref("browser.bookmarks.restore_default_bookmarks");
432     } catch(ex) {}
434     if (restoreDefaultBookmarks) {
435       // Ensure that we already have a bookmarks backup for today
436       this._archiveBookmarks();
437       // we will restore bookmarks from html
438       importBookmarks = true;
439     }
440     else {
441       try {
442         importBookmarks = prefBranch.getBoolPref("browser.places.importBookmarksHTML");
443       } catch(ex) {}
444     }
446     if (!importBookmarks) {
447       // Call it here for Fx3 profiles created before the Places folder
448       // has been added, otherwise it's called during import.
449       this.ensurePlacesDefaultQueriesInitialized();
450     }
451     else {
452       // get latest backup
453       Cu.import("resource://gre/modules/utils.js");
454       var bookmarksFile = PlacesUtils.getMostRecentBackup();
456       if (!restoreDefaultBookmarks &&
457           bookmarksFile && bookmarksFile.leafName.match("\.json$")) {
458         // restore a JSON backup
459         PlacesUtils.restoreBookmarksFromJSONFile(bookmarksFile);
460       }
461       else {
462         // if there's no JSON backup or we are restoring default bookmarks
464         // ensurePlacesDefaultQueriesInitialized() is called by import.
465         prefBranch.setIntPref("browser.places.smartBookmarksVersion", 0);
467         var dirService = Cc["@mozilla.org/file/directory_service;1"].
468                          getService(Ci.nsIProperties);
470         var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
471         if (restoreDefaultBookmarks || !bookmarksFile.exists()) {
472           // get bookmarks.html file from default profile folder
473           bookmarksFile = dirService.get("profDef", Ci.nsILocalFile);
474           bookmarksFile.append("bookmarks.html");
475         }
477         // import the file
478         try {
479           var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
480                          getService(Ci.nsIPlacesImportExportService);
481           importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */);
482         } catch (err) {
483           // Report the error, but ignore it.
484           Cu.reportError(err);
485         }
486         prefBranch.setBoolPref("browser.places.importBookmarksHTML", false);
487         if (restoreDefaultBookmarks)
488           prefBranch.setBoolPref("browser.bookmarks.restore_default_bookmarks",
489                                  false);
490       }
491     }
493     // Initialize bookmark archiving on idle.
494     // Once a day, either on idle or shutdown, bookmarks are backed up.
495     this.idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
496   },
498   /**
499    * Places shut-down tasks
500    * - back up and archive bookmarks
501    * - export bookmarks as HTML, if so configured
502    *
503    * Note: quit-application-granted notification is received twice
504    *       so replace this method with a no-op when first called.
505    */
506   _shutdownPlaces: function bg__shutdownPlaces() {
507     // Backup and archive Places bookmarks.
508     this._archiveBookmarks();
510     // Backup bookmarks to bookmarks.html to support apps that depend
511     // on the legacy format.
512     var prefs = Cc["@mozilla.org/preferences-service;1"].
513                 getService(Ci.nsIPrefBranch);
514     var autoExportHTML = false;
515     try {
516       autoExportHTML = prefs.getBoolPref("browser.bookmarks.autoExportHTML");
517     } catch(ex) {
518       Components.utils.reportError(ex);
519     }
521     if (autoExportHTML) {
522       Cc["@mozilla.org/browser/places/import-export-service;1"].
523         getService(Ci.nsIPlacesImportExportService).
524         backupBookmarksFile();
525     }
526   },
528   /**
529    * Back up and archive bookmarks
530    */
531   _archiveBookmarks: function nsBrowserGlue__archiveBookmarks() {
532     Cu.import("resource://gre/modules/utils.js");
534     var lastBackup = PlacesUtils.getMostRecentBackup();
536     // Backup bookmarks if there aren't any backups or 
537     // they haven't been backed up in the last 24 hrs.
538     if (!lastBackup ||
539         Date.now() - lastBackup.lastModifiedTime > BOOKMARKS_ARCHIVE_INTERVAL) {
540       var maxBackups = 5;
541       var prefs = Cc["@mozilla.org/preferences-service;1"].
542                   getService(Ci.nsIPrefBranch);
543       try {
544         maxBackups = prefs.getIntPref("browser.bookmarks.max_backups");
545       } catch(ex) {}
547       PlacesUtils.archiveBookmarksFile(maxBackups, false /* don't force */);
548     }
549   },
551   _migrateUI: function bg__migrateUI() {
552     var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
554     var migration = 0;
555     try {
556       migration = prefBranch.getIntPref("browser.migration.version");
557     } catch(ex) {}
559     if (migration == 0) {
560       // this code should always migrate pre-FF3 profiles to the current UI state
562       // grab the localstore.rdf and make changes needed for new UI
563       this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
564       this._dataSource = this._rdf.GetDataSource("rdf:local-store");
565       this._dirty = false;
567       let currentsetResource = this._rdf.GetResource("currentset");
568       let toolbars = ["nav-bar", "toolbar-menubar", "PersonalToolbar"];
569       for (let i = 0; i < toolbars.length; i++) {
570         let toolbar = this._rdf.GetResource("chrome://browser/content/browser.xul#" + toolbars[i]);
571         let currentset = this._getPersist(toolbar, currentsetResource);
572         if (!currentset) {
573           // toolbar isn't customized
574           if (i == 0)
575             // new button is in the defaultset, nothing to migrate
576             break;
577           continue;
578         }
579         if (/(?:^|,)unified-back-forward-button(?:$|,)/.test(currentset))
580           // new button is already there, nothing to migrate
581           break;
582         if (/(?:^|,)back-button(?:$|,)/.test(currentset)) {
583           let newset = currentset.replace(/(^|,)back-button($|,)/,
584                                           "$1unified-back-forward-button,back-button$2")
585           this._setPersist(toolbar, currentsetResource, newset);
586           // done migrating
587           break;
588         }
589       }
591       // force the RDF to be saved
592       if (this._dirty)
593         this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
595       // free up the RDF service
596       this._rdf = null;
597       this._dataSource = null;
599       // update the migration version
600       prefBranch.setIntPref("browser.migration.version", 1);
601     }
602   },
604   _getPersist: function bg__getPersist(aSource, aProperty) {
605     var target = this._dataSource.GetTarget(aSource, aProperty, true);
606     if (target instanceof Ci.nsIRDFLiteral)
607       return target.Value;
608     return null;
609   },
611   _setPersist: function bg__setPersist(aSource, aProperty, aTarget) {
612     this._dirty = true;
613     try {
614       var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
615       if (oldTarget) {
616         if (aTarget)
617           this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
618         else
619           this._dataSource.Unassert(aSource, aProperty, oldTarget);
620       }
621       else {
622         this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
623       }
624     }
625     catch(ex) {}
626   },
628   // ------------------------------
629   // public nsIBrowserGlue members
630   // ------------------------------
631   
632   sanitize: function(aParentWindow) 
633   {
634     this.Sanitizer.sanitize(aParentWindow);
635   },
637   ensurePlacesDefaultQueriesInitialized: function() {
638     const SMART_BOOKMARKS_VERSION = 1;
639     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
640     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
642     // XXX should this be a pref?  see bug #399268
643     const MAX_RESULTS = 10;
645     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
646                      getService(Ci.nsIPrefBranch);
648     // get current smart bookmarks version
649     // By default, if the pref is not set up, we must create Smart Bookmarks
650     var smartBookmarksCurrentVersion = 0;
651     try {
652       smartBookmarksCurrentVersion = prefBranch.getIntPref(SMART_BOOKMARKS_PREF);
653     } catch(ex) {}
655     // bail out if we don't have to create or update Smart Bookmarks
656     if (smartBookmarksCurrentVersion == -1 ||
657         smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION)
658       return;
660     var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
661                 getService(Ci.nsINavBookmarksService);
662     var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
663                   getService(Ci.nsIAnnotationService);
665     var callback = {
666       _placesBundle: Cc["@mozilla.org/intl/stringbundle;1"].
667                      getService(Ci.nsIStringBundleService).
668                      createBundle("chrome://browser/locale/places/places.properties"),
670       _uri: function(aSpec) {
671         return Cc["@mozilla.org/network/io-service;1"].
672                getService(Ci.nsIIOService).
673                newURI(aSpec, null, null);
674       },
676       runBatched: function() {
677         var smartBookmarks = [];
678         var bookmarksMenuIndex = 0;
679         var bookmarksToolbarIndex = 0;
681         // MOST VISITED
682         var smart = {queryId: "MostVisited", // don't change this
683                      itemId: null,
684                      title: this._placesBundle.GetStringFromName("mostVisitedTitle"),
685                      uri: this._uri("place:queryType=" +
686                                     Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
687                                     "&sort=" +
688                                     Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
689                                     "&maxResults=" + MAX_RESULTS),
690                      parent: bmsvc.toolbarFolder,
691                      position: bookmarksToolbarIndex++};
692         smartBookmarks.push(smart);
694         // RECENTLY BOOKMARKED
695         smart = {queryId: "RecentlyBookmarked", // don't change this
696                  itemId: null,
697                  title: this._placesBundle.GetStringFromName("recentlyBookmarkedTitle"),
698                  uri: this._uri("place:folder=BOOKMARKS_MENU" +
699                                 "&folder=UNFILED_BOOKMARKS" +
700                                 "&folder=TOOLBAR" +
701                                 "&queryType=" +
702                                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
703                                 "&sort=" +
704                                 Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
705                                 "&excludeItemIfParentHasAnnotation=livemark%2FfeedURI" +
706                                 "&maxResults=" + MAX_RESULTS +
707                                 "&excludeQueries=1"),
708                  parent: bmsvc.bookmarksMenuFolder,
709                  position: bookmarksMenuIndex++};
710         smartBookmarks.push(smart);
712         // RECENT TAGS
713         smart = {queryId: "RecentTags", // don't change this
714                  itemId: null,
715                  title: this._placesBundle.GetStringFromName("recentTagsTitle"),
716                  uri: this._uri("place:"+
717                     "type=" +
718                     Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
719                     "&sort=" +
720                     Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
721                     "&maxResults=" + MAX_RESULTS),
722                  parent: bmsvc.bookmarksMenuFolder,
723                  position: bookmarksMenuIndex++};
724         smartBookmarks.push(smart);
726         var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, {});
727         // set current itemId, parent and position if Smart Bookmark exists
728         for each(var itemId in smartBookmarkItemIds) {
729           var queryId = annosvc.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
730           for (var i = 0; i < smartBookmarks.length; i++){
731             if (smartBookmarks[i].queryId == queryId) {
732               smartBookmarks[i].itemId = itemId;
733               smartBookmarks[i].parent = bmsvc.getFolderIdForItem(itemId);
734               smartBookmarks[i].position = bmsvc.getItemIndex(itemId);
735               // remove current item, since it will be replaced
736               bmsvc.removeItem(itemId);
737               break;
738             }
739             // We don't remove old Smart Bookmarks because user could still
740             // find them useful, or could have personalized them.
741             // Instead we remove the Smart Bookmark annotation.
742             if (i == smartBookmarks.length - 1)
743               annosvc.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
744           }
745         }
747         // create smart bookmarks
748         for each(var smartBookmark in smartBookmarks) {
749           smartBookmark.itemId = bmsvc.insertBookmark(smartBookmark.parent,
750                                                       smartBookmark.uri,
751                                                       smartBookmark.position,
752                                                       smartBookmark.title);
753           annosvc.setItemAnnotation(smartBookmark.itemId,
754                                     SMART_BOOKMARKS_ANNO, smartBookmark.queryId,
755                                     0, annosvc.EXPIRE_NEVER);
756         }
757         
758         // If we are creating all Smart Bookmarks from ground up, add a
759         // separator below them in the bookmarks menu.
760         if (smartBookmarkItemIds.length == 0)
761           bmsvc.insertSeparator(bmsvc.bookmarksMenuFolder, bookmarksMenuIndex);
762       }
763     };
765     try {
766       bmsvc.runInBatchMode(callback, null);
767     }
768     catch(ex) {
769       Components.utils.reportError(ex);
770     }
771     finally {
772       prefBranch.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
773       prefBranch.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
774     }
775   },
777   // for XPCOM
778   classDescription: "Firefox Browser Glue Service",
779   classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),
780   contractID:       "@mozilla.org/browser/browserglue;1",
782   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
783                                          Ci.nsISupportsWeakReference,
784                                          Ci.nsIBrowserGlue]),
786   // redefine the default factory for XPCOMUtils
787   _xpcom_factory: BrowserGlueServiceFactory,
789   // get this contractID registered for certain categories via XPCOMUtils
790   _xpcom_categories: [
791     // make BrowserGlue a startup observer
792     { category: "app-startup", service: true }
793   ]
796 //module initialization
797 function NSGetModule(aCompMgr, aFileSpec) {
798   return XPCOMUtils.generateModule([BrowserGlue]);