/*
***** BEGIN LICENSE BLOCK *****
Copyright © 2009 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://zotero.org
This file is part of Zotero.
Zotero is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Zotero is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Zotero. If not, see .
Based on nsChromeExtensionHandler example code by Ed Anuff at
http://kb.mozillazine.org/Dev_:_Extending_the_Chrome_Protocol
***** END LICENSE BLOCK *****
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
/** XPCOM files to be loaded for all modes **/
const xpcomFilesAll = [
'zotero',
'intl',
'prefs',
'dataDirectory',
'debug',
'error',
'utilities/date',
'utilities/utilities',
'utilities/utilities_item',
'utilities/openurl',
'utilities/xregexp-all',
'utilities/xregexp-unicode-zotero',
'utilities_internal',
'translate/src/utilities_translate',
'file',
'http',
'mimeTypeHandler',
'pdfWorker/manager',
'ipc',
'profile',
'progressWindow',
'proxy',
'translate/src/translation/translate',
'translate/src/translator',
'translate/src/tlds',
'translation/translate_firefox',
'isbn',
];
/** XPCOM files to be loaded only for local translation and DB access **/
const xpcomFilesLocal = [
'collectionTreeRow',
'annotations',
'api',
'attachments',
'cite',
'citeprocRsBridge',
'cookieSandbox',
'data/library',
'data/libraries',
'data/dataObject',
'data/dataObjects',
'data/dataObjectUtilities',
'data/cachedTypes',
'data/notes',
'data/item',
'data/items',
'data/collection',
'data/collections',
'data/feedItem',
'data/feedItems',
'data/feed',
'data/feeds',
'data/creators',
'data/group',
'data/groups',
'data/itemFields',
'data/relations',
'data/search',
'data/searchConditions',
'data/searches',
'data/tags',
'db',
'dictionaries',
'duplicates',
'editorInstance',
'feedReader',
'fileDragDataProvider',
'fulltext',
'id',
'integration',
'locale',
'locateManager',
'mime',
'notifier',
'openPDF',
'reader',
'progressQueue',
'progressQueueDialog',
'quickCopy',
'recognizePDF',
'report',
'retractions',
'router',
'schema',
'server',
'session',
'streamer',
'style',
'sync',
'sync/syncAPIClient',
'sync/syncEngine',
'sync/syncExceptions',
'sync/syncEventListeners',
'sync/syncFullTextEngine',
'sync/syncLocal',
'sync/syncRunner',
'sync/syncUtilities',
'storage',
'storage/storageEngine',
'storage/storageLocal',
'storage/storageRequest',
'storage/storageResult',
'storage/storageUtilities',
'storage/streamListener',
'storage/zfs',
'storage/webdav',
'syncedSettings',
'timeline',
'uri',
'users',
'translation/translate_item',
'translation/translators',
'connector/httpIntegrationClient',
'connector/server_connector',
'connector/server_connectorIntegration',
];
Components.utils.import("resource://gre/modules/ComponentUtils.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var instanceID = (new Date()).getTime();
var isFirstLoadThisSession = true;
var zContext = null;
var initCallbacks = [];
var zInitOptions = {};
// Components.utils.import('resource://zotero/require.js');
// Not using Cu.import here since we don't want the require module to be cached
// for includes within ZoteroPane or other code, where we want the window instance available to modules.
Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
.loadSubScript('resource://zotero/require.js');
var ZoteroContext = function() {}
ZoteroContext.prototype = {
require,
/**
* Convenience method to replicate window.alert()
**/
// TODO: is this still used? if so, move to zotero.js
"alert":function alert(msg){
this.Zotero.debug("alert() is deprecated from Zotero XPCOM");
Cc["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Ci.nsIPromptService)
.alert(null, "", msg);
},
/**
* Convenience method to replicate window.confirm()
**/
// TODO: is this still used? if so, move to zotero.js
"confirm":function confirm(msg){
this.Zotero.debug("confirm() is deprecated from Zotero XPCOM");
return Cc["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Ci.nsIPromptService)
.confirm(null, "", msg);
},
"Cc":Cc,
"Ci":Ci,
/**
* Convenience method to replicate window.setTimeout()
**/
"setTimeout":function setTimeout(func, ms){
return this.Zotero.setTimeout(func, ms);
},
"clearTimeout":function setTimeout(id) {
this.Zotero.clearTimeout(id);
},
/**
* Switches in or out of connector mode
*/
"switchConnectorMode":function(isConnector) {
if(isConnector !== this.isConnector) {
Services.obs.notifyObservers(zContext.Zotero, "zotero-before-reload", isConnector ? "connector" : "full");
zContext.Zotero.shutdown().then(function() {
// create a new zContext
makeZoteroContext(isConnector);
return zContext.Zotero.init(zInitOptions);
}).done();
}
return zContext;
},
/**
* Shuts down Zotero, calls a callback (that may return a promise),
* then reinitializes Zotero. Returns a promise that is resolved
* when this process completes.
*/
"reinit":function(cb, isConnector, options = {}) {
Services.obs.notifyObservers(zContext.Zotero, "zotero-before-reload", isConnector ? "connector" : "full");
return zContext.Zotero.shutdown().then(function() {
return cb ? cb() : false;
}).finally(function() {
makeZoteroContext(isConnector);
var o = {};
Object.assign(o, zInitOptions);
Object.assign(o, options);
return zContext.Zotero.init(o);
});
}
};
/**
* The class from which the Zotero global XPCOM context is constructed
*
* @constructor
* This runs when ZoteroService is first requested to load all applicable scripts and initialize
* Zotero. Calls to other XPCOM components must be in here rather than in top-level code, as other
* components may not have yet been initialized.
*/
function makeZoteroContext(isConnector) {
if(zContext) {
// Swap out old zContext
var oldzContext = zContext;
// Create new zContext
zContext = new ZoteroContext();
// Swap in old Zotero object, so that references don't break, but empty it
zContext.Zotero = oldzContext.Zotero;
for(var key in zContext.Zotero) delete zContext.Zotero[key];
} else {
zContext = new ZoteroContext();
zContext.Zotero = function() {};
}
var subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
// Load zotero.js first
subscriptLoader.loadSubScript("chrome://zotero/content/xpcom/" + xpcomFilesAll[0] + ".js", zContext, 'utf-8');
// Load CiteProc into Zotero.CiteProc namespace
zContext.Zotero.CiteProc = {"Zotero":zContext.Zotero};
subscriptLoader.loadSubScript("chrome://zotero/content/xpcom/citeproc.js", zContext.Zotero.CiteProc, 'utf-8');
// Load XRegExp object into Zotero.XRegExp
const xregexpFiles = [
/**Core functions**/
'xregexp-all',
'xregexp-unicode-zotero' //adds support for some Unicode categories used in Zotero
];
for (var i=0; i Zotero.IPC.parsePipeInput(ipcParam), 0);
}
if(isStandalone()) {
var fileToOpen;
// Special handler for "zotero" URIs at the command line to prevent them from opening a new window
var param = cmdLine.handleFlagWithParam("url", false);
if (param) {
var uri = cmdLine.resolveURI(param);
if(uri.schemeIs("zotero")) {
addInitCallback(function (Zotero) {
Zotero.uiReadyPromise
.then(function () {
// Check for existing window and focus it
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var win = wm.getMostRecentWindow("navigator:browser");
if (win) {
win.focus();
win.ZoteroPane.loadURI(uri.spec)
}
});
});
}
// See below
else if (uri.schemeIs("file")) {
Components.utils.import("resource://gre/modules/osfile.jsm")
fileToOpen = OS.Path.fromFileURI(uri.spec)
}
else {
dump(`Not handling URL: ${uri.spec}\n\n`);
}
}
param = cmdLine.handleFlag("debugger", false);
if (param) {
try {
let portOrPath = Services.prefs.getBranch('').getIntPref('devtools.debugger.remote-port');
const { DevToolsLoader } = ChromeUtils.import(
"resource://devtools/shared/loader/Loader.jsm"
);
const loader = new DevToolsLoader({
freshCompartment: true,
});
const { DevToolsServer } = loader.require("devtools/server/devtools-server");
const { SocketListener } = loader.require("devtools/shared/security/socket");
if (DevToolsServer.initialized) {
dump("Debugger server already initialized\n\n");
return;
}
DevToolsServer.init();
DevToolsServer.registerAllActors();
DevToolsServer.allowChromeProcess = true;
const socketOptions = { portOrPath };
const listener = new SocketListener(DevToolsServer, socketOptions);
await listener.open();
if (!DevToolsServer.listeningSockets) {
throw new Error("No listening sockets");
}
dump(`Debugger server started on ${portOrPath}\n\n`);
}
catch (e) {
dump(e + "\n\n");
Components.utils.reportError(e);
}
}
// In Fx49-based Mac Standalone, if Zotero is closed, an associated file is launched, and
// Zotero hasn't been opened before, a -file parameter is passed and two main windows open.
// Subsequent file openings when closed result in -url with file:// URLs (converted above)
// and don't result in two windows. Here we prevent the double window.
param = fileToOpen;
if (!param) {
param = cmdLine.handleFlagWithParam("file", false);
if (param && isMac()) {
cmdLine.preventDefault = true;
}
}
if (param) {
addInitCallback(function (Zotero) {
// Wait to handle things that require the UI until after it's loaded
Zotero.uiReadyPromise
.then(function () {
var file = Zotero.File.pathToFile(param);
if(file.leafName.substr(-4).toLowerCase() === ".csl"
|| file.leafName.substr(-8).toLowerCase() === ".csl.txt") {
// Install CSL file
Zotero.Styles.install({ file: file.path }, file.path);
} else {
// Ask before importing
var checkState = {
value: Zotero.Prefs.get('import.createNewCollection.fromFileOpenHandler')
};
if (Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService)
.confirmCheck(null, Zotero.getString('ingester.importFile.title'),
Zotero.getString('ingester.importFile.text', [file.leafName]),
Zotero.getString('ingester.importFile.intoNewCollection'),
checkState)) {
Zotero.Prefs.set(
'import.createNewCollection.fromFileOpenHandler', checkState.value
);
// Perform file import in front window
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
var browserWindow = wm.getMostRecentWindow("navigator:browser");
browserWindow.Zotero_File_Interface.importFile({
file,
createNewCollection: checkState.value
});
}
}
});
});
}
}
},
classID: Components.ID("{531828f8-a16c-46be-b9aa-14845c3b010f}"),
service: true,
_xpcom_categories: [{category:"command-line-handler", entry:"m-zotero"}],
QueryInterface: ChromeUtils.generateQI([Components.interfaces.nsICommandLineHandler])
};
var NSGetFactory = ComponentUtils.generateNSGetFactory([ZoteroService, ZoteroCommandLineHandler]);