Bug 414715 - Notify the user if places.sqlite is locked and bookmarks and history will not work (r=marco, r=gavin, l10n-r=pike)

This commit is contained in:
Dietrich Ayala 2008-12-18 13:47:48 -08:00
Родитель 81d714e6a0
Коммит 3dc8ba0501
9 изменённых файлов: 263 добавлений и 48 удалений

Просмотреть файл

@ -951,7 +951,11 @@ var PlacesMenuDNDController = {
var PlacesStarButton = {
init: function PSB_init() {
PlacesUtils.bookmarks.addObserver(this, false);
try {
PlacesUtils.bookmarks.addObserver(this, false);
} catch(ex) {
Components.utils.reportError("PlacesStarButton.init(): error adding bookmark observer: " + ex);
}
},
uninit: function PSB_uninit() {

Просмотреть файл

@ -1144,7 +1144,11 @@ function prepareForStartup() {
gBrowser.browsers[0].removeAttribute("disablehistory");
// enable global history
gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
try {
gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
} catch(ex) {
Components.utils.reportError("Places database may be locked: " + ex);
}
// hook up UI through progress listener
gBrowser.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);

Просмотреть файл

@ -628,8 +628,9 @@
.getService(Components.interfaces.nsIIOService);
aURI = ios.newURI(aURI, null, null);
}
this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
aURI, false);
if (this.mFaviconService)
this.mFaviconService.setAndLoadFaviconForPage(browser.currentURI,
aURI, false);
}
this.updateIcon(aTab);
@ -728,7 +729,9 @@
aURI = ios.newURI(aURI, null, null);
}
return this.mFaviconService.isFailedFavicon(aURI);
if (this.mFaviconService)
return this.mFaviconService.isFailedFavicon(aURI);
return null;
]]>
</body>
</method>

Просмотреть файл

@ -74,12 +74,39 @@ function BrowserGlue() {
}
BrowserGlue.prototype = {
__prefs: null,
get _prefs() {
if (!this.__prefs)
this.__prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
return this.__prefs;
var prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefBranch);
this.__defineGetter__("_prefs", function() prefs);
return this._prefs;
},
get _bundleService() {
var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService);
this.__defineGetter__("_bundleService", function() bundleService);
return this._bundleService;
},
get _placesBundle() {
var strings = this._bundleService.
createBundle("chrome://browser/locale/places/places.properties");
this.__defineGetter__("_placesBundle", function() strings);
return this._placesBundle;
},
get _idleService() {
var idleSvc = Cc["@mozilla.org/widget/idleservice;1"].
getService(Ci.nsIIdleService);
this.__defineGetter__("_idleService", function() idleSvc);
return this._idleService;
},
get _observerService() {
const obssvc = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
this.__defineGetter__("_observerService", function() obssvc);
return this._observerService;
},
_saveSession: false,
@ -130,9 +157,18 @@ BrowserGlue.prototype = {
break;
case "places-init-complete":
this._initPlaces();
this._observerService.removeObserver(this, "places-init-complete");
// no longer needed, since history was initialized completely.
this._observerService.removeObserver(this, "places-database-locked");
break;
case "places-database-locked":
this._isPlacesDatabaseLocked = true;
// stop observing, so further attempts to load history service
// do not show the prompt.
this._observerService.removeObserver(this, "places-database-locked");
break;
case "idle":
if (this.idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
if (this._idleService.idleTime > BOOKMARKS_ARCHIVE_IDLE_TIME * 1000) {
// Back up bookmarks.
this._archiveBookmarks();
}
@ -144,8 +180,7 @@ BrowserGlue.prototype = {
_init: function()
{
// observer registration
const osvr = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const osvr = this._observerService;
osvr.addObserver(this, "quit-application", false);
osvr.addObserver(this, "xpcom-shutdown", false);
osvr.addObserver(this, "prefservice:after-app-defaults", false);
@ -156,14 +191,14 @@ BrowserGlue.prototype = {
osvr.addObserver(this, "quit-application-granted", false);
osvr.addObserver(this, "session-save", false);
osvr.addObserver(this, "places-init-complete", false);
osvr.addObserver(this, "places-database-locked", false);
},
// cleanup (called on application shutdown)
_dispose: function()
{
// observer removal
const osvr = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const osvr = this._observerService;
osvr.removeObserver(this, "quit-application");
osvr.removeObserver(this, "xpcom-shutdown");
osvr.removeObserver(this, "prefservice:after-app-defaults");
@ -173,7 +208,6 @@ BrowserGlue.prototype = {
osvr.removeObserver(this, "quit-application-requested");
osvr.removeObserver(this, "quit-application-granted");
osvr.removeObserver(this, "session-save");
osvr.removeObserver(this, "places-init-complete");
},
_onAppDefaults: function()
@ -206,16 +240,14 @@ BrowserGlue.prototype = {
// handle any UI migration
this._migrateUI();
const osvr = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
osvr.notifyObservers(null, "browser-ui-startup-complete", "");
this._observerService.notifyObservers(null, "browser-ui-startup-complete", "");
},
// profile shutdown handler (contains profile cleanup routines)
_onProfileShutdown: function()
{
this._shutdownPlaces();
this.idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
this._idleService.removeIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
this.Sanitizer.onShutdown();
},
@ -245,6 +277,15 @@ BrowserGlue.prototype = {
ww.openWindow(null, EMURL, "_blank", EMFEATURES, args);
this._prefs.clearUserPref(PREF_EM_NEW_ADDONS_LIST);
}
// Load the "more info" page for a locked places.sqlite
// This property is set earlier in the startup process:
// nsPlacesDBFlush loads after profile-after-change and initializes
// the history service, which sends out places-database-locked
// which sets this property.
if (this._isPlacesDatabaseLocked) {
this._showPlacesLockedNotificationBox();
}
},
_onQuitRequest: function(aCancelQuit, aQuitType)
@ -295,14 +336,12 @@ BrowserGlue.prototype = {
return false;
var buttonChoice = 0;
var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService);
var quitBundle = bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
var quitBundle = this._bundleService.createBundle("chrome://browser/locale/quitDialog.properties");
var brandBundle = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
var appName = brandBundle.GetStringFromName("brandShortName");
var quitDialogTitle = quitBundle.formatStringFromName(aQuitType + "DialogTitle",
[appName], 1);
[appName], 1);
var message;
if (aQuitType == "restart")
@ -408,10 +447,8 @@ BrowserGlue.prototype = {
var browser = win.gBrowser; // for closure in notification bar callback
var notifyBox = browser.getNotificationBox();
var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService);
var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
var rightsBundle = bundleService.createBundle("chrome://browser/locale/aboutRights.properties");
var brandBundle = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
var rightsBundle = this._bundleService.createBundle("chrome://browser/locale/aboutRights.properties");
var buttonLabel = rightsBundle.GetStringFromName("buttonLabel");
var buttonAccessKey = rightsBundle.GetStringFromName("buttonAccessKey");
@ -448,14 +485,6 @@ BrowserGlue.prototype = {
return Sanitizer;
},
_idleService: null,
get idleService() {
if (!this._idleService)
this._idleService = Cc["@mozilla.org/widget/idleservice;1"].
getService(Ci.nsIIdleService);
return this._idleService;
},
/**
* Initialize Places
* - imports the bookmarks html file if bookmarks database is empty, try to
@ -482,11 +511,12 @@ BrowserGlue.prototype = {
// forced migration (due to a major schema change).
var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
var databaseStatus = histsvc.databaseStatus;
// If the database is corrupt or has been newly created we should
// import bookmarks.
var importBookmarks = databaseStatus != histsvc.DATABASE_STATUS_OK;
var databaseStatus = histsvc.databaseStatus;
var importBookmarks = databaseStatus == histsvc.DATABASE_STATUS_CREATE ||
databaseStatus == histsvc.DATABASE_STATUS_CORRUPT;
// Check if user or an extension has required to import bookmarks.html
var importBookmarksHTML = false;
@ -584,7 +614,7 @@ BrowserGlue.prototype = {
// Initialize bookmark archiving on idle.
// Once a day, either on idle or shutdown, bookmarks are backed up.
this.idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
this._idleService.addIdleObserver(this, BOOKMARKS_ARCHIVE_IDLE_TIME);
},
/**
@ -636,6 +666,43 @@ BrowserGlue.prototype = {
}
},
/**
* Show the notificationBox for a locked places database.
*/
_showPlacesLockedNotificationBox: function nsBrowserGlue__showPlacesLockedNotificationBox() {
var brandBundle = this._bundleService.createBundle("chrome://branding/locale/brand.properties");
var applicationName = brandBundle.GetStringFromName("brandShortName");
var title = this._placesBundle.GetStringFromName("lockPrompt.title");
var text = this._placesBundle.formatStringFromName("lockPrompt.text", [applicationName], 1);
var buttonText = this._placesBundle.GetStringFromName("lockPromptInfoButton.label");
var accessKey = this._placesBundle.GetStringFromName("lockPromptInfoButton.accessKey");
var helpTopic = "places-locked";
var url = Cc["@mozilla.org/toolkit/URLFormatterService;1"].
getService(Components.interfaces.nsIURLFormatter).
formatURLPref("app.support.baseURL");
url += helpTopic;
var browser = this.getMostRecentBrowserWindow().gBrowser;
var buttons = [
{
label: buttonText,
accessKey: accessKey,
popup: null,
callback: function(aNotificationBar, aButton) {
browser.selectedTab = browser.addTab(url);
}
}
];
var notifyBox = browser.getNotificationBox();
var box = notifyBox.appendNotification(text, title, null,
notifyBox.PRIORITY_CRITICAL_MEDIUM,
buttons);
box.persistence = -1; // Until user closes it
},
_migrateUI: function bg__migrateUI() {
var migration = 0;
try {
@ -744,11 +811,10 @@ BrowserGlue.prototype = {
getService(Ci.nsINavBookmarksService);
var annosvc = Cc["@mozilla.org/browser/annotation-service;1"].
getService(Ci.nsIAnnotationService);
var strings = this._placesBundle;
var callback = {
_placesBundle: Cc["@mozilla.org/intl/stringbundle;1"].
getService(Ci.nsIStringBundleService).
createBundle("chrome://browser/locale/places/places.properties"),
_placesBundle: strings,
_uri: function(aSpec) {
return Cc["@mozilla.org/network/io-service;1"].

Просмотреть файл

@ -118,3 +118,10 @@ tagResultLabel=Tag
# for url bar autocomplete results of type "bookmark"
# See createResultLabel() in urlbarBindings.xml
bookmarkResultLabel=Bookmark
# LOCALIZATION NOTE (lockPrompt.text)
# %S will be replaced with the application name.
lockPrompt.title=Browser Startup Error
lockPrompt.text=The bookmarks and history system will not be functional because one of %S's files is in use by another application. Some security software can cause this problem.
lockPromptInfoButton.label=Learn More
lockPromptInfoButton.accessKey=L

Просмотреть файл

@ -1062,6 +1062,16 @@ interface nsINavHistoryQueryOptions : nsISupports
[scriptable, uuid(437f539b-d541-4a0f-a200-6f9a6d45cce2)]
interface nsINavHistoryService : nsISupports
{
/**
* System Notifications:
*
* places-init-complete - Sent once the History service is completely
* initialized successfully.
* places-database-locked - Sent if initialization of the History service
* failed due to the inability to open the places.sqlite
* for access reasons.
*/
/**
* This transition type means the user followed a link and got a new toplevel
* window.

Просмотреть файл

@ -282,21 +282,25 @@ protected:
nsNavHistory& mNavHistory;
};
class PlacesInitCompleteEvent : public nsRunnable {
class PlacesEvent : public nsRunnable {
public:
PlacesEvent(const char* aTopic) {
mTopic = aTopic;
}
NS_IMETHOD Run() {
nsresult rv;
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->NotifyObservers(nsnull,
PLACES_INIT_COMPLETE_EVENT_TOPIC,
nsnull);
rv = observerService->NotifyObservers(nsnull, mTopic, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
protected:
const char* mTopic;
};
// if adding a new one, be sure to update nsNavBookmarks statements and
@ -447,7 +451,7 @@ nsNavHistory::Init()
// Notify we have finished database initialization
// Enqueue the notification, so if we init another service that requires
// nsNavHistoryService we don't recursive try to get it.
nsCOMPtr<PlacesInitCompleteEvent> completeEvent = new PlacesInitCompleteEvent();
nsCOMPtr<PlacesEvent> completeEvent = new PlacesEvent(PLACES_INIT_COMPLETE_EVENT_TOPIC);
rv = NS_DispatchToMainThread(completeEvent);
NS_ENSURE_SUCCESS(rv, rv);
@ -630,6 +634,14 @@ nsNavHistory::InitDBFile(PRBool aForceInit)
NS_ENSURE_SUCCESS(rv, rv);
rv = mDBService->OpenUnsharedDatabase(mDBFile, getter_AddRefs(mDBConn));
}
if (rv != NS_OK && rv != NS_ERROR_FILE_CORRUPTED) {
// If the database cannot be opened for any reason other than corruption,
// send out a notification and do not continue initialization.
// Note: We swallow errors here, since we want service init to fail anyway.
nsCOMPtr<PlacesEvent> lockedEvent = new PlacesEvent(PLACES_DB_LOCKED_EVENT_TOPIC);
(void)NS_DispatchToMainThread(lockedEvent);
}
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

Просмотреть файл

@ -122,6 +122,7 @@
#define PRIVATEBROWSING_NOTINITED (PRBool(0xffffffff))
#define PLACES_INIT_COMPLETE_EVENT_TOPIC "places-init-complete"
#define PLACES_DB_LOCKED_EVENT_TOPIC "places-database-locked"
struct AutoCompleteIntermediateResult;
class AutoCompleteResultComparator;

Просмотреть файл

@ -0,0 +1,108 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const NS_PLACES_INIT_COMPLETE_TOPIC = "places-init-complete";
const NS_PLACES_DATABASE_LOCKED_TOPIC = "places-database-locked";
function run_test() {
do_test_pending();
// Create an observer for the Places notifications
var os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
var observer = {
_lockedNotificationReceived: false,
observe: function thn_observe(aSubject, aTopic, aData)
{
switch (aTopic) {
case NS_PLACES_INIT_COMPLETE_TOPIC:
do_check_true(this._lockedNotificationReceived);
os.removeObserver(this, NS_PLACES_INIT_COMPLETE_TOPIC);
os.removeObserver(this, NS_PLACES_DATABASE_LOCKED_TOPIC);
do_test_finished();
break;
case NS_PLACES_DATABASE_LOCKED_TOPIC:
if (this._lockedNotificationReceived)
do_throw("Locked notification should be observed only one time");
this._lockedNotificationReceived = true;
break;
}
}
};
os.addObserver(observer, NS_PLACES_INIT_COMPLETE_TOPIC, false);
os.addObserver(observer, NS_PLACES_DATABASE_LOCKED_TOPIC, false);
// Create a dummy places.sqlite and open an unshared connection on it
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
getService(Ci.nsIProperties);
var db = dirSvc.get('ProfD', Ci.nsIFile);
db.append("places.sqlite");
var storage = Cc["@mozilla.org/storage/service;1"].
getService(Ci.mozIStorageService);
var dbConn = storage.openUnsharedDatabase(db);
do_check_true(db.exists());
// We need an exclusive lock on the db
dbConn.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
// Exclusive locking is lazy applied, we need to make a write to activate it
dbConn.executeSimpleSQL("PRAGMA USER_VERSION = 1");
// Try to create history service while the db is locked
try {
var hs1 = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
do_throw("Creating an instance of history service on a locked db should throw");
} catch (ex) {}
// Close our connection and try to cleanup the file (could fail on Windows)
dbConn.close();
if (db.exists()) {
try {
db.remove(false);
} catch(e) { dump("Unable to remove dummy places.sqlite"); }
}
// Create history service correctly
try {
var hs2 = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
} catch (ex) {
do_throw("Creating an instance of history service on a not locked db should not throw");
}
}