Remove firefox-zotero IPC pipe and switching code (#2990)

This commit is contained in:
Adomas Ven 2023-02-14 02:18:40 +02:00 committed by Dan Stillman
parent 2c9e9f8a11
commit ba06db2e19
6 changed files with 4 additions and 317 deletions

View file

@ -909,39 +909,6 @@ Zotero.DataDirectory = {
return false;
}
// Check for an existing pipe from other running versions of Zotero pointing at the same data
// directory, and skip migration if found
try {
let foundPipe = yield Zotero.IPC.pipeExists();
if (foundPipe) {
Zotero.debug("Found existing pipe -- skipping migration");
if (!automatic) {
let ps = Services.prompt;
let buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
+ (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
let index = ps.confirmEx(null,
Zotero.getString('dataDir.migration.failure.title'),
Zotero.getString('dataDir.migration.failure.full.firefoxOpen'),
buttonFlags,
Zotero.getString('general.tryAgain'),
Zotero.getString('general.tryLater'),
null, null, {}
);
if (index == 0) {
return this.checkForMigration(newDir, newDir);
}
}
return false;
}
}
catch (e) {
Zotero.logError("Error checking for pipe -- skipping migration:\n\n" + e);
return false;
}
// If there are other profiles pointing to the old directory, make sure we can edit the prefs.js
// file before doing anything, or else we risk orphaning a 4.0 installation
try {
@ -1043,10 +1010,6 @@ Zotero.DataDirectory = {
// Set data directory again
Zotero.debug("Using new data directory " + newDir);
this._cache(newDir);
// Tell Zotero for Firefox in connector mode to reload and find the new data directory
if (Zotero.isStandalone) {
Zotero.IPC.broadcast('reinit');
}
// At least the database was copied, but other things failed
if (errors.length) {

View file

@ -128,7 +128,6 @@ Zotero.Integration = new function() {
this.deletePipe = function(pipe) {
try {
if(pipe.exists()) {
Zotero.IPC.safePipeWrite(pipe, "Zotero shutdown\n");
pipe.remove(false);
}
return true;

View file

@ -27,79 +27,6 @@ Zotero.IPC = new function() {
var _libc, _libcPath, _instancePipe, _user32, open, write, close;
/**
* Initialize pipe for communication with connector
*/
this.init = function() {
if(!Zotero.isWin) { // no pipe support on Fx 3.6
_instancePipe = _getPipeDirectory();
if(!_instancePipe.exists()) {
_instancePipe.create(Ci.nsIFile.DIRECTORY_TYPE, 0o700);
}
_instancePipe.append(Zotero.instanceID);
Zotero.IPC.Pipe.initPipeListener(_instancePipe, this.parsePipeInput);
}
}
/**
* Parses input received via instance pipe
*/
this.parsePipeInput = function(msgs) {
for (let msg of msgs.split("\n")) {
if(!msg) continue;
Zotero.debug('IPC: Received "'+msg+'"');
/*
* The below messages coordinate switching Zotero for Firefox from extension mode to
* connector mode without restarting after Zotero Standalone has been launched. The
* dance typically proceeds as follows:
*
* 1. SA sends a releaseLock message to Z4Fx that tells it to release its lock.
* 2. Z4Fx releases its lock and sends a lockReleased message to SA.
* 3. Z4Fx restarts in connector mode. Once it's ready for an IPC command, it sends
* a checkInitComplete message to SA.
* 4. Once SA finishes initializing, or immediately after a checkInitComplete message
* has been received if it is already initialized, SA sends an initComplete message
* to Z4Fx.
*/
if(msg.substr(0, 11) === "releaseLock") {
// Standalone sends this to the Firefox extension to tell the Firefox extension to
// release its lock on the Zotero database
if(!Zotero.isConnector && (msg.length === 11 ||
msg.substr(12) === Zotero.DataDirectory.getDatabase())) {
switchConnectorMode(true);
}
} else if(msg === "lockReleased") {
// The Firefox extension sends this to Standalone to let Standalone know that it has
// released its lock
Zotero.onDBLockReleased();
} else if(msg === "checkInitComplete") {
// The Firefox extension sends this to Standalone to tell Standalone to send an
// initComplete message when it is fully initialized
if(Zotero.initialized) {
Zotero.IPC.broadcast("initComplete");
} else {
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
var _loadObserver = function() {
Zotero.IPC.broadcast("initComplete");
observerService.removeObserver(_loadObserver, "zotero-loaded");
};
observerService.addObserver(_loadObserver, "zotero-loaded", false);
}
} else if(msg === "initComplete") {
// Standalone sends this to the Firefox extension to let the Firefox extension
// know that Standalone has fully initialized and it should pull the list of
// translators
Zotero.initComplete();
}
else if (msg == "reinit") {
if (Zotero.isConnector) {
reinit(false, true);
}
}
}
}
/**
* Writes safely to a file, avoiding blocking.
@ -135,165 +62,6 @@ Zotero.IPC = new function() {
return true;
}
/**
* Broadcast a message to all other Zotero instances
*/
this.broadcast = function(msg) {
if(Zotero.isWin) { // communicate via WM_COPYDATA method
Components.utils.import("resource://gre/modules/ctypes.jsm");
// communicate via message window
var user32 = ctypes.open("user32.dll");
/* http://msdn.microsoft.com/en-us/library/ms633499%28v=vs.85%29.aspx
* HWND WINAPI FindWindow(
* __in_opt LPCTSTR lpClassName,
* __in_opt LPCTSTR lpWindowName
* );
*/
var FindWindow = user32.declare("FindWindowW", ctypes.winapi_abi, ctypes.int32_t,
ctypes.jschar.ptr, ctypes.jschar.ptr);
/* http://msdn.microsoft.com/en-us/library/ms633539%28v=vs.85%29.aspx
* BOOL WINAPI SetForegroundWindow(
* __in HWND hWnd
* );
*/
var SetForegroundWindow = user32.declare("SetForegroundWindow", ctypes.winapi_abi,
ctypes.bool, ctypes.int32_t);
/*
* LRESULT WINAPI SendMessage(
* __in HWND hWnd,
* __in UINT Msg,
* __in WPARAM wParam,
* __in LPARAM lParam
* );
*/
var SendMessage = user32.declare("SendMessageW", ctypes.winapi_abi, ctypes.uintptr_t,
ctypes.int32_t, ctypes.unsigned_int, ctypes.voidptr_t, ctypes.voidptr_t);
/* http://msdn.microsoft.com/en-us/library/ms649010%28v=vs.85%29.aspx
* typedef struct tagCOPYDATASTRUCT {
* ULONG_PTR dwData;
* DWORD cbData;
* PVOID lpData;
* } COPYDATASTRUCT, *PCOPYDATASTRUCT;
*/
var COPYDATASTRUCT = ctypes.StructType("COPYDATASTRUCT", [
{"dwData":ctypes.voidptr_t},
{"cbData":ctypes.uint32_t},
{"lpData":ctypes.voidptr_t}
]);
// Aurora/Nightly are always named "Firefox" in
// application.ini
const appNames = ["Firefox", "Zotero"];
// Different from Zotero.appName; this corresponds to the
// name in application.ini
const myAppName = Services.appinfo.name;
for (let appName of appNames) {
// don't send messages to ourself
if(appName === myAppName) continue;
var thWnd = FindWindow(appName+"MessageWindow", null);
if(thWnd) {
Zotero.debug('IPC: Broadcasting "'+msg+'" to window "'+appName+'MessageWindow"');
// allocate message
var data = ctypes.char.array()('firefox.exe -silent -ZoteroIPC "'+msg.replace('"', '""', "g")+'"\x00C:\\');
var dataSize = data.length*data.constructor.size;
// create new COPYDATASTRUCT
var cds = new COPYDATASTRUCT();
cds.dwData = null;
cds.cbData = dataSize;
cds.lpData = data.address();
// send COPYDATASTRUCT
var success = SendMessage(thWnd, 0x004A /** WM_COPYDATA **/, null, cds.address());
user32.close();
return !!success;
}
}
user32.close();
return false;
} else { // communicate via pipes
// look for other Zotero instances
var pipes = [];
var pipeDir = _getPipeDirectory();
if(pipeDir.exists()) {
var dirEntries = pipeDir.directoryEntries;
while (dirEntries.hasMoreElements()) {
var pipe = dirEntries.getNext().QueryInterface(Ci.nsIFile);
if(pipe.leafName[0] !== "." && (!_instancePipe || !pipe.equals(_instancePipe))) {
pipes.push(pipe);
}
}
}
if(!pipes.length) return false;
var success = false;
for (let pipe of pipes) {
Zotero.debug('IPC: Trying to broadcast "'+msg+'" to instance '+pipe.leafName);
var defunct = false;
if(pipe.isFile()) {
// not actually a pipe
if(pipe.isDirectory()) {
// not a file, so definitely defunct
defunct = true;
} else {
// check to see whether the size exceeds a certain threshold that we find
// reasonable for the queue, and if not, delete the pipe, because it's
// probably just a file that wasn't deleted on shutdown and is now
// accumulating vast amounts of data
defunct = pipe.fileSize > 1024;
}
}
if(!defunct) {
// Try to write to the pipe for 100 ms
var time = Date.now(), timeout = time+100, wroteToPipe;
do {
wroteToPipe = Zotero.IPC.safePipeWrite(pipe, msg+"\n");
} while(Date.now() < timeout && !wroteToPipe);
if (wroteToPipe) Zotero.debug('IPC: Pipe took '+(Date.now()-time)+' ms to become available');
success = success || wroteToPipe;
defunct = !wroteToPipe;
}
if(defunct) {
Zotero.debug('IPC: Removing defunct pipe '+pipe.leafName);
try {
pipe.remove(true);
} catch(e) {};
}
}
return success;
}
}
/**
* Get directory containing Zotero pipes
*/
function _getPipeDirectory() {
var dir = Zotero.File.pathToFile(Zotero.DataDirectory.dir);
dir.append("pipes");
return dir;
}
this.pipeExists = Zotero.Promise.coroutine(function* () {
var dir = _getPipeDirectory().path;
return (yield OS.File.exists(dir)) && !(yield Zotero.File.directoryIsEmpty(dir));
});
/**
* Gets the path to libc as a string
*/
@ -359,19 +127,8 @@ Zotero.IPC.Pipe = new function() {
this.initPipeListener = function(file, callback) {
Zotero.debug("IPC: Initializing pipe at "+file.path);
// determine type of pipe
if(!_pipeClass) {
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.2a1pre", appInfo.platformVersion) <= 0) { // Gecko 5
_pipeClass = Zotero.IPC.Pipe.DeferredOpen;
}
}
// make new pipe
new _pipeClass(file, callback);
new Zotero.IPC.Pipe.DeferredOpen(file, callback);
}
/**
@ -395,7 +152,7 @@ Zotero.IPC.Pipe = new function() {
* Adds a shutdown listener for a pipe that writes "Zotero shutdown\n" to the pipe and then
* deletes it
*/
this.writeShutdownMessage = function(pipe, file) {
this.remove = function(pipe, file) {
// Make sure pipe actually exists
if(!file.exists()) {
Zotero.debug("IPC: Not closing pipe "+file.path+": already deleted");
@ -425,7 +182,7 @@ Zotero.IPC.Pipe.DeferredOpen = function(file, callback) {
this._initPump();
// add shutdown listener
Zotero.addShutdownListener(Zotero.IPC.Pipe.writeShutdownMessage.bind(null, this, file));
Zotero.addShutdownListener(Zotero.IPC.Pipe.remove.bind(null, this, file));
}
Zotero.IPC.Pipe.DeferredOpen.prototype = {

View file

@ -385,16 +385,6 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
var _shutdownObserver = {observe:function() { Zotero.shutdown().done() }};
Services.obs.addObserver(_shutdownObserver, "quit-application", false);
try {
Zotero.IPC.init();
}
catch (e) {
if (_checkDataDirAccessError(e)) {
return false;
}
throw (e);
}
// Get startup errors
try {
let messages = Services.console.getMessageArray();
@ -783,11 +773,6 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
let dbfile = Zotero.DataDirectory.getDatabase();
// Tell any other Zotero instances to release their lock,
// in case we lost the lock on the database (how?) and it's
// now open in two places at once
Zotero.IPC.broadcast("releaseLock " + dbfile);
// Test write access on Zotero data directory
if (!Zotero.File.pathToFile(OS.Path.dirname(dbfile)).isWritable()) {
var msg = 'Cannot write to ' + OS.Path.dirname(dbfile) + '/';
@ -906,11 +891,6 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
if (Zotero.DB) {
// close DB
yield Zotero.DB.closeDatabase(true)
if (!Zotero.restarting) {
// broadcast that DB lock has been released
Zotero.IPC.broadcast("lockReleased");
}
}
} catch(e) {
Zotero.logError(e);

View file

@ -532,16 +532,6 @@ ZoteroCommandLineHandler.prototype = {
zContext.Zotero.Integration.execCommand(agent, command, docId, templateVersion);
}
// handler for Windows IPC commands
var ipcParam = cmdLine.handleFlagWithParam("ZoteroIPC", false);
if(ipcParam) {
// Don't open a new window
cmdLine.preventDefault = true;
if (!zContext) new ZoteroService();
let Zotero = zContext.Zotero;
Zotero.setTimeout(() => 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

View file

@ -37,8 +37,6 @@ describe("Zotero.DataDirectory", function () {
newMigrationMarker = OS.Path.join(newDir, Zotero.DataDirectory.MIGRATION_MARKER);
stubs.canMigrate = sinon.stub(Zotero.DataDirectory, "canMigrate").returns(true);
// A pipe always exists during tests, since Zotero is running
stubs.pipeExists = sinon.stub(Zotero.IPC, "pipeExists").returns(Zotero.Promise.resolve(false));
stubs.setDataDir = sinon.stub(Zotero.DataDirectory, "set");
stubs.isNewDirOnDifferentDrive = sinon.stub(Zotero.DataDirectory, 'isNewDirOnDifferentDrive').resolves(true);
});