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