From 9b2f2016aa1d8ad360d1c54dd394b712edf3ea8b Mon Sep 17 00:00:00 2001 From: foudfou Date: Sun, 16 Mar 2014 18:40:59 +0100 Subject: [PATCH] Overcome titlebar buttons not firing expected events. --- src/chrome/content/overlay.js | 103 +++++++++++++++++++++++---- src/modules/FiretrayHandler.jsm | 2 +- src/modules/commons.js | 17 ++++- src/modules/linux/FiretrayWindow.jsm | 2 +- src/modules/winnt/FiretrayWindow.jsm | 2 +- 5 files changed, 107 insertions(+), 19 deletions(-) diff --git a/src/chrome/content/overlay.js b/src/chrome/content/overlay.js index dbe01a9..f65306e 100644 --- a/src/chrome/content/overlay.js +++ b/src/chrome/content/overlay.js @@ -27,6 +27,7 @@ var firetrayChrome = { // each new window gets a new firetrayChrome ! this.winId = firetray.Handler.registerWindow(win); win.addEventListener('close', firetrayChrome.onClose, true); + this.hijackTitlebarButtons(); firetray_log.debug('Firetray LOADED: ' + init); return true; @@ -47,30 +48,102 @@ var firetrayChrome = { // each new window gets a new firetrayChrome ! hides_on_close is set (we are not actually closing the tabs!). There is no use trying to set warnOnClose=false temporarily in onClose, since onClose is called *after* the popup */ - // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=827880 menubar - // prevents close button to fire 'close' onClose: function(event) { firetray_log.debug('Firetray CLOSE'); - if (event.originalTarget != window) - throw new TypeError('originalTarget not the current ChromeWindow'); - let hides_on_close = firetray.Utils.prefService.getBoolPref('hides_on_close'); firetray_log.debug('hides_on_close: '+hides_on_close); - if (hides_on_close) { - let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window'); - let hides_last_only = firetray.Utils.prefService.getBoolPref('hides_last_only'); - firetray_log.debug('hides_single_window='+hides_single_window+', windowsCount='+firetray.Handler.windowsCount); - if (hides_last_only && (firetray.Handler.windowsCount > 1)) return; + if (!hides_on_close) return false; - if (hides_single_window) - firetray.Handler.hideWindow(firetrayChrome.winId); - else - firetray.Handler.hideAllWindows(); + let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window'); + let hides_last_only = firetray.Utils.prefService.getBoolPref('hides_last_only'); + firetray_log.debug('hides_single_window='+hides_single_window+', windowsCount='+firetray.Handler.windowsCount); + if (hides_last_only && (firetray.Handler.windowsCount > 1)) return true; - event && event.preventDefault(); + if (hides_single_window) + firetray.Handler.hideWindow(firetrayChrome.winId); + else + firetray.Handler.hideAllWindows(); + + if (event) event.preventDefault(); + return true; + }, + + /* + * Minimize/Restore/Close buttons can be overlayed by titlebar (fake) buttons + * which do not fire the events that we rely on (see Bug 827880). This is why + * we override the fake buttons' default actions. + */ + hijackTitlebarButtons: function() { + Object.keys(this.titlebarDispatch).map(function(val, idx) { + let fInfo = firetrayChrome.replaceCommand(val, firetrayChrome.titlebarDispatch[val]['new']); + if (fInfo) { + firetrayChrome.titlebarDispatch[val]['old'] = fInfo[0]; + firetray_log.debug('replaced command='+val+' type='+fInfo[1]+' func='+fInfo[0]); + firetrayChrome.titlebarDispatch[val]['type'] = fInfo[1]; + } + }); + }, + + titlebarDispatch: { + "titlebar-min": {new: function(e){ + firetray_log.debug(' titlebar-min clicked'); + if (!firetray.Handler.onMinimize(firetrayChrome.winId)) + firetrayChrome.applyDefaultCommand("titlebar-min"); + }, old: null, type: null}, + + "titlebar-close": {new: function(e){ + firetray_log.debug(' titlebar-close clicked'); + if (!firetrayChrome.onClose(null)) { + firetrayChrome.applyDefaultCommand("titlebar-close"); + } + }, old: null, type: null} + }, + + replaceCommand: function(eltId, func) { + let elt = document.getElementById(eltId); + if (!elt) { + firetray_log.info("Element '"+eltId+"' not found. Command not replaced."); + return null; + } + + let command = elt.command; + let oncommand = elt.getAttribute("oncommand"); + let old = null, type = null; + if (command) { + firetray_log.debug('command'); + type = FIRETRAY_XUL_ATTRIBUTE_COMMAND; + old = elt.command; + elt.command = null; + elt.addEventListener('click', func, false); + } else if (oncommand) { + firetray_log.debug('oncommand'); + type = FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND; + let prev = elt.getAttribute("oncommand"); + old = new Function(prev); + elt.setAttribute("oncommand", void(0)); + elt.addEventListener('command', func, false); + } else { + firetray_log.warn('Could not replace oncommand on '+eltId); + } + + return [old, type]; + }, + applyDefaultCommand: function(key) { + let callType = this.titlebarDispatch[key]['type']; + if (callType === FIRETRAY_XUL_ATTRIBUTE_COMMAND) { + let cmdName = firetrayChrome.titlebarDispatch[key]['old']; + let cmd = document.getElementById(cmdName); + cmd.doCommand(); + + } else if (callType === FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND) { + firetrayChrome.titlebarDispatch[key]['old'](); + + } else { + firetray_log.error("Calling type undefined for "+key); } } + }; // should be sufficient for a delayed Startup (no need for window.setTimeout()) diff --git a/src/modules/FiretrayHandler.jsm b/src/modules/FiretrayHandler.jsm index 0c4a59a..2cce91b 100644 --- a/src/modules/FiretrayHandler.jsm +++ b/src/modules/FiretrayHandler.jsm @@ -370,7 +370,7 @@ firetray.Handler = { } }, - hideOnMinimizeMaybe: function(wid) { + onMinimize: function(wid) { let hidden = false; let hides_on_minimize = firetray.Utils.prefService.getBoolPref('hides_on_minimize'); if (hides_on_minimize) { diff --git a/src/modules/commons.js b/src/modules/commons.js index c0f3f1f..ade9158 100644 --- a/src/modules/commons.js +++ b/src/modules/commons.js @@ -15,7 +15,8 @@ var EXPORTED_SYMBOLS = "FIRETRAY_ACCOUNT_SERVER_TYPE_IM", "FIRETRAY_DELAY_STARTUP_MILLISECONDS", "FIRETRAY_DELAY_NOWAIT_MILLISECONDS", "FIRETRAY_MESSAGE_COUNT_TYPE_UNREAD", "FIRETRAY_MESSAGE_COUNT_TYPE_NEW", "FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL", - "FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE", "FIRETRAY_APP_DB" ]; + "FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE", "FIRETRAY_APP_DB", + "FIRETRAY_XUL_ATTRIBUTE_COMMAND", "FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND" ]; const Cc = Components.classes; const Ci = Components.interfaces; @@ -55,6 +56,9 @@ const FIRETRAY_DELAY_NOWAIT_MILLISECONDS = 0; const FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL = 0; const FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE = 1; +const FIRETRAY_XUL_ATTRIBUTE_COMMAND = 0; +const FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND = 1; + const FIRETRAY_APP_DB = { firefox: { @@ -266,6 +270,17 @@ firetray.Utils = { timer.initWithCallback({ notify: callback }, delay, timerType); return timer; + }, + + /* + * Extracts statements from functions. Intended for feeding a 'oncommand' + * attribute. + * BUG: |let| assignations break oncommand inline callback under TB27 + * The statements should probably be limited to a single function call. + */ + bodyToString: function(func) { + let matches = func.toSource().match(/\{([\s\S]*)\}/m); + return matches ? matches[1] : matches; } }; diff --git a/src/modules/linux/FiretrayWindow.jsm b/src/modules/linux/FiretrayWindow.jsm index 7c77713..577114d 100644 --- a/src/modules/linux/FiretrayWindow.jsm +++ b/src/modules/linux/FiretrayWindow.jsm @@ -560,7 +560,7 @@ firetray.Window.filterWindow = function(xev, gdkEv, data) { // prevent the event. if (isHidden) { log.debug("GOT ICONIFIED"); - firetray.Handler.hideOnMinimizeMaybe(xid); + firetray.Handler.onMinimize(xid); } break; diff --git a/src/modules/winnt/FiretrayWindow.jsm b/src/modules/winnt/FiretrayWindow.jsm index afd9a4f..eed2bd1 100644 --- a/src/modules/winnt/FiretrayWindow.jsm +++ b/src/modules/winnt/FiretrayWindow.jsm @@ -74,7 +74,7 @@ firetray.Window.wndProc = function(hWnd, uMsg, wParam, lParam) { // filterWindow log.debug("wndProc CALLED with WM_SYSCOMMAND wParam="+wParam); if (wParam === win32.SC_MINIMIZE) { log.debug("GOT ICONIFIED"); - if (firetray.Window.hideOnMinimizeMaybe(wid)) { + if (firetray.Handler.hideOnMinimizeMaybe(wid)) { return 0; // processed => preventDefault } }