Use asynchronous DB and file access for schema checks/updates

This change should improve Firefox startup time. If the Zotero pane is
opened before Zotero has initialized, the pane will be blocked with a
progress bar until it's done. Standalone currently does the same, but it
should be changed to just delay opening the window if possible.

The upgrade wizard has been disabled for schema upgrades, in the hope
that upgrades can be done in a way that won't break compatibility with
earlier versions (or that can at least be done in a way such that we can
put out point releases of the last major version that provide
compatibility during beta/post-upgrade periods).

This patch likely breaks many things, and definitely breaks connector
mode.

This change also removes upgrade steps for databases from Zotero 2.1b2
and earlier. Users with such databases will need to upgrade via Zotero
4.0.x first or delete their data directories and start anew. This should
only affect users who haven't opened Zotero since Nov. 2010.
This commit is contained in:
Dan Stillman 2013-08-11 20:51:16 -04:00
parent 2b4de89aaf
commit 5787b230f7
13 changed files with 862 additions and 2396 deletions

View File

@ -101,7 +101,7 @@ var Zotero_Browser = new function() {
* Initialize some variables and prepare event listeners for when chrome is done loading
*/
function init() {
if (!Zotero || !Zotero.initialized || !window.hasOwnProperty("gBrowser")) {
if (!Zotero || Zotero.skipLoading || !window.hasOwnProperty("gBrowser")) {
return;
}

View File

@ -669,6 +669,6 @@ Zotero_File_Interface.Progress = new function() {
}
function close() {
Zotero.hideZoteroPaneOverlay();
Zotero.hideZoteroPaneOverlays();
}
}

View File

@ -24,17 +24,16 @@
*/
var ZoteroItemPane = new function() {
this.onLoad = onLoad;
var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox;
function onLoad()
{
if (!Zotero || !Zotero.initialized) {
this.onLoad = function () {
if (!Zotero) {
return;
}
// Not in item pane, so skip the introductions
//
// DEBUG: remove?
if (!document.getElementById('zotero-view-tabbox')) {
return;
}

View File

@ -32,15 +32,47 @@ var ZoteroOverlay = new function()
var toolbarCollapseState, showInPref;
var zoteroPane, zoteroSplitter;
var _stateBeforeReload = false;
var _initializationDeferred, _initializationPromise;
this.isTab = false;
this.onLoad = function() {
try {
zoteroPane = document.getElementById('zotero-pane-stack');
zoteroSplitter = document.getElementById('zotero-splitter');
// Make Zotero icon visible, if requested
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch('extensions.zotero.');
var addonBar = document.getElementById('addon-bar');
var iconPref = prefBranch.getIntPref('statusBarIcon');
// If this is the first run, add icon to add-on bar if not
// in the window already and not hidden by the Zotero prefs
if (!document.getElementById("zotero-toolbar-button") && iconPref != 0) {
addonBar.insertItem("zotero-toolbar-button");
addonBar.setAttribute("currentset", addonBar.currentSet);
document.persist(addonBar.id, "currentset");
addonBar.setAttribute("collapsed", false);
document.persist(addonBar.id, "collapsed");
}
var icon = document.getElementById('zotero-toolbar-button');
var self = this;
Q.fcall(function () {
if (!Zotero || Zotero.skipLoading) {
throw true;
}
return Zotero.unlockPromise;
})
.then(function () {
Zotero.debug("Initializing overlay");
if (Zotero.skipLoading) {
throw true;
}
ZoteroPane_Overlay = ZoteroPane;
ZoteroPane.init();
@ -48,7 +80,7 @@ var ZoteroOverlay = new function()
showInPref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch('extensions.zotero.').getIntPref('showIn');
this.isTab = showInPref !== 1;
self.isTab = showInPref !== 1;
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
@ -70,31 +102,9 @@ var ZoteroOverlay = new function()
observerService.addObserver(zoteroObserver, "browser-delayed-startup-finished", false);
// Make Zotero icon visible, if requested
var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch('extensions.zotero.');
var addonBar = document.getElementById('addon-bar');
var iconPref = prefBranch.getIntPref('statusBarIcon');
// If this is the first run, add icon to add-on bar if not
// in the window already and not hidden by the Zotero prefs
if (!document.getElementById("zotero-toolbar-button") && iconPref != 0) {
addonBar.insertItem("zotero-toolbar-button");
addonBar.setAttribute("currentset", addonBar.currentSet);
document.persist(addonBar.id, "currentset");
addonBar.setAttribute("collapsed", false);
document.persist(addonBar.id, "collapsed");
}
var icon = document.getElementById('zotero-toolbar-button');
// Add a listener for toolbar change events
window.addEventListener("customizationchange", onToolbarChange, false);
if (Zotero && Zotero.initialized){
document.getElementById('appcontent').addEventListener('mousemove', Zotero.ProgressWindowSet.updateTimers, false);
if (icon) {
if (iconPref == 1) {
@ -111,32 +121,9 @@ var ZoteroOverlay = new function()
}
}
}
}
else {
if (Zotero) {
var errMsg = Zotero.startupError;
}
// Use defaults if necessary
if (!errMsg) {
// Get the stringbundle manually
var src = 'chrome://zotero/locale/zotero.properties';
var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
getService(Components.interfaces.nsILocaleService);
var appLocale = localeService.getApplicationLocale();
var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var stringBundle = stringBundleService.createBundle(src, appLocale);
var errMsg = stringBundle.GetStringFromName('startupError');
}
icon.setAttribute('tooltiptext', errMsg);
icon.setAttribute('error', 'true');
}
// Used for loading pages from upgrade wizard
if (Zotero && Zotero.initialURL) {
if (Zotero.initialURL) {
setTimeout(function () {
gBrowser.selectedTab = gBrowser.addTab(Zotero.initialURL);
Zotero.initialURL = null;
@ -158,11 +145,25 @@ var ZoteroOverlay = new function()
ZoteroOverlay.toggleDisplay(_stateBeforeReload, true);
}
});
})
.catch(function (e) {
var errMsg = Zotero ? Zotero.startupError : null;
// Use defaults if necessary
if (!errMsg) {
// Get the stringbundle manually
var src = 'chrome://zotero/locale/zotero.properties';
var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1']
.getService(Components.interfaces.nsILocaleService);
var appLocale = localeService.getApplicationLocale();
var stringBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var stringBundle = stringBundleService.createBundle(src, appLocale);
errMsg = stringBundle.GetStringFromName('startupError');
}
catch (e) {
Zotero.debug(e);
}
icon.setAttribute('tooltiptext', errMsg);
icon.setAttribute('error', 'true');
});
}
@ -209,7 +210,7 @@ var ZoteroOverlay = new function()
*/
this.toggleDisplay = function(makeVisible, dontRefocus)
{
if(!Zotero || !Zotero.initialized) {
if (!Zotero || Zotero.skipLoading) {
ZoteroPane.displayStartupError();
return;
}
@ -236,14 +237,6 @@ var ZoteroOverlay = new function()
*/
if(makeVisible) {
if (Zotero.locked) {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished');
ps.alert(null, "", msg);
return false;
}
zoteroSplitter.setAttribute('hidden', false);
zoteroPane.setAttribute('hidden', false);
zoteroPane.setAttribute('collapsed', false);
@ -349,6 +342,19 @@ var ZoteroOverlay = new function()
}
}
window.addEventListener("load", function(e) { ZoteroOverlay.onLoad(e); }, false);
window.addEventListener("load", function(e) {
try {
ZoteroOverlay.onLoad(e);
}
catch (e) {
Components.utils.reportError(e);
if (Zotero) {
Zotero.debug(e, 1);
}
else {
dump(e + "\n\n");
}
}
}, false);
window.addEventListener("unload", function(e) { ZoteroOverlay.onUnload(e); }, false);
window.addEventListener("beforeunload", function(e) { ZoteroOverlay.onBeforeUnload(e); }, false);

View File

@ -38,7 +38,6 @@
<overlay id="zotero"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- Include the global XPCOM object -->
<script src="overlay.js"/>
<popup id="contentAreaContextMenu"/>

View File

@ -33,11 +33,17 @@ const ZoteroStandalone = new function() {
* Run when standalone window first opens
*/
this.onLoad = function() {
if(!Zotero || !Zotero.initialized) {
ZoteroPane.displayStartupError();
window.close();
return;
Q.fcall(function () {
if(!Zotero) {
throw true;
}
if(Zotero.initializationPromise.isPending()) {
Zotero.showZoteroPaneProgressMeter();
}
return Zotero.initializationPromise;
})
.then(function () {
Zotero.hideZoteroPaneOverlays();
ZoteroPane.init();
ZoteroPane.makeVisible();
@ -59,6 +65,12 @@ const ZoteroStandalone = new function() {
Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
})
.catch(function (e) {
ZoteroPane.displayStartupError();
window.close();
return;
});
}
/**

View File

@ -1476,12 +1476,11 @@ Zotero.Attachments = new function(){
}
// Since the callback can be called during an import process that uses
// Zotero.wait(), try to queue the callback to run at the end,
// or run now if not queued
var queued = Zotero.addUnlockCallback(writeCallback);
if (!queued) {
// Zotero.wait(), wait until we're unlocked
Zotero.unlockPromise
.then(function () {
writeCallback();
}
});
};
Zotero.File.addCharsetListener(browser, callback, itemID);

File diff suppressed because it is too large Load Diff

View File

@ -1563,7 +1563,7 @@ Zotero.Sync.Server = new function () {
Zotero.UnresponsiveScriptIndicator.enable();
if (Zotero.locked) {
Zotero.hideZoteroPaneOverlay();
Zotero.hideZoteroPaneOverlays();
}
Zotero.suppressUIUpdates = false;
_updatesInProgress = false;
@ -1585,7 +1585,7 @@ Zotero.Sync.Server = new function () {
Zotero.UnresponsiveScriptIndicator.enable();
if (Zotero.locked) {
Zotero.hideZoteroPaneOverlay();
Zotero.hideZoteroPaneOverlays();
}
Zotero.suppressUIUpdates = false;
_updatesInProgress = false;

View File

@ -156,6 +156,19 @@ Components.utils.import("resource://gre/modules/Services.jsm");
* with an overlay
*/
this.__defineGetter__('locked', function () _locked);
this.__defineSetter__('locked', function (lock) {
var wasLocked = _locked;
_locked = lock;
if (!wasLocked && lock) {
this.unlockDeferred = Q.defer();
this.unlockPromise = this.unlockDeferred.promise;
}
else if (wasLocked && !lock) {
Zotero.debug("Running unlock callbacks");
this.unlockDeferred.resolve();
}
});
/**
* @property {Boolean} suppressUIUpdates Don't update UI on Notifier triggers
@ -167,14 +180,19 @@ Components.utils.import("resource://gre/modules/Services.jsm");
*/
this.closing = false;
this.initializationDeferred;
this.initializationPromise;
this.unlockDeferred;
this.unlockPromise;
var _startupErrorHandler;
var _zoteroDirectory = false;
var _localizedStringBundle;
var _localUserKey;
var _waiting = 0;
var _locked;
var _unlockCallbacks = [];
var _locked = false;
var _shutdownListeners = [];
var _progressMeters;
var _progressPopup;
@ -208,12 +226,18 @@ Components.utils.import("resource://gre/modules/Services.jsm");
/**
* Initialize the extension
*
* @return {Boolean|Promise:Boolean}
*/
function init() {
if (this.initialized || this.skipLoading) {
return false;
}
this.initializationDeferred = Q.defer();
this.initializationPromise = this.initializationDeferred.promise;
this.locked = true;
// Load in the preferences branch for the extension
Zotero.Prefs.init();
Zotero.Debug.init();
@ -456,9 +480,13 @@ Components.utils.import("resource://gre/modules/Services.jsm");
}
} else {
Zotero.debug("Loading in full mode");
if(!_initFull()) return false;
return Q.fcall(_initFull)
.then(function (success) {
if(!success) return false;
if(Zotero.isStandalone) Zotero.Standalone.init();
Zotero.initComplete();
});
}
return true;
@ -469,7 +497,10 @@ Components.utils.import("resource://gre/modules/Services.jsm");
*/
this.initComplete = function() {
if(Zotero.initialized) return;
Zotero.debug("Running initialization callbacks");
this.initialized = true;
this.initializationDeferred.resolve();
if(Zotero.isConnector) {
Zotero.Repo.init();
@ -487,12 +518,14 @@ Components.utils.import("resource://gre/modules/Services.jsm");
/**
* Initialization function to be called only if Zotero is in full mode
*
* @return {Promise:Boolean}
*/
function _initFull() {
var dataDir = Zotero.getZoteroDirectory();
Zotero.VersionHeader.init();
// Check for DB restore
var dataDir = Zotero.getZoteroDirectory();
var restoreFile = dataDir.clone();
restoreFile.append('restore-from-server');
if (restoreFile.exists()) {
@ -530,8 +563,14 @@ Components.utils.import("resource://gre/modules/Services.jsm");
Zotero.Fulltext.init();
return Q.fcall(function () {
// Require >=2.1b3 database to ensure proper locking
if (Zotero.isStandalone && Zotero.Schema.getDBVersion('system') > 0 && Zotero.Schema.getDBVersion('system') < 31) {
if (!Zotero.isStandalone) {
return;
}
return Zotero.Schema.getDBVersion('system')
.then(function (dbSystemVersion) {
if (dbSystemVersion > 0 && dbSystemVersion < 31) {
var dir = Zotero.getProfileDirectory();
dir.append('zotero');
@ -581,47 +620,14 @@ Components.utils.import("resource://gre/modules/Services.jsm");
Services.startup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit);
}
Zotero.skipLoading = true;
return false;
}
// Trigger updating of schema and scrapers
if (Zotero.Schema.userDataUpgradeRequired()) {
var upgraded = Zotero.Schema.showUpgradeWizard();
if (!upgraded) {
Zotero.skipLoading = true;
return false;
}
}
// If no userdata upgrade, still might need to process system
else {
try {
var updated = Zotero.Schema.updateSchema();
}
catch (e) {
if (typeof e == 'string' && e.match('newer than SQL file')) {
var kbURL = "http://zotero.org/support/kb/newer_db_version";
var msg = Zotero.localeJoin([
Zotero.getString('startupError.zoteroVersionIsOlder'),
Zotero.getString('startupError.zoteroVersionIsOlder.upgrade')
]) + "\n\n"
+ Zotero.getString('startupError.zoteroVersionIsOlder.current', Zotero.version) + "\n\n"
+ Zotero.getString('general.seeForMoreInformation', kbURL);
Zotero.startupError = msg;
}
else {
Zotero.startupError = Zotero.getString('startupError.databaseUpgradeError') + "\n\n" + e;
}
Zotero.skipLoading = true;
Components.utils.reportError(e);
return false;
}
}
// Populate combined tables for custom types and fields -- this is likely temporary
if (!upgraded && !updated) {
Zotero.Schema.updateCustomTables();
throw true;
}
});
})
.then(function () {
return Zotero.Schema.updateSchema()
.then(function (updated) {
Zotero.locked = false;
// Initialize various services
Zotero.Integration.init();
@ -645,8 +651,21 @@ Components.utils.import("resource://gre/modules/Services.jsm");
Zotero.LocateManager.init();
Zotero.Items.startEmptyTrashTimer();
})
.catch(function (e) {
Zotero.debug(e, 1);
Components.utils.reportError(e); // DEBUG: doesn't always work
Zotero.startupError = Zotero.getString('startupError.databaseUpgradeError') + "\n\n" + e;
throw true;
});
})
.then(function () {
return true;
})
.catch(function (e) {
Zotero.skipLoading = true;
return false;
});
}
/**
@ -1602,6 +1621,7 @@ Components.utils.import("resource://gre/modules/Services.jsm");
* @return void
*/
this.showZoteroPaneProgressMeter = function (msg, determinate, icon) {
if (!msg) msg = "";
var currentWindow = Services.wm.getMostRecentWindow("navigator:browser");
var enumerator = Services.wm.getEnumerator("navigator:browser");
var progressMeters = [];
@ -1626,7 +1646,14 @@ Components.utils.import("resource://gre/modules/Services.jsm");
continue;
}
win.ZoteroPane.document.getElementById('zotero-pane-progress-label').value = msg;
var label = win.ZoteroPane.document.getElementById('zotero-pane-progress-label');
if (msg) {
label.hidden = false;
label.value = msg;
}
else {
label.hidden = true;
}
var progressMeter = win.ZoteroPane.document.getElementById('zotero-pane-progressmeter')
if (determinate) {
progressMeter.mode = 'determined';
@ -1642,7 +1669,7 @@ Components.utils.import("resource://gre/modules/Services.jsm");
progressMeters.push(progressMeter);
}
_locked = true;
this.locked = true;
_progressMeters = progressMeters;
}
@ -1679,14 +1706,8 @@ Components.utils.import("resource://gre/modules/Services.jsm");
/**
* Hide Zotero pane overlay in all windows
*/
this.hideZoteroPaneOverlay = function () {
// Run any queued callbacks
if (_unlockCallbacks.length) {
var func;
while (func = _unlockCallbacks.shift()) {
func();
}
}
this.hideZoteroPaneOverlays = function () {
this.locked = false;
var enumerator = Services.wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
@ -1700,26 +1721,12 @@ Components.utils.import("resource://gre/modules/Services.jsm");
_progressPopup.close();
}
_locked = false;
_progressMeters = [];
_progressPopup = null;
_lastPercentage = null;
}
/**
* Adds a callback to be called when the Zotero pane overlay closes
*
* @param {Boolean} TRUE if added, FALSE if not locked
*/
this.addUnlockCallback = function (callback) {
if (!_locked) {
return false;
}
_unlockCallbacks.push(callback);
return true;
}
/**
* Adds a listener to be called when Zotero shuts down (even if Firefox is not shut down)
*/

View File

@ -97,6 +97,8 @@ var ZoteroPane = new function()
* Called when the window containing Zotero pane is open
*/
function init() {
Zotero.debug("Initializing Zotero pane");
// Set "Report Errors..." label via property rather than DTD entity,
// since we need to reference it in script elsewhere
document.getElementById('zotero-tb-actions-reportErrors').setAttribute('label',
@ -104,9 +106,6 @@ var ZoteroPane = new function()
// Set key down handler
document.getElementById('appcontent').addEventListener('keydown', ZoteroPane_Local.handleKeyDown, true);
if (Zotero.locked) {
return;
}
_loaded = true;
var zp = document.getElementById('zotero-pane');
@ -139,8 +138,6 @@ var ZoteroPane = new function()
* mode
*/
function _loadPane() {
if(!Zotero || !Zotero.initialized) return;
if(Zotero.isConnector) {
ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('connector.standaloneOpen'));
return;
@ -356,20 +353,20 @@ var ZoteroPane = new function()
*/
function makeVisible()
{
if (Zotero.locked) {
Zotero.showZoteroPaneProgressMeter();
}
Zotero.unlockPromise
.then(function () {
Zotero.hideZoteroPaneOverlays();
// If pane not loaded, load it or display an error message
if (!ZoteroPane_Local.loaded) {
if (Zotero.locked) {
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
var msg = Zotero.getString('general.operationInProgress') + '\n\n' + Zotero.getString('general.operationInProgress.waitUntilFinished');
ps.alert(null, "", msg);
return false;
}
ZoteroPane_Local.init();
}
// If Zotero could not be initialized, display an error message and return
if(!Zotero || !Zotero.initialized) {
if (!Zotero || Zotero.skipLoading) {
this.displayStartupError();
return false;
}
@ -448,6 +445,8 @@ var ZoteroPane = new function()
}
return true;
}.bind(this))
.done();
}
/**
@ -3965,7 +3964,6 @@ var ZoteroPane = new function()
}
this.displayStartupError = function(asPaneMessage) {
if(!Zotero || !Zotero.initialized) {
if (Zotero) {
var errMsg = Zotero.startupError;
var errFunc = Zotero.startupErrorHandler;
@ -4000,7 +3998,6 @@ var ZoteroPane = new function()
//}
}
}
}
/**
* Toggles Zotero-as-a-tab by passing off the request to the ZoteroOverlay object associated

View File

@ -167,7 +167,7 @@ ZoteroContext.prototype = {
zContext.Zotero.shutdown().then(function() {
// create a new zContext
makeZoteroContext(isConnector);
zContext.Zotero.init();
return zContext.Zotero.init();
}).done();
}
@ -289,20 +289,30 @@ function ZoteroService() {
if(isFirstLoadThisSession) {
makeZoteroContext(false);
try {
zContext.Zotero.init();
} catch(e) {
// if Zotero should start as a connector, reload it
zContext.Zotero.shutdown().then(function() {
makeZoteroContext(true);
zContext.Zotero.init();
}).done();
}
}
isFirstLoadThisSession = false; // no longer first load
this.wrappedJSObject = zContext.Zotero;
Q.fcall(function () {
return zContext.Zotero.init();
})
.catch(function (e) {
dump(e + "\n\n");
Components.utils.reportError(e);
// if Zotero should start as a connector, reload it
return zContext.Zotero.shutdown()
.then(function() {
makeZoteroContext(true);
return zContext.Zotero.init();
})
})
.then(function () {
zContext.Zotero.debug("Initialized in "+(Date.now() - start)+" ms");
})
.done();
}
else {
zContext.Zotero.debug("Already initialized");
}
isFirstLoadThisSession = false;
this.wrappedJSObject = zContext.Zotero;
} catch(e) {
var msg = typeof e == 'string' ? e : e.name;
dump(e + "\n\n");

View File

@ -1,4 +1,4 @@
-- 78
-- 79
-- Copyright (c) 2009 Center for History and New Media
-- George Mason University, Fairfax, Virginia, USA