diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js index 7d178751ee..4ed90d203b 100644 --- a/chrome/content/zotero/xpcom/integration.js +++ b/chrome/content/zotero/xpcom/integration.js @@ -45,135 +45,82 @@ Zotero.Integration = new function() { * Initializes the pipe used for integration on non-Windows platforms. */ this.init = function() { - if(!Zotero.isWin) { - // determine directory to put the pipe in - if(Zotero.isMac) { - // on OS X, first try /Users/Shared for those who can't put pipes in their home - // directories - _fifoFile = Components.classes["@mozilla.org/file/local;1"]. - createInstance(Components.interfaces.nsILocalFile); - _fifoFile.initWithPath("/Users/Shared"); - - if(_fifoFile.exists() && _fifoFile.isDirectory() && _fifoFile.isWritable()) { - var logname = Components.classes["@mozilla.org/process/environment;1"]. - getService(Components.interfaces.nsIEnvironment). - get("LOGNAME"); - _fifoFile.append(".zoteroIntegrationPipe_"+logname); - } else { - _fifoFile = null; - } - } - - if(!_fifoFile) { - // on other platforms, or as a fallback, use home directory - _fifoFile = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("Home", Components.interfaces.nsIFile); - _fifoFile.append(".zoteroIntegrationPipe"); - } - - Zotero.debug("Initializing Zotero integration pipe at "+_fifoFile.path); - - // destroy old pipe, if one exists - try { - if(_fifoFile.exists()) { - _fifoFile.remove(false); - } - } catch (e) { - Zotero.debug("Error removing old integration pipe", 1); - Components.utils.reportError( - "Zotero word processor integration initialization failed. " - + "See http://forums.zotero.org/discussion/12054/#Item_10 " - + "for instructions on correcting this problem." - ); - if(Zotero.isMac) { - try { - // can attempt to delete on OS X - var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - var deletePipe = promptService.confirm(null, Zotero.getString("integration.error.title"), Zotero.getString("integration.error.deletePipe")); - if(!deletePipe) return; - let escapedFifoFile = _fifoFile.path.replace("'", "'\\''"); - _executeAppleScript("do shell script \"rmdir '"+escapedFifoFile+"'; rm -f '"+escapedFifoFile+"'\" with administrator privileges", true); - if(_fifoFile.exists()) return; - } catch(e) { - Zotero.debug(e); - return; - } - } else { - return; - } - } - - // make a new pipe - var mkfifo = Components.classes["@mozilla.org/file/local;1"]. + // initialize SOAP server just to throw version errors + Zotero.Integration.Compat.init(); + + // Windows uses a command line handler for integration. See + // components/zotero-integration-service.js for this implementation. + if(Zotero.isWin) return; + + // Determine where to put the pipe + if(Zotero.isMac) { + // on OS X, first try /Users/Shared for those who can't put pipes in their home + // directories + _fifoFile = Components.classes["@mozilla.org/file/local;1"]. createInstance(Components.interfaces.nsILocalFile); - mkfifo.initWithPath("/usr/bin/mkfifo"); - if(!mkfifo.exists()) mkfifo.initWithPath("/bin/mkfifo"); - if(!mkfifo.exists()) mkfifo.initWithPath("/usr/local/bin/mkfifo"); + _fifoFile.initWithPath("/Users/Shared"); - if(mkfifo.exists()) { - var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread; - var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0); - - var me = this; - function mainThread(agent, cmd, doc) { - this.agent = agent; - this.cmd = cmd; - this.document = doc; - } - mainThread.prototype.run = function() { - me.execCommand(this.agent, this.cmd, this.document); - } - - function fifoThread() {} - fifoThread.prototype.run = function() { - var proc = Components.classes["@mozilla.org/process/util;1"]. - createInstance(Components.interfaces.nsIProcess); - proc.init(mkfifo); - proc.run(true, [_fifoFile.path], 1); - - if(!_fifoFile.exists()) Zotero.debug("Could not initialize Zotero integration pipe"); - - var fifoStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); - var line = {}; - while(true) { - fifoStream.QueryInterface(Components.interfaces.nsIFileInputStream); - fifoStream.init(_fifoFile, -1, 0, 0); - fifoStream.QueryInterface(Components.interfaces.nsILineInputStream); - fifoStream.readLine(line); - fifoStream.close(); - - var parts = line.value.split(" "); - var agent = parts[0]; - var cmd = parts[1]; - var document = parts.length >= 3 ? line.value.substr(agent.length+cmd.length+2) : null; - if(agent == "Zotero" && cmd == "shutdown") return; - main.dispatch(new mainThread(agent, cmd, document), background.DISPATCH_NORMAL); - } - } - - fifoThread.prototype.QueryInterface = mainThread.prototype.QueryInterface = function(iid) { - if (iid.equals(Components.interfaces.nsIRunnable) || - iid.equals(Components.interfaces.nsISupports)) return this; - throw Components.results.NS_ERROR_NO_INTERFACE; - } - - background.dispatch(new fifoThread(), background.DISPATCH_NORMAL); - - var observerService = Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService); - observerService.addObserver({ - observe: me.destroy - }, "quit-application", false); + if(_fifoFile.exists() && _fifoFile.isDirectory() && _fifoFile.isWritable()) { + var logname = Components.classes["@mozilla.org/process/environment;1"]. + getService(Components.interfaces.nsIEnvironment). + get("LOGNAME"); + _fifoFile.append(".zoteroIntegrationPipe_"+logname); } else { - Zotero.debug("mkfifo not found -- not initializing integration pipe"); + _fifoFile = null; } } - // initialize SOAP server just to throw version errors - Zotero.Integration.Compat.init(); + if(!_fifoFile) { + // on other platforms, or as a fallback, use home directory + _fifoFile = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("Home", Components.interfaces.nsIFile); + _fifoFile.append(".zoteroIntegrationPipe"); + } + + Zotero.debug("Initializing Zotero integration pipe at "+_fifoFile.path); + + // destroy old pipe, if one exists + try { + if(_fifoFile.exists()) { + _fifoFile.remove(false); + } + } catch (e) { + // if pipe can't be deleted, log an error + Zotero.debug("Error removing old integration pipe", 1); + Components.utils.reportError( + "Zotero word processor integration initialization failed. " + + "See http://forums.zotero.org/discussion/12054/#Item_10 " + + "for instructions on correcting this problem." + ); + if(Zotero.isMac) { + // can attempt to delete on OS X + try { + var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + var deletePipe = promptService.confirm(null, Zotero.getString("integration.error.title"), Zotero.getString("integration.error.deletePipe")); + if(!deletePipe) return; + let escapedFifoFile = _fifoFile.path.replace("'", "'\\''"); + _executeAppleScript("do shell script \"rmdir '"+escapedFifoFile+"'; rm -f '"+escapedFifoFile+"'\" with administrator privileges", true); + if(_fifoFile.exists()) return; + } catch(e) { + Zotero.logError(e); + return; + } + } + } + + // try to initialize pipe + var pipeInitialized = (Zotero.isFx4 ? _initializeIntegrationPipeFx4(_fifoFile) : + _initializeIntegrationPipeFx36(_fifoFile)); + + if(pipeInitialized) { + var me = this; + // if initialization succeeded, add an observer so that we don't hang shutdown + var observerService = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + observerService.addObserver({ observe: me.destroy }, "quit-application", false); + } } /** @@ -226,6 +173,113 @@ Zotero.Integration = new function() { } } + /** + * Initializes the Zotero Integration Pipe in Firefox 4+ + */ + function _initializeIntegrationPipeFx4(_fifoFile) { + // ensure Firefox 4.0b9 or later, since earlier versions do not support ChromeWorkers + // from XPCOM + var verComp = Components.classes["@mozilla.org/xpcom/version-comparator;1"] + .getService(Components.interfaces.nsIVersionComparator); + var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]. + getService(Components.interfaces.nsIXULAppInfo); + if(verComp.compare("2.0b9", appInfo.version) > 0) { + Components.utils.reportError("Zotero word processor integration requires "+ + "Firefox 4.0b9 or later. Please update to the latest Firefox 4.0 beta."); + return false; + } else { + Components.utils.import("resource://gre/modules/ctypes.jsm"); + + // initialize library + var libc = Zotero.isMac ? "/usr/lib/libc.dylib" : "libc.so"; + var lib = ctypes.open(libc); + // int mkfifo(const char *path, mode_t mode); + var mkfifo = lib.declare("mkfifo", ctypes.default_abi, ctypes.int, ctypes.char.ptr, ctypes.unsigned_int); + + // make pipe + var ret = mkfifo(_fifoFile.path, 0600); + if(!_fifoFile.exists()) return false; + lib.close(); + + // set up worker + var worker = Components.classes["@mozilla.org/threads/workerfactory;1"] + .createInstance(Components.interfaces.nsIWorkerFactory) + .newChromeWorker("chrome://zotero/content/xpcom/integration_worker.js"); + worker.onmessage = function(event) { + Zotero.Integration.execCommand(event.data[0], event.data[1], event.data[2]); + } + worker.postMessage({"path":_fifoFile.path, "libc":libc}); + + return true; + } + } + + /** + * Initializes the Zotero Integration Pipe in Firefox 3.6 + */ + function _initializeIntegrationPipeFx36(_fifoFile) { + // make a new pipe + var mkfifo = Components.classes["@mozilla.org/file/local;1"]. + createInstance(Components.interfaces.nsILocalFile); + mkfifo.initWithPath("/usr/bin/mkfifo"); + if(!mkfifo.exists()) mkfifo.initWithPath("/bin/mkfifo"); + if(!mkfifo.exists()) mkfifo.initWithPath("/usr/local/bin/mkfifo"); + + if(mkfifo.exists()) { + var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread; + var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0); + + function mainThread(agent, cmd, doc) { + this.agent = agent; + this.cmd = cmd; + this.document = doc; + } + mainThread.prototype.run = function() { + Zotero.Integration.execCommand(this.agent, this.cmd, this.document); + } + + function fifoThread() {} + fifoThread.prototype.run = function() { + var proc = Components.classes["@mozilla.org/process/util;1"]. + createInstance(Components.interfaces.nsIProcess); + proc.init(mkfifo); + proc.run(true, [_fifoFile.path], 1); + + if(!_fifoFile.exists()) Zotero.debug("Could not initialize Zotero integration pipe"); + + var fifoStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(Components.interfaces.nsIFileInputStream); + var line = {}; + while(true) { + fifoStream.QueryInterface(Components.interfaces.nsIFileInputStream); + fifoStream.init(_fifoFile, -1, 0, 0); + fifoStream.QueryInterface(Components.interfaces.nsILineInputStream); + fifoStream.readLine(line); + fifoStream.close(); + + var parts = line.value.split(" "); + var agent = parts[0]; + var cmd = parts[1]; + var document = parts.length >= 3 ? line.value.substr(agent.length+cmd.length+2) : null; + if(agent == "Zotero" && cmd == "shutdown") return; + main.dispatch(new mainThread(agent, cmd, document), background.DISPATCH_NORMAL); + } + } + + fifoThread.prototype.QueryInterface = mainThread.prototype.QueryInterface = function(iid) { + if (iid.equals(Components.interfaces.nsIRunnable) || + iid.equals(Components.interfaces.nsISupports)) return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + } + + background.dispatch(new fifoThread(), background.DISPATCH_NORMAL); + return true; + } else { + Zotero.debug("mkfifo not found -- not initializing integration pipe"); + return false; + } + } + /** * Calls the Integration applicatoon */ @@ -329,11 +383,18 @@ Zotero.Integration = new function() { */ this.activate = function() { if(Zotero.isMac) { - if(Zotero.oscpu == "PPC Mac OS X 10.4" || Zotero.oscpu == "Intel Mac OS X 10.4") { + const BUNDLE_IDS = { + "Zotero":"org.zotero.zotero", + "Firefox":"org.mozilla.firefox", + "Minefield":"org.mozilla.minefield" + }; + + if(Zotero.oscpu == "PPC Mac OS X 10.4" || Zotero.oscpu == "Intel Mac OS X 10.4" + || !BUNDLE_IDS[Zotero.appName]) { // 10.4 doesn't support "tell application id" - _executeAppleScript('tell application "'+(Zotero.isStandalone ? "Zotero" : "Firefox")+'" to activate'); + _executeAppleScript('tell application "'+Zotero.appName+'" to activate'); } else { - _executeAppleScript('tell application id "'+(Zotero.isStandalone ? "org.zotero.zotero" : "org.mozilla.firefox")+'" to activate'); + _executeAppleScript('tell application id "'+BUNDLE_IDS[Zotero.appName]+'" to activate'); } } } diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index 6928ef4b47..39899ed57f 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -196,7 +196,6 @@ var Zotero = new function(){ var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]. getService(Components.interfaces.nsIXULAppInfo); - this.appName = appInfo.name; this.isFx = true; this.isFx3 = appInfo.platformVersion.indexOf('1.9') === 0; this.isFx35 = appInfo.platformVersion.indexOf('1.9.1') === 0; @@ -242,16 +241,20 @@ var Zotero = new function(){ } // Load in the localization stringbundle for use by getString(name) - var src = 'chrome://zotero/locale/zotero.properties'; + var stringBundleService = + Components.classes["@mozilla.org/intl/stringbundle;1"] + .getService(Components.interfaces.nsIStringBundleService); 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); + _localizedStringBundle = stringBundleService.createBundle( + "chrome://zotero/locale/zotero.properties", appLocale); - _localizedStringBundle = stringBundleService.createBundle(src, appLocale); + // Also load the brand as appName + var brandBundle = stringBundleService.createBundle( + "chrome://branding/locale/brand.properties", appLocale); + this.appName = brandBundle.GetStringFromName("brandShortName"); // Set the locale direction to Zotero.dir // DEBUG: is there a better way to get the entity from JS?