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
14 # The Original Code is the Browser Search Service.
16 # The Initial Developer of the Original Code is
18 # Portions created by the Initial Developer are Copyright (C) 2005
19 # the Initial Developer. All Rights Reserved.
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;
58 const BrowserGlueServiceFactory = {
60 createInstance: function (outer, iid)
63 throw Components.results.NS_ERROR_NO_AGGREGATION;
64 return this._instance == null ?
65 this._instance = new BrowserGlue() : this._instance;
71 function BrowserGlue() {
75 BrowserGlue.prototype = {
78 _setPrefToSaveSession: function()
80 var prefBranch = Cc["@mozilla.org/preferences-service;1"].
81 getService(Ci.nsIPrefBranch);
82 prefBranch.setBoolPref("browser.sessionstore.resume_session_once", true);
85 // nsIObserver implementation
86 observe: function(subject, topic, data)
89 case "xpcom-shutdown":
92 case "quit-application":
93 this._onProfileShutdown();
95 case "prefservice:after-app-defaults":
96 this._onAppDefaults();
98 case "final-ui-startup":
99 this._onProfileStartup();
101 case "sessionstore-windows-restored":
102 this._onBrowserStartup();
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)
111 case "quit-application-requested":
112 this._onQuitRequest(subject, data);
114 case "quit-application-granted":
115 if (this._saveSession) {
116 this._setPrefToSaveSession();
120 this._setPrefToSaveSession();
121 subject.QueryInterface(Ci.nsISupportsPRBool);
125 if (this.idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
126 // Back up bookmarks.
127 this._archiveBookmarks();
133 // initialization (called on application startup)
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);
150 // cleanup (called on application shutdown)
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");
167 _onAppDefaults: function()
169 // apply distribution customizations (prefs)
170 // other customizations are applied in _onProfileStartup()
171 var distro = new DistributionCustomizer();
172 distro.applyPrefDefaults();
175 // profile startup handler (contains profile initialization routines)
176 _onProfileStartup: function()
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;
185 mustDisplayEULA = !prefBranch.getBoolPref("browser.EULA.override");
187 // Pref might not exist
190 // Make sure it hasn't already been accepted
191 if (mustDisplayEULA) {
193 var EULAVersion = prefBranch.getIntPref("browser.EULA.version");
194 mustDisplayEULA = !prefBranch.getBoolPref("browser.EULA." + EULAVersion + ".accepted");
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);
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);
220 // apply distribution customizations
221 // prefs are applied in _onAppDefaults()
222 var distro = new DistributionCustomizer();
223 distro.applyCustomizations();
225 // handle any UI migration
229 // profile shutdown handler (contains profile cleanup routines)
230 _onProfileShutdown: function()
232 this._shutdownPlaces();
233 this.idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
234 this.Sanitizer.onShutdown();
237 // Browser startup complete. All initial windows have opened.
238 _onBrowserStartup: function()
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);
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);
263 _onQuitRequest: function(aCancelQuit, aQuitType)
265 // If user has already dismissed quit request, then do nothing
266 if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
269 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
270 getService(Ci.nsIWindowMediator);
274 var browserEnum = wm.getEnumerator("navigator:browser");
275 while (browserEnum.hasMoreElements()) {
278 var browser = browserEnum.getNext();
279 var tabbrowser = browser.document.getElementById("content");
281 pagecount += tabbrowser.browsers.length;
284 this._saveSession = false;
288 if (aQuitType != "restart")
291 var prefBranch = Cc["@mozilla.org/preferences-service;1"].
292 getService(Ci.nsIPrefBranch);
293 var showPrompt = true;
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"))
303 else if (aQuitType == "restart")
304 showPrompt = prefBranch.getBoolPref("browser.warnOnRestart");
306 showPrompt = prefBranch.getBoolPref("browser.tabs.warnOnClose");
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",
323 if (aQuitType == "restart")
324 message = quitBundle.formatStringFromName("messageRestart",
326 else if (windowcount == 1)
327 message = quitBundle.formatStringFromName("messageNoWindows",
330 message = quitBundle.formatStringFromName("message",
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");
348 flags += promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
349 button0Title = quitBundle.GetStringFromName("saveTitle");
350 button2Title = quitBundle.GetStringFromName("quitTitle");
353 buttonChoice = promptService.confirmEx(null, quitDialogTitle, message,
354 flags, button0Title, button1Title, button2Title,
355 neverAskText, neverAsk);
357 switch (buttonChoice) {
360 prefBranch.setBoolPref("browser.tabs.warnOnClose", false);
363 aCancelQuit.QueryInterface(Ci.nsISupportsPRBool);
364 aCancelQuit.data = true;
366 case 0: // Save & Quit
367 this._saveSession = true;
368 if (neverAsk.value) {
369 if (aQuitType == "restart")
370 prefBranch.setBoolPref("browser.warnOnRestart", false);
372 // always save state when shutting down
373 prefBranch.setIntPref("browser.startup.page", 3);
380 // returns the (cached) Sanitizer constructor
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);
393 if (!this._idleService)
394 this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
395 getService(Ci.nsIIdleService);
396 return this._idleService;
401 * - imports the bookmarks html file if bookmarks datastore is empty
403 * These prefs are set by the backend services upon creation (or recreation)
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.
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
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;
431 restoreDefaultBookmarks = prefBranch.getBoolPref("browser.bookmarks.restore_default_bookmarks");
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;
442 importBookmarks = prefBranch.getBoolPref("browser.places.importBookmarksHTML");
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();
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);
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");
479 var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
480 getService(Ci.nsIPlacesImportExportService);
481 importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */);
483 // Report the error, but ignore it.
486 prefBranch.setBoolPref("browser.places.importBookmarksHTML", false);
487 if (restoreDefaultBookmarks)
488 prefBranch.setBoolPref("browser.bookmarks.restore_default_bookmarks",
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);
499 * Places shut-down tasks
500 * - back up and archive bookmarks
501 * - export bookmarks as HTML, if so configured
503 * Note: quit-application-granted notification is received twice
504 * so replace this method with a no-op when first called.
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;
516 autoExportHTML = prefs.getBoolPref("browser.bookmarks.autoExportHTML");
518 Components.utils.reportError(ex);
521 if (autoExportHTML) {
522 Cc["@mozilla.org/browser/places/import-export-service;1"].
523 getService(Ci.nsIPlacesImportExportService).
524 backupBookmarksFile();
529 * Back up and archive bookmarks
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.
539 Date.now() - lastBackup.lastModifiedTime > BOOKMARKS_ARCHIVE_INTERVAL) {
541 var prefs = Cc["@mozilla.org/preferences-service;1"].
542 getService(Ci.nsIPrefBranch);
544 maxBackups = prefs.getIntPref("browser.bookmarks.max_backups");
547 PlacesUtils.archiveBookmarksFile(maxBackups, false /* don't force */);
551 _migrateUI: function bg__migrateUI() {
552 var prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
556 migration = prefBranch.getIntPref("browser.migration.version");
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");
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);
573 // toolbar isn't customized
575 // new button is in the defaultset, nothing to migrate
579 if (/(?:^|,)unified-back-forward-button(?:$|,)/.test(currentset))
580 // new button is already there, nothing to migrate
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);
591 // force the RDF to be saved
593 this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
595 // free up the RDF service
597 this._dataSource = null;
599 // update the migration version
600 prefBranch.setIntPref("browser.migration.version", 1);
604 _getPersist: function bg__getPersist(aSource, aProperty) {
605 var target = this._dataSource.GetTarget(aSource, aProperty, true);
606 if (target instanceof Ci.nsIRDFLiteral)
611 _setPersist: function bg__setPersist(aSource, aProperty, aTarget) {
614 var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
617 this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
619 this._dataSource.Unassert(aSource, aProperty, oldTarget);
622 this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
628 // ------------------------------
629 // public nsIBrowserGlue members
630 // ------------------------------
632 sanitize: function(aParentWindow)
634 this.Sanitizer.sanitize(aParentWindow);
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;
652 smartBookmarksCurrentVersion = prefBranch.getIntPref(SMART_BOOKMARKS_PREF);
655 // bail out if we don't have to create or update Smart Bookmarks
656 if (smartBookmarksCurrentVersion == -1 ||
657 smartBookmarksCurrentVersion >= SMART_BOOKMARKS_VERSION)
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);
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);
676 runBatched: function() {
677 var smartBookmarks = [];
678 var bookmarksMenuIndex = 0;
679 var bookmarksToolbarIndex = 0;
682 var smart = {queryId: "MostVisited", // don't change this
684 title: this._placesBundle.GetStringFromName("mostVisitedTitle"),
685 uri: this._uri("place:queryType=" +
686 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
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
697 title: this._placesBundle.GetStringFromName("recentlyBookmarkedTitle"),
698 uri: this._uri("place:folder=BOOKMARKS_MENU" +
699 "&folder=UNFILED_BOOKMARKS" +
702 Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
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);
713 smart = {queryId: "RecentTags", // don't change this
715 title: this._placesBundle.GetStringFromName("recentTagsTitle"),
716 uri: this._uri("place:"+
718 Ci.nsINavHistoryQueryOptions.RESULTS_AS_TAG_QUERY +
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);
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);
747 // create smart bookmarks
748 for each(var smartBookmark in smartBookmarks) {
749 smartBookmark.itemId = bmsvc.insertBookmark(smartBookmark.parent,
751 smartBookmark.position,
752 smartBookmark.title);
753 annosvc.setItemAnnotation(smartBookmark.itemId,
754 SMART_BOOKMARKS_ANNO, smartBookmark.queryId,
755 0, annosvc.EXPIRE_NEVER);
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);
766 bmsvc.runInBatchMode(callback, null);
769 Components.utils.reportError(ex);
772 prefBranch.setIntPref(SMART_BOOKMARKS_PREF, SMART_BOOKMARKS_VERSION);
773 prefBranch.QueryInterface(Ci.nsIPrefService).savePrefFile(null);
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,
786 // redefine the default factory for XPCOMUtils
787 _xpcom_factory: BrowserGlueServiceFactory,
789 // get this contractID registered for certain categories via XPCOMUtils
791 // make BrowserGlue a startup observer
792 { category: "app-startup", service: true }
796 //module initialization
797 function NSGetModule(aCompMgr, aFileSpec) {
798 return XPCOMUtils.generateModule([BrowserGlue]);