diff --git a/chrome/content/zotero/xpcom/integration.js b/chrome/content/zotero/xpcom/integration.js index fa9a1f235..0fa509166 100644 --- a/chrome/content/zotero/xpcom/integration.js +++ b/chrome/content/zotero/xpcom/integration.js @@ -36,13 +36,12 @@ const INTEGRATION_MIN_VERSION = "3.1a0"; Zotero.Integration = new function() { var _fifoFile = null; var _tmpFile = null; - var _shCmd = null; - var _shProc = null; var _osascriptFile; var _inProgress = false; var _integrationVersionsOK = null; var _pipeMode = false; var _winUser32; + var _timer; this.sessions = {}; @@ -233,22 +232,21 @@ Zotero.Integration = new function() { }}; /** - * Reads from the temp file set up to handle integration pipe and executes the appropriate - * integration command - * - * Used to read from the integration pipe on Fx 3.6 + * Polling mechanism for file */ - var _integrationPipeObserverFx36 = {"observe":function(subject) { - // if we had an error reading from the pipe, return immediately, because trying to read - // again will probably just cause us to loop - if(subject.exitValue !== 0) { - Components.utils.reportError("Zotero: An error occurred reading from integration pipe"); - return; - } + var _integrationPipeObserverFx36 = {"notify":function() { + if(_fifoFile.fileSize === 0) return; - // read from pipe - var string = Zotero.File.getContents(_tmpFile); + // read from pipe (file, actually) + var string = Zotero.File.getContents(_fifoFile); + // clear file + var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. + createInstance(Components.interfaces.nsIFileOutputStream); + foStream.init(_fifoFile, 0x02 | 0x08 | 0x20, 0666, 0); + foStream.close(); + + // run command _parseIntegrationPipeCommand(string); }}; @@ -256,22 +254,17 @@ Zotero.Integration = new function() { * Initializes the nsIInputStream and nsIInputStreamPump to read from _fifoFile */ function _initializePipeStreamPump() { - if(_pipeMode === "deferredOpen") { - // Fx >4 supports deferred open; no need to use sh - var fifoStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); - fifoStream.QueryInterface(Components.interfaces.nsIFileInputStream); - // 16 = open as deferred so that we don't block on open - fifoStream.init(_fifoFile, -1, 0, 16); - - var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]. - createInstance(Components.interfaces.nsIInputStreamPump); - pump.init(fifoStream, -1, -1, 4096, 1, true); - pump.asyncRead(_integrationPipeListenerFx42, null); - } else { - // Fx 3.6 doesn't support deferred open - _shProc.runAsync(_shCmd, _shCmd.length, _integrationPipeObserverFx36); - } + // Fx >4 supports deferred open; no need to use sh + var fifoStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(Components.interfaces.nsIFileInputStream); + fifoStream.QueryInterface(Components.interfaces.nsIFileInputStream); + // 16 = open as deferred so that we don't block on open + fifoStream.init(_fifoFile, -1, 0, 16); + + var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]. + createInstance(Components.interfaces.nsIInputStreamPump); + pump.init(fifoStream, -1, -1, 4096, 1, true); + pump.asyncRead(_integrationPipeListenerFx42, null); } /** @@ -294,7 +287,7 @@ Zotero.Integration = new function() { } } else { if(Zotero.isMac) { - _pipeMode = "subprocess"; + _pipeMode = "poll"; } else { _pipeMode = "fx36thread"; } @@ -302,151 +295,141 @@ Zotero.Integration = new function() { Zotero.debug("Using integration pipe mode "+_pipeMode); - // 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"); - - // get sh - var sh = Components.classes["@mozilla.org/file/local;1"]. - createInstance(Components.interfaces.nsILocalFile); - sh.initWithPath("/bin/sh"); - - if(mkfifo.exists() && sh.exists()) { - // create named pipe - var proc = Components.classes["@mozilla.org/process/util;1"]. - createInstance(Components.interfaces.nsIProcess); - proc.init(mkfifo); - proc.run(true, [_fifoFile.path], 1); + if(_pipeMode === "poll") { + // create empty file + var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. + createInstance(Components.interfaces.nsIFileOutputStream); + foStream.init(_fifoFile, 0x02 | 0x08 | 0x20, 0666, 0); + foStream.close(); - if(_fifoFile.exists()) { - if(_pipeMode === "subprocess") { - // no deferred open capability, so we need to use the sh/tmpfile hack - - // make a tmp file - _tmpFile = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("TmpD", Components.interfaces.nsIFile); - _tmpFile.append("zoteroIntegrationTmp"); - _tmpFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666); - - // begin reading from named pipe - _shCmd = ["-c", "cat '"+_fifoFile.path.replace("'", "'\\''")+"' > '"+ - _tmpFile.path.replace("'", "'\\''")+"'"]; - Zotero.debug("Calling sh "+_shCmd.join(" ")); - - _shProc = Components.classes["@mozilla.org/process/util;1"]. - createInstance(Components.interfaces.nsIProcess); - _shProc.init(sh); - - _initializePipeStreamPump(); - } else if(_pipeMode === "deferredOpen") { - _initializePipeStreamPump(); - } else if(_pipeMode === "fx36thread") { - 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 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); - } else if(_pipeMode === "fx4thread") { - Components.utils.import("resource://gre/modules/ctypes.jsm"); - - // get possible names for libc - if(Zotero.isMac) { - var possibleLibcs = ["/usr/lib/libc.dylib"]; - } else { - var possibleLibcs = [ - "libc.so.6", - "libc.so.6.1", - "libc.so" - ]; - } - - // try all possibilities - while(possibleLibcs.length) { - var libc = possibleLibcs.shift(); - try { - var lib = ctypes.open(libc); - break; - } catch(e) {} - } - - // throw appropriate error on failure - if(!lib) { - throw "libc could not be loaded. Please post on the Zotero Forums so we can add "+ - "support for your operating system."; - } - - // 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) { - if(event.data[0] == "Exception") { - throw event.data[1]; - } else if(event.data[0] == "Debug") { - Zotero.debug(event.data[1]); - } else { - Zotero.Integration.execCommand(event.data[0], event.data[1], event.data[2]); - } - } - worker.postMessage({"path":_fifoFile.path, "libc":libc}); - } - - return true; - } - - Components.utils.reportError("Zotero: mkfifo failed -- not initializing integration pipe"); - return false; + // no deferred open capability, so we need to poll + // has to be global so that we don't get garbage collected + _timer = Components.classes["@mozilla.org/timer;1"]. + createInstance(Components.interfaces.nsITimer); + _timer.initWithCallback(_integrationPipeObserverFx36, 1000, + Components.interfaces.nsITimer.TYPE_REPEATING_SLACK); } else { - Components.utils.reportError("Zotero: mkfifo or sh not found -- not initializing integration pipe"); - return false; + // 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()) { + // create named pipe + 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()) { + if(_pipeMode === "deferredOpen") { + _initializePipeStreamPump(); + } else if(_pipeMode === "fx36thread") { + 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 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); + } else if(_pipeMode === "fx4thread") { + Components.utils.import("resource://gre/modules/ctypes.jsm"); + + // get possible names for libc + if(Zotero.isMac) { + var possibleLibcs = ["/usr/lib/libc.dylib"]; + } else { + var possibleLibcs = [ + "libc.so.6", + "libc.so.6.1", + "libc.so" + ]; + } + + // try all possibilities + while(possibleLibcs.length) { + var libc = possibleLibcs.shift(); + try { + var lib = ctypes.open(libc); + break; + } catch(e) {} + } + + // throw appropriate error on failure + if(!lib) { + throw "libc could not be loaded. Please post on the Zotero Forums so we can add "+ + "support for your operating system."; + } + + // 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) { + if(event.data[0] == "Exception") { + throw event.data[1]; + } else if(event.data[0] == "Debug") { + Zotero.debug(event.data[1]); + } else { + Zotero.Integration.execCommand(event.data[0], event.data[1], event.data[2]); + } + } + worker.postMessage({"path":_fifoFile.path, "libc":libc}); + } + } else { + Components.utils.reportError("Zotero: mkfifo failed -- not initializing integration pipe"); + return false; + } + } else { + Components.utils.reportError("Zotero: mkfifo or sh not found -- not initializing integration pipe"); + return false; + } } + + return true; } /** @@ -549,15 +532,16 @@ Zotero.Integration = new function() { * Destroys the integration pipe. */ this.destroy = function() { - // send shutdown message to fifo thread - var oStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. - getService(Components.interfaces.nsIFileOutputStream); - oStream.init(_fifoFile, 0x02 | 0x10, 0, 0); - var cmd = "Zotero shutdown\n"; - oStream.write(cmd, cmd.length); - oStream.close(); + if(_pipeMode !== "poll") { + // send shutdown message to fifo thread + var oStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. + getService(Components.interfaces.nsIFileOutputStream); + oStream.init(_fifoFile, 0x02 | 0x10, 0, 0); + var cmd = "Zotero shutdown\n"; + oStream.write(cmd, cmd.length); + oStream.close(); + } _fifoFile.remove(false); - if(_pipeMode === "subprocess") _tmpFile.remove(false); } /**