diff --git a/src/Makefile b/src/Makefile index 968aa76..6742dc5 100755 --- a/src/Makefile +++ b/src/Makefile @@ -111,7 +111,9 @@ modules_sources := $(wildcard $(modules_dir)/*.js) \ $(wildcard $(modules_dir)/*.jsm) \ $(wildcard $(modules_dir)/ctypes/*.jsm) \ $(wildcard $(modules_dir)/ctypes/linux/*.jsm) \ - $(wildcard $(modules_dir)/linux/*.jsm) + $(wildcard $(modules_dir)/ctypes/winnt/*.jsm) \ + $(wildcard $(modules_dir)/linux/*.jsm) \ + $(wildcard $(modules_dir)/winnt/*.jsm) # The components (JSM) dir. components_dir := components diff --git a/src/modules/commons.js b/src/modules/commons.js index eb6e0c4..cf8697a 100644 --- a/src/modules/commons.js +++ b/src/modules/commons.js @@ -291,6 +291,12 @@ firetray.js = { // https://developer.mozilla.org/en/js-ctypes/Using_js-ctypes/Working_with_data#Quirks_in_equality strEquals: function(obj1, obj2) { return obj1.toString() === obj2.toString(); + }, + + assert: function(condition, message) { + if (!condition) { + throw message || "Assertion failed"; + } } }; diff --git a/src/modules/ctypes/winnt/user32.jsm b/src/modules/ctypes/winnt/user32.jsm index f894413..579618b 100644 --- a/src/modules/ctypes/winnt/user32.jsm +++ b/src/modules/ctypes/winnt/user32.jsm @@ -59,6 +59,7 @@ function user32_defines(lib) { lib.lazy_bind("GetPropW", win32.HANDLE, win32.HWND, win32.LPCTSTR); lib.lazy_bind("SetPropW", win32.BOOL, win32.HWND, win32.LPCTSTR, win32.HANDLE); + lib.lazy_bind("RemovePropW", win32.HANDLE, win32.HWND, win32.LPCTSTR); lib.lazy_bind("GetWindowLongW", win32.LONG_PTR, win32.HWND, ctypes.int); lib.lazy_bind("SetWindowLongW", win32.LONG_PTR , win32.HWND, ctypes.int, win32.LONG_PTR); diff --git a/src/modules/winnt/FiretrayWindow.jsm b/src/modules/winnt/FiretrayWindow.jsm index ad6e98d..b84eaab 100644 --- a/src/modules/winnt/FiretrayWindow.jsm +++ b/src/modules/winnt/FiretrayWindow.jsm @@ -9,12 +9,11 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/ctypes.jsm"); Cu.import("resource://firetray/ctypes/ctypesMap.jsm"); Cu.import("resource://firetray/ctypes/winnt/win32.jsm"); -Cu.import("resource://firetray/ctypes/winnt/kernel32.jsm"); Cu.import("resource://firetray/ctypes/winnt/user32.jsm"); Cu.import("resource://firetray/winnt/FiretrayWin32.jsm"); Cu.import("resource://firetray/FiretrayWindow.jsm"); Cu.import("resource://firetray/commons.js"); -firetray.Handler.subscribeLibsForClosing([kernel32, user32]); +firetray.Handler.subscribeLibsForClosing([user32]); let log = firetray.Logging.getLogger("firetray.Window"); @@ -24,12 +23,10 @@ if ("undefined" == typeof(firetray.Handler)) const FIRETRAY_XWINDOW_HIDDEN = 1 << 0; // when minimized also const FIRETRAY_XWINDOW_MAXIMIZED = 1 << 1; -const kPropProcPrev = "_FIRETRAY_OLD_PROC"; - -// NOTE: storing ctypes pointers into a JS object doesn't work: pointers are -// "evolving" after a while (maybe due to back and forth conversion). So we -// need to store them into a real ctypes array ! -firetray.Handler.callProcHooks = new ctypesMap(win32.HHOOK); +// We need to keep long-living references to wndProcs callbacks. As they also +// happen to be ctypes pointers, we store them into real ctypes arrays. +firetray.Handler.wndProcs = new ctypesMap(user32.WNDPROC); +firetray.Handler.wndProcsOrig = new ctypesMap(user32.WNDPROC); firetray.Window = new FiretrayWindow(); @@ -58,69 +55,38 @@ firetray.Window.setVisibility = function(xid, visibility) { }; firetray.Window.wndProc = function(hWnd, uMsg, wParam, lParam) { // filterWindow - log.debug("wndProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam); + // log.debug("wndProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam); - let proc = user32.GetWindowLongW(hWnd, user32.GWLP_WNDPROC); - log.debug(" proc="+proc.toString(16)+" winLastError="+ctypes.winLastError); + if (uMsg === firetray.Win32.WM_TRAYMESSAGE) { + log.debug("wndProc CALLED with WM_TRAYMESSAGE"); -try { + } else if (uMsg === firetray.Win32.WM_TRAYMESSAGEFWD) { + log.debug("wndProc CALLED with WM_TRAYMESSAGEFWD"); + + } else if (uMsg === win32.WM_USER) { + log.debug("wndProc CALLED with WM_USER"); + } let wid = firetray.Win32.hwndToHexStr(hWnd); - // let procPrev = firetray.Handler.wndProcsOrig.get(wid); - // let procPrev = ctypes.cast(user32.GetPropW(hWnd, win32._T(kPropProcPrev)), user32.WNDPROC); - // let procPrev = user32.GetPropW(hWnd, win32._T(kPropProcPrev)); - log.debug(" wid="+wid+" prev="+procPrev); - - // let rv = user32.CallWindowProcW(procPrev, hWnd, uMsg, wParam, lParam); - let rv = procPrev(hWnd, uMsg, wParam, lParam); - log.debug(" CallWindowProc="+rv); - return rv; - - } catch(error) { -log.error(error); - } - - // user32.SetWindowLongW(hWnd, user32.GWLP_WNDPROC, ctypes.cast(procPrev, win32.LONG_PTR)); - - // if (uMsg === win32.WM_USER) { - // log.debug("wndProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam); - - // // return user32.DefWindowProcW(hWnd, uMsg, wParam, lParam); - // } - - // return user32.DefWindowProcW(hWnd, uMsg, wParam, lParam); + let procPrev = firetray.Handler.wndProcsOrig.get(wid); + return user32.CallWindowProcW(procPrev, hWnd, uMsg, wParam, lParam); }; -firetray.Window.callProcHook = function(nCode, wParam, lParam) { // WH_CALLWNDPROC, WH_GETMESSAGE - // log.debug("callProcHook CALLED: nCode="+nCode+", wParam="+wParam+", lParam="+lParam); - let cwpstruct = ctypes.cast(win32.LPARAM(lParam), user32.CWPSTRUCT.ptr).contents; - let uMsg = cwpstruct.message; - - if (uMsg === firetray.Win32.WM_TRAYMESSAGEFWD) { - log.debug("WM_TRAYMESSAGEFWD received!"); - - if (wParam != 0) { - log.debug("message was sent by the current thread"); - } - - let hWnd = cwpstruct.hwnd; - log.debug("hWnd="+hWnd); - let wid = firetray.Win32.hwndToHexStr(hWnd); - log.debug("wid="+wid); - - if (nCode === user32.HC_ACTION) { - log.warn("*** must process the message ***"); - } else if (nCode < 0) { - log.warn("*** must pass the message to the CallNextHookEx function without further processing ***"); - } - - } - - return user32.CallNextHookEx(null, nCode, wParam, lParam); +firetray.Window.restoreWndProc = function(wid) { + let procPrev = firetray.Handler.wndProcsOrig.get(wid); + let hwnd = new win32.HWND(ctypes.UInt64(wid)); + log.debug("hwnd="+hwnd); + let proc = user32.WNDPROC( + user32.SetWindowLongW(hwnd, user32.GWLP_WNDPROC, + ctypes.cast(procPrev, win32.LONG_PTR)) + ); + firetray.js.assert(proc == firetray.Handler.wndProcs.get(wid), + "Wrong WndProc replaced."); + firetray.Handler.wndProcs.remove(wid); + firetray.Handler.wndProcsOrig.remove(wid); }; - ///////////////////////// firetray.Handler overriding ///////////////////////// /** debug facility */ @@ -138,7 +104,7 @@ firetray.Handler.registerWindow = function(win) { let baseWin = firetray.Handler.getWindowInterface(win, "nsIBaseWindow"); let nativeHandle = baseWin.nativeHandle; let hwnd = nativeHandle ? - new ctypes.voidptr_t(ctypes.UInt64(nativeHandle)) : + new win32.HWND(ctypes.UInt64(nativeHandle)) : user32.FindWindowW("MozillaWindowClass", win.document.title); let wid = firetray.Win32.hwndToHexStr(hwnd); log.debug("=== hwnd="+hwnd+" wid="+wid+" win.document.title: "+win.document.title); @@ -158,23 +124,24 @@ firetray.Handler.registerWindow = function(win) { // ::SetPropW(hwnd, kIconMouseEventProc, reinterpret_cast(callback)); // ::SetPropW(hwnd, kIcon, reinterpret_cast(0x1)); - this.windowsCount += 1; - // NOTE: no need to check for window state to set visibility because all - // windows *are* shown at startup - firetray.Window.updateVisibility(wid, true); - log.debug("window "+wid+" registered"); - try { - let callProcHook = user32.HOOKPROC(firetray.Window.callProcHook); - log.debug("callhk="+callProcHook); - // Global hooks must reside in a dll (hence hInst). This is important for - // the scope of variables. - let hhook = user32.SetWindowsHookExW( - user32.WH_CALLWNDPROC, callProcHook, null, kernel32.GetCurrentThreadId()); - log.debug(" hhook="+hhook+" winLastError="+ctypes.winLastError); - this.callProcHooks.insert(wid, hhook); + this.windowsCount += 1; + // NOTE: no need to check for window state to set visibility because all + // windows *are* shown at startup + firetray.Window.updateVisibility(wid, true); + log.debug("window "+wid+" registered"); - firetray.Win32.acceptAllMessages(hwnd); + let wndProc = user32.WNDPROC(firetray.Window.wndProc); + log.debug("proc="+wndProc); + this.wndProcs.insert(wid, wndProc); + let procPrev = user32.WNDPROC( + user32.SetWindowLongW(hwnd, user32.GWLP_WNDPROC, + ctypes.cast(wndProc, win32.LONG_PTR)) + ); + log.debug("procPrev="+procPrev+" winLastError="+ctypes.winLastError); + // we can't store WNDPROC callbacks (JS ctypes objects) with SetPropW(), as + // we need long-living refs. + this.wndProcsOrig.insert(wid, procPrev); } catch (x) { if (x.name === "RangeError") // instanceof not working :-( @@ -184,6 +151,8 @@ firetray.Handler.registerWindow = function(win) { else win.alert(x); } + firetray.Win32.acceptAllMessages(hwnd); + log.debug("AFTER"); firetray.Handler.dumpWindows(); return wid; }; @@ -191,17 +160,16 @@ firetray.Handler.registerWindow = function(win) { firetray.Handler.unregisterWindow = function(win) { log.debug("unregister window"); - let wid = firetray.Window.getWIDFromChromeWindow(win); - + let wid = firetray.Window.getRegisteredWinIdFromChromeWindow(win); if (!firetray.Handler.windows.hasOwnProperty(wid)) { log.error("can't unregister unknown window "+wid); return false; } + firetray.Window.restoreWndProc(wid); + if (!delete firetray.Handler.windows[wid]) throw new DeleteError(); - user32.UnhookWindowsHookEx(firetray.Handler.callProcHooks.get(wid)); - firetray.Handler.callProcHooks.remove(wid); firetray.Handler.windowsCount -= 1; firetray.Handler.visibleWindowsCount -= 1;