Bug 465497, Wait for AUS to notice new snippets before testing them, r=bhearsum
[mozilla-1.9.git] / browser / components / nsBrowserGlue.js
blobe2fe1f01b7d618b49b17bf256c005ead96d1604c
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   __prefs: null,
77   get _prefs() {
78     if (!this.__prefs)
79       this.__prefs = Cc["@mozilla.org/preferences-service;1"].
80                      getService(Ci.nsIPrefBranch);
81     return this.__prefs;
82   },
84   _saveSession: false,
86   _setPrefToSaveSession: function()
87   {
88     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
89                      getService(Ci.nsIPrefBranch);
90     prefBranch.setBoolPref("browser.sessionstore.resume_session_once", true);
91   },
93   // nsIObserver implementation 
94   observe: function(subject, topic, data) 
95   {
96     switch(topic) {
97       case "xpcom-shutdown":
98         this._dispose();
99         break;
100       case "quit-application": 
101         this._onProfileShutdown();
102         break;
103       case "prefservice:after-app-defaults":
104         this._onAppDefaults();
105         break;
106       case "final-ui-startup":
107         this._onProfileStartup();
108         break;
109       case "sessionstore-windows-restored":
110         this._onBrowserStartup();
111         break;
112       case "browser:purge-session-history":
113         // reset the console service's error buffer
114         const cs = Cc["@mozilla.org/consoleservice;1"].
115                    getService(Ci.nsIConsoleService);
116         cs.logStringMessage(null); // clear the console (in case it's open)
117         cs.reset();
118         break;
119       case "quit-application-requested":
120         this._onQuitRequest(subject, data);
121         break;
122       case "quit-application-granted":
123         if (this._saveSession) {
124           this._setPrefToSaveSession();
125         }
126         break;
127       case "session-save":
128         this._setPrefToSaveSession();
129         subject.QueryInterface(Ci.nsISupportsPRBool);
130         subject.data = true;
131         break;
132       case "idle":
133         if (this.idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
134           // Back up bookmarks.
135           this._archiveBookmarks();
136         }
137         break;
138     }
139   }, 
141   // initialization (called on application startup) 
142   _init: function() 
143   {
144     // observer registration
145     const osvr = Cc['@mozilla.org/observer-service;1'].
146                  getService(Ci.nsIObserverService);
147     osvr.addObserver(this, "quit-application", false);
148     osvr.addObserver(this, "xpcom-shutdown", false);
149     osvr.addObserver(this, "prefservice:after-app-defaults", false);
150     osvr.addObserver(this, "final-ui-startup", false);
151     osvr.addObserver(this, "sessionstore-windows-restored", false);
152     osvr.addObserver(this, "browser:purge-session-history", false);
153     osvr.addObserver(this, "quit-application-requested", false);
154     osvr.addObserver(this, "quit-application-granted", false);
155     osvr.addObserver(this, "session-save", false);
156   },
158   // cleanup (called on application shutdown)
159   _dispose: function() 
160   {
161     // observer removal 
162     const osvr = Cc['@mozilla.org/observer-service;1'].
163                  getService(Ci.nsIObserverService);
164     osvr.removeObserver(this, "quit-application");
165     osvr.removeObserver(this, "xpcom-shutdown");
166     osvr.removeObserver(this, "prefservice:after-app-defaults");
167     osvr.removeObserver(this, "final-ui-startup");
168     osvr.removeObserver(this, "sessionstore-windows-restored");
169     osvr.removeObserver(this, "browser:purge-session-history");
170     osvr.removeObserver(this, "quit-application-requested");
171     osvr.removeObserver(this, "quit-application-granted");
172     osvr.removeObserver(this, "session-save");
173   },
175   _onAppDefaults: function()
176   {
177     // apply distribution customizations (prefs)
178     // other customizations are applied in _onProfileStartup()
179     var distro = new DistributionCustomizer();
180     distro.applyPrefDefaults();
181   },
183   // profile startup handler (contains profile initialization routines)
184   _onProfileStartup: function() 
185   {
186     this.Sanitizer.onStartup();
187     // check if we're in safe mode
188     var app = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
189               QueryInterface(Ci.nsIXULRuntime);
190     if (app.inSafeMode) {
191       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
192                getService(Ci.nsIWindowWatcher);
193       ww.openWindow(null, "chrome://browser/content/safeMode.xul", 
194                     "_blank", "chrome,centerscreen,modal,resizable=no", null);
195     }
197     // initialize Places
198     this._initPlaces();
200     // apply distribution customizations
201     // prefs are applied in _onAppDefaults()
202     var distro = new DistributionCustomizer();
203     distro.applyCustomizations();
205     // handle any UI migration
206     this._migrateUI();
208     const osvr = Cc['@mozilla.org/observer-service;1'].
209                  getService(Ci.nsIObserverService);
210     osvr.notifyObservers(null, "browser-ui-startup-complete", "");
211   },
213   // profile shutdown handler (contains profile cleanup routines)
214   _onProfileShutdown: function() 
215   {
216     this._shutdownPlaces();
217     this.idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
218     this.Sanitizer.onShutdown();
219   },
221   // Browser startup complete. All initial windows have opened.
222   _onBrowserStartup: function()
223   {
224     // Show about:rights notification, if needed.
225     if (this._shouldShowRights())
226       this._showRightsNotification();
228     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
229                      getService(Ci.nsIPrefBranch);
230     // If new add-ons were installed during startup open the add-ons manager.
231     if (prefBranch.prefHasUserValue(PREF_EM_NEW_ADDONS_LIST)) {
232       var args = Cc["@mozilla.org/supports-array;1"].
233                  createInstance(Ci.nsISupportsArray);
234       var str = Cc["@mozilla.org/supports-string;1"].
235                 createInstance(Ci.nsISupportsString);
236       str.data = "";
237       args.AppendElement(str);
238       var str = Cc["@mozilla.org/supports-string;1"].
239                 createInstance(Ci.nsISupportsString);
240       str.data = prefBranch.getCharPref(PREF_EM_NEW_ADDONS_LIST);
241       args.AppendElement(str);
242       const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
243       const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
244       var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
245                getService(Ci.nsIWindowWatcher);
246       ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
247       prefBranch.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
248     }
249   },
251   _onQuitRequest: function(aCancelQuit, aQuitType)
252   {
253     // If user has already dismissed quit request, then do nothing
254     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
255       return;
257     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
258              getService(Ci.nsIWindowMediator);
260     var windowcount = 0;
261     var pagecount = 0;
262     var browserEnum = wm.getEnumerator("navigator:browser");
263     while (browserEnum.hasMoreElements()) {
264       windowcount++;
266       var browser = browserEnum.getNext();
267       var tabbrowser = browser.document.getElementById("content");
268       if (tabbrowser)
269         pagecount += tabbrowser.browsers.length;
270     }
272     this._saveSession = false;
273     if (pagecount < 2)
274       return;
276     if (aQuitType != "restart")
277       aQuitType = "quit";
279     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
280                      getService(Ci.nsIPrefBranch);
281     var showPrompt = true;
282     try {
283       // browser.warnOnQuit is a hidden global boolean to override all quit prompts
284       // browser.warnOnRestart specifically covers app-initiated restarts where we restart the app
285       // browser.tabs.warnOnClose is the global "warn when closing multiple tabs" pref
287       var sessionWillBeSaved = prefBranch.getIntPref("browser.startup.page") == 3 ||
288                                prefBranch.getBoolPref("browser.sessionstore.resume_session_once");
289       if (sessionWillBeSaved || !prefBranch.getBoolPref("browser.warnOnQuit"))
290         showPrompt = false;
291       else if (aQuitType == "restart")
292         showPrompt = prefBranch.getBoolPref("browser.warnOnRestart");
293       else
294         showPrompt = prefBranch.getBoolPref("browser.tabs.warnOnClose");
295     } catch (ex) {}
297     if (!showPrompt)
298       return false;
300     var buttonChoice = 0;
301     var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
302                         getService(Ci.nsIStringBundleService);
303     var quitBundle = bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
304     var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
306     var appName = brandBundle.GetStringFromName("brandShortName");
307     var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
308                                                             [appName], 1);
310     var message;
311     if (aQuitType == "restart")
312       message = quitBundle.formatStringFromName("messageRestart",
313                                                 [appName], 1);
314     else if (windowcount == 1)
315       message = quitBundle.formatStringFromName("messageNoWindows",
316                                                 [appName], 1);
317     else
318       message = quitBundle.formatStringFromName("message",
319                                                 [appName], 1);
321     var promptService = Cc["@mozilla.org/embedcomp/prompt-service;1"].
322                         getService(Ci.nsIPromptService);
324     var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
325                 promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
326                 promptService.BUTTON_POS_0_DEFAULT;
328     var neverAsk = {value:false};
329     var button0Title, button2Title;
330     var button1Title = quitBundle.GetStringFromName("cancelTitle");
331     var neverAskText = quitBundle.GetStringFromName("neverAsk");
333     if (aQuitType == "restart")
334       button0Title = quitBundle.GetStringFromName("restartTitle");
335     else {
336       flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
337       button0Title = quitBundle.GetStringFromName("saveTitle");
338       button2Title = quitBundle.GetStringFromName("quitTitle");
339     }
341     buttonChoice = promptService.confirmEx(null, quitDialogTitle, message,
342                                  flags, button0Title, button1Title, button2Title,
343                                  neverAskText, neverAsk);
345     switch (buttonChoice) {
346     case 2: // Quit
347       if (neverAsk.value)
348         prefBranch.setBoolPref("browser.tabs.warnOnClose", false);
349       break;
350     case 1: // Cancel
351       aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
352       aCancelQuit.data = true;
353       break;
354     case 0: // Save & Quit
355       this._saveSession = true;
356       if (neverAsk.value) {
357         if (aQuitType == "restart")
358           prefBranch.setBoolPref("browser.warnOnRestart", false);
359         else {
360           // always save state when shutting down
361           prefBranch.setIntPref("browser.startup.page", 3);
362         }
363       }
364       break;
365     }
366   },
368   /*
369    * _shouldShowRights - Determines if the user should be shown the
370    * about:rights notification. The notification should *not* be shown if
371    * we've already shown the current version, or if the override pref says to
372    * never show it. The notification *should* be shown if it's never been seen
373    * before, if a newer version is available, or if the override pref says to
374    * always show it.
375    */
376   _shouldShowRights : function () {
377     // Look for an unconditional override pref. If set, do what it says.
378     // (true --> never show, false --> always show)
379     try {
380       return !this._prefs.getBoolPref("browser.rights.override");
381     } catch (e) { }
382     // Ditto, for the legacy EULA pref.
383     try {
384       return !this._prefs.getBoolPref("browser.EULA.override");
385     } catch (e) { }
387 #ifndef OFFICIAL_BUILD
388     // Non-official builds shouldn't shouldn't show the notification.
389     return false;
390 #endif
392     // Look to see if the user has seen the current version or not.
393     var currentVersion = this._prefs.getIntPref("browser.rights.version");
394     try {
395       return !this._prefs.getBoolPref("browser.rights." + currentVersion + ".shown");
396     } catch (e) { }
398     // Legacy: If the user accepted a EULA, we won't annoy them with the
399     // equivalent about:rights page until the version changes.
400     try {
401       return !this._prefs.getBoolPref("browser.EULA." + currentVersion + ".accepted");
402     } catch (e) { }
404     // We haven't shown the notification before, so do so now.
405     return true;
406   },
408   _showRightsNotification : function () {
409     // Stick the notification onto the selected tab of the active browser window.
410     var win = this._getMostRecentBrowserWindow();
411     var browser = win.gBrowser; // for closure in notification bar callback
412     var notifyBox = browser.getNotificationBox();
414     var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
415                         getService(Ci.nsIStringBundleService);
416     var brandBundle  = bundleService.createBundle("chrome://branding/locale/brand.properties");
417     var rightsBundle = bundleService.createBundle("chrome://browser/locale/aboutRights.properties");
419     var buttonLabel     = rightsBundle.GetStringFromName("buttonLabel");
420     var buttonAccessKey = rightsBundle.GetStringFromName("buttonAccessKey");
421     var productName     = brandBundle.GetStringFromName("brandFullName");
422     var notifyText      = rightsBundle.formatStringFromName("notifyText", [productName], 1);
423     
424     var buttons = [
425                     {
426                       label:     buttonLabel,
427                       accessKey: buttonAccessKey,
428                       popup:     null,
429                       callback: function(aNotificationBar, aButton) {
430                         browser.selectedTab = browser.addTab("about:rights");
431                       }
432                     }
433                   ];
435     // Set pref to indicate we've shown the notification.
436     var currentVersion = this._prefs.getIntPref("browser.rights.version");
437     this._prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
439     notifyBox.appendNotification(notifyText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons);
440   },
442   // returns the (cached) Sanitizer constructor
443   get Sanitizer() 
444   {
445     if(typeof(Sanitizer) != "function") { // we should dynamically load the script
446       Cc["@mozilla.org/moz/jssubscript-loader;1"].
447       getService(Ci.mozIJSSubScriptLoader).
448       loadSubScript("chrome://browser/content/sanitize.js", null);
449     }
450     return Sanitizer;
451   },
453   _idleService: null,
454   get idleService() {
455     if (!this._idleService)
456       this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
457                           getService(Ci.nsIIdleService);
458     return this._idleService;
459   },
461   /**
462    * Initialize Places
463    * - imports the bookmarks html file if bookmarks datastore is empty
464    *
465    * These prefs are set by the backend services upon creation (or recreation)
466    * of the Places db:
467    * - browser.places.importBookmarksHTML
468    *   Set to false by the history service to indicate we need to re-import.
469    * - browser.places.smartBookmarksVersion
470    *   Set during HTML import to indicate that Smart Bookmarks were created.
471    *   Set to -1 to disable Smart Bookmarks creation.
472    *   Set to 0 to restore current Smart Bookmarks.
473    *
474    * These prefs are set up by the frontend:
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 need to instantiate the history service before checking
481     // the browser.places.importBookmarksHTML pref, as
482     // nsNavHistory::ForceMigrateBookmarksDB() will set that pref
483     // if we need to force a migration (due to a schema change)
484     var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
485                   getService(Ci.nsINavHistoryService);
487     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
488                      getService(Ci.nsIPrefBranch);
490     var importBookmarks = false;
491     var restoreDefaultBookmarks = false;
492     try {
493       restoreDefaultBookmarks = prefBranch.getBoolPref("browser.bookmarks.restore_default_bookmarks");
494     } catch(ex) {}
496     if (restoreDefaultBookmarks) {
497       // Ensure that we already have a bookmarks backup for today
498       this._archiveBookmarks();
499       // we will restore bookmarks from html
500       importBookmarks = true;
501     }
502     else {
503       try {
504         importBookmarks = prefBranch.getBoolPref("browser.places.importBookmarksHTML");
505       } catch(ex) {}
506     }
508     if (!importBookmarks) {
509       // Call it here for Fx3 profiles created before the Places folder
510       // has been added, otherwise it's called during import.
511       this.ensurePlacesDefaultQueriesInitialized();
512     }
513     else {
514       // get latest backup
515       Cu.import("resource://gre/modules/utils.js");
516       var bookmarksFile = PlacesUtils.getMostRecentBackup();
518       if (!restoreDefaultBookmarks &&
519           bookmarksFile && bookmarksFile.leafName.match("\.json$")) {
520         // restore a JSON backup
521         PlacesUtils.restoreBookmarksFromJSONFile(bookmarksFile);
522       }
523       else {
524         // if there's no JSON backup or we are restoring default bookmarks
526         // ensurePlacesDefaultQueriesInitialized() is called by import.
527         prefBranch.setIntPref("browser.places.smartBookmarksVersion", 0);
529         var dirService = Cc["@mozilla.org/file/directory_service;1"].
530                          getService(Ci.nsIProperties);
532         var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
533         if (restoreDefaultBookmarks || !bookmarksFile.exists()) {
534           // get bookmarks.html file from default profile folder
535           bookmarksFile = dirService.get("profDef", Ci.nsILocalFile);
536           bookmarksFile.append("bookmarks.html");
537         }
539         // import the file
540         try {
541           var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
542                          getService(Ci.nsIPlacesImportExportService);
543           importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */);
544         } catch (err) {
545           // Report the error, but ignore it.
546           Cu.reportError(err);
547         }
548         prefBranch.setBoolPref("browser.places.importBookmarksHTML", false);
549         if (restoreDefaultBookmarks)
550           prefBranch.setBoolPref("browser.bookmarks.restore_default_bookmarks",
551                                  false);
552       }
553     }
555     // Initialize bookmark archiving on idle.
556     // Once a day, either on idle or shutdown, bookmarks are backed up.
557     this.idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
558   },
560   /**
561    * Places shut-down tasks
562    * - back up and archive bookmarks
563    * - export bookmarks as HTML, if so configured
564    *
565    * Note: quit-application-granted notification is received twice
566    *       so replace this method with a no-op when first called.
567    */
568   _shutdownPlaces: function bg__shutdownPlaces() {
569     // Backup and archive Places bookmarks.
570     this._archiveBookmarks();
572     // Backup bookmarks to bookmarks.html to support apps that depend
573     // on the legacy format.
574     var prefs = Cc["@mozilla.org/preferences-service;1"].
575                 getService(Ci.nsIPrefBranch);
576     var autoExportHTML = false;
577     try {
578       autoExportHTML = prefs.getBoolPref("browser.bookmarks.autoExportHTML");
579     } catch(ex) {
580       Components.utils.reportError(ex);
581     }
583     if (autoExportHTML) {
584       Cc["@mozilla.org/browser/places/import-export-service;1"].
585         getService(Ci.nsIPlacesImportExportService).
586         backupBookmarksFile();
587     }
588   },
590   /**
591    * Back up and archive bookmarks
592    */
593   _archiveBookmarks: function nsBrowserGlue__archiveBookmarks() {
594     Cu.import("resource://gre/modules/utils.js");
596     var lastBackup = PlacesUtils.getMostRecentBackup();
598     // Backup bookmarks if there aren't any backups or 
599     // they haven't been backed up in the last 24 hrs.
600     if (!lastBackup ||
601         Date.now() - lastBackup.lastModifiedTime > BOOKMARKS_ARCHIVE_INTERVAL) {
602       var maxBackups = 5;
603       var prefs = Cc["@mozilla.org/preferences-service;1"].
604                   getService(Ci.nsIPrefBranch);
605       try {
606         maxBackups = prefs.getIntPref("browser.bookmarks.max_backups");
607       } catch(ex) {}
609       PlacesUtils.archiveBookmarksFile(maxBackups, false /* don't force */);
610     }
611   },
613   _migrateUI: function bg__migrateUI() {
614     var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
616     var migration = 0;
617     try {
618       migration = prefBranch.getIntPref("browser.migration.version");
619     } catch(ex) {}
621     if (migration == 0) {
622       // this code should always migrate pre-FF3 profiles to the current UI state
624       // grab the localstore.rdf and make changes needed for new UI
625       this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
626       this._dataSource = this._rdf.GetDataSource("rdf:local-store");
627       this._dirty = false;
629       let currentsetResource = this._rdf.GetResource("currentset");
630       let toolbars = ["nav-bar", "toolbar-menubar", "PersonalToolbar"];
631       for (let i = 0; i < toolbars.length; i++) {
632         let toolbar = this._rdf.GetResource("chrome://browser/content/browser.xul#" + toolbars[i]);
633         let currentset = this._getPersist(toolbar, currentsetResource);
634         if (!currentset) {
635           // toolbar isn't customized
636           if (i == 0)
637             // new button is in the defaultset, nothing to migrate
638             break;
639           continue;
640         }
641         if (/(?:^|,)unified-back-forward-button(?:$|,)/.test(currentset))
642           // new button is already there, nothing to migrate
643           break;
644         if (/(?:^|,)back-button(?:$|,)/.test(currentset)) {
645           let newset = currentset.replace(/(^|,)back-button($|,)/,
646                                           "$1unified-back-forward-button,back-button$2")
647           this._setPersist(toolbar, currentsetResource, newset);
648           // done migrating
649           break;
650         }
651       }
653       // force the RDF to be saved
654       if (this._dirty)
655         this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
657       // free up the RDF service
658       this._rdf = null;
659       this._dataSource = null;
661       // update the migration version
662       prefBranch.setIntPref("browser.migration.version", 1);
663     }
664   },
666   _getPersist: function bg__getPersist(aSource, aProperty) {
667     var target = this._dataSource.GetTarget(aSource, aProperty, true);
668     if (target instanceof Ci.nsIRDFLiteral)
669       return target.Value;
670     return null;
671   },
673   _setPersist: function bg__setPersist(aSource, aProperty, aTarget) {
674     this._dirty = true;
675     try {
676       var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
677       if (oldTarget) {
678         if (aTarget)
679           this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
680         else
681           this._dataSource.Unassert(aSource, aProperty, oldTarget);
682       }
683       else {
684         this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
685       }
686     }
687     catch(ex) {}
688   },
690   // ------------------------------
691   // public nsIBrowserGlue members
692   // ------------------------------
693   
694   sanitize: function(aParentWindow) 
695   {
696     this.Sanitizer.sanitize(aParentWindow);
697   },
699   ensurePlacesDefaultQueriesInitialized: function() {
700     const SMART_BOOKMARKS_VERSION = 1;
701     const SMART_BOOKMARKS_ANNO = "Places/SmartBookmark";
702     const SMART_BOOKMARKS_PREF = "browser.places.smartBookmarksVersion";
704     // XXX should this be a pref?  see bug #399268
705     const MAX_RESULTS = 10;
707     var prefBranch = Cc["@mozilla.org/preferences-service;1"].
708                      getService(Ci.nsIPrefBranch);
710     // get current smart bookmarks version
711     // By default, if the pref is not set up, we must create Smart Bookmarks
712     var smartBookmarksCurrentVersion = 0;
713     try {
714       smartBookmarksCurrentVersion = prefBranch.getIntPref(SMART_BOOKMARKS_PREF);
715     } catch(ex) {}
717     // bail out if we don't have to create or update Smart Bookmarks
718     if (smartBookmarksCurrentVersion == -1 ||
719         smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION)
720       return;
722     var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
723                 getService(Ci.nsINavBookmarksService);
724     var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
725                   getService(Ci.nsIAnnotationService);
727     var callback = {
728       _placesBundle: Cc["@mozilla.org/intl/stringbundle;1"].
729                      getService(Ci.nsIStringBundleService).
730                      createBundle("chrome://browser/locale/places/places.properties"),
732       _uri: function(aSpec) {
733         return Cc["@mozilla.org/network/io-service;1"].
734                getService(Ci.nsIIOService).
735                newURI(aSpec, null, null);
736       },
738       runBatched: function() {
739         var smartBookmarks = [];
740         var bookmarksMenuIndex = 0;
741         var bookmarksToolbarIndex = 0;
743         // MOST VISITED
744         var smart = {queryId: "MostVisited", // don't change this
745                      itemId: null,
746                      title: this._placesBundle.GetStringFromName("mostVisitedTitle"),
747                      uri: this._uri("place:queryType=" +
748                                     Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
749                                     "&sort=" +
750                                     Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
751                                     "&maxResults=" + MAX_RESULTS),
752                      parent: bmsvc.toolbarFolder,
753                      position: bookmarksToolbarIndex++};
754         smartBookmarks.push(smart);
756         // RECENTLY BOOKMARKED
757         smart = {queryId: "RecentlyBookmarked", // don't change this
758                  itemId: null,
759                  title: this._placesBundle.GetStringFromName("recentlyBookmarkedTitle"),
760                  uri: this._uri("place:folder=BOOKMARKS_MENU" +
761                                 "&folder=UNFILED_BOOKMARKS" +
762                                 "&folder=TOOLBAR" +
763                                 "&queryType=" +
764                                 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
765                                 "&sort=" +
766                                 Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
767                                 "&excludeItemIfParentHasAnnotation=livemark%2FfeedURI" +
768                                 "&maxResults=" + MAX_RESULTS +
769                                 "&excludeQueries=1"),
770                  parent: bmsvc.bookmarksMenuFolder,
771                  position: bookmarksMenuIndex++};
772         smartBookmarks.push(smart);
774         // RECENT TAGS
775         smart = {queryId: "RecentTags", // don't change this
776                  itemId: null,
777                  title: this._placesBundle.GetStringFromName("recentTagsTitle"),
778                  uri: this._uri("place:"+
779                     "type=" +
780                     Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
781                     "&sort=" +
782                     Ci.nsINavHistoryQueryOptions.SORT_BY_LASTMODIFIED_DESCENDING +
783                     "&maxResults=" + MAX_RESULTS),
784                  parent: bmsvc.bookmarksMenuFolder,
785                  position: bookmarksMenuIndex++};
786         smartBookmarks.push(smart);
788         var smartBookmarkItemIds = annosvc.getItemsWithAnnotation(SMART_BOOKMARKS_ANNO, {});
789         // set current itemId, parent and position if Smart Bookmark exists
790         for each(var itemId in smartBookmarkItemIds) {
791           var queryId = annosvc.getItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
792           for (var i = 0; i < smartBookmarks.length; i++){
793             if (smartBookmarks[i].queryId == queryId) {
794               smartBookmarks[i].itemId = itemId;
795               smartBookmarks[i].parent = bmsvc.getFolderIdForItem(itemId);
796               smartBookmarks[i].position = bmsvc.getItemIndex(itemId);
797               // remove current item, since it will be replaced
798               bmsvc.removeItem(itemId);
799               break;
800             }
801             // We don't remove old Smart Bookmarks because user could still
802             // find them useful, or could have personalized them.
803             // Instead we remove the Smart Bookmark annotation.
804             if (i == smartBookmarks.length - 1)
805               annosvc.removeItemAnnotation(itemId, SMART_BOOKMARKS_ANNO);
806           }
807         }
809         // create smart bookmarks
810         for each(var smartBookmark in smartBookmarks) {
811           smartBookmark.itemId = bmsvc.insertBookmark(smartBookmark.parent,
812                                                       smartBookmark.uri,
813                                                       smartBookmark.position,
814                                                       smartBookmark.title);
815           annosvc.setItemAnnotation(smartBookmark.itemId,
816                                     SMART_BOOKMARKS_ANNO, smartBookmark.queryId,
817                                     0, annosvc.EXPIRE_NEVER);
818         }
819         
820         // If we are creating all Smart Bookmarks from ground up, add a
821         // separator below them in the bookmarks menu.
822         if (smartBookmarkItemIds.length == 0)
823           bmsvc.insertSeparator(bmsvc.bookmarksMenuFolder, bookmarksMenuIndex);
824       }
825     };
827     try {
828       bmsvc.runInBatchMode(callback, null);
829     }
830     catch(ex) {
831       Components.utils.reportError(ex);
832     }
833     finally {
834       prefBranch.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
835       prefBranch.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
836     }
837   },
839 #ifdef XP_UNIX
840 #ifndef XP_MACOSX
841 #define BROKEN_WM_Z_ORDER
842 #endif
843 #endif
844 #ifdef XP_OS2
845 #define BROKEN_WM_Z_ORDER
846 #endif
848   // this returns the most recent non-popup browser window
849   _getMostRecentBrowserWindow : function ()
850   {
851     var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
852              getService(Components.interfaces.nsIWindowMediator);
854 #ifdef BROKEN_WM_Z_ORDER
855     var win = wm.getMostRecentWindow("navigator:browser", true);
857     // if we're lucky, this isn't a popup, and we can just return this
858     if (win && win.document.documentElement.getAttribute("chromehidden")) {
859       win = null;
860       var windowList = wm.getEnumerator("navigator:browser", true);
861       // this is oldest to newest, so this gets a bit ugly
862       while (windowList.hasMoreElements()) {
863         var nextWin = windowList.getNext();
864         if (!nextWin.document.documentElement.getAttribute("chromehidden"))
865           win = nextWin;
866       }
867     }
868 #else
869     var windowList = wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
870     if (!windowList.hasMoreElements())
871       return null;
873     var win = windowList.getNext();
874     while (win.document.documentElement.getAttribute("chromehidden")) {
875       if (!windowList.hasMoreElements())
876         return null;
878       win = windowList.getNext();
879     }
880 #endif
882     return win;
883   },
885   // for XPCOM
886   classDescription: "Firefox Browser Glue Service",
887   classID:          Components.ID("{eab9012e-5f74-4cbc-b2b5-a590235513cc}"),
888   contractID:       "@mozilla.org/browser/browserglue;1",
890   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
891                                          Ci.nsISupportsWeakReference,
892                                          Ci.nsIBrowserGlue]),
894   // redefine the default factory for XPCOMUtils
895   _xpcom_factory: BrowserGlueServiceFactory,
897   // get this contractID registered for certain categories via XPCOMUtils
898   _xpcom_categories: [
899     // make BrowserGlue a startup observer
900     { category: "app-startup", service: true }
901   ]
904 //module initialization
905 function NSGetModule(aCompMgr, aFileSpec) {
906   return XPCOMUtils.generateModule([BrowserGlue]);