From 1fbcb3f7bf7ddf955193d798837ab88cf2815112 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 23 Feb 2014 18:32:00 +0100 Subject: convert into a restartless extension --- Makefile | 2 +- bootstrap.js | 100 +++++++++++++++++++++++++++++++++++++++ chrome.manifest | 6 --- chrome/content/gdk.js | 20 ++++---- chrome/content/overlay.js | 52 -------------------- chrome/content/overlay.xul | 4 -- chrome/content/topmenu-client.js | 2 + chrome/content/topmenuservice.js | 98 +++++++++++++++++++++++++------------- chrome/content/vkgdkmap.js | 17 ++++--- install.rdf | 3 +- topmenu-mozilla.files | 3 +- 11 files changed, 190 insertions(+), 117 deletions(-) create mode 100644 bootstrap.js delete mode 100644 chrome/content/overlay.js delete mode 100644 chrome/content/overlay.xul diff --git a/Makefile b/Makefile index 51d3e3f..5cededa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ ZIP?=zip # What goes inside the .xpi package -CONTENT:=install.rdf chrome.manifest \ +CONTENT:=install.rdf chrome.manifest bootstrap.js \ $(wildcard chrome/content/*) all: dist diff --git a/bootstrap.js b/bootstrap.js new file mode 100644 index 0000000..080d98c --- /dev/null +++ b/bootstrap.js @@ -0,0 +1,100 @@ +"use strict"; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +function loadIntoWindow(window) { + if (!window) + return; + + window.topmenuProxy = TopMenuService.createWindowProxy(window); +} + +function unloadFromWindow(window) { + if (!window) + return; + if (!window.topmenuProxy) + return; + + window.topmenuProxy.unload(); + window.topmenuProxy.dispose(); + window.topmenuProxy = null; +} + +var windowListener = { + onOpenWindow: function(window) { + // Wait for the window to finish loading + var domWindow = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow); + domWindow.addEventListener("load", function onLoad() { + domWindow.removeEventListener("load", onLoad, false); + domWindow.addEventListener("unload", function onUnload() { + domWindow.removeEventListener("unload", onUnload, false); + unloadFromWindow(domWindow); + }, false); + loadIntoWindow(domWindow); + }, false); + }, + + onCloseWindow: function(window) {}, + onWindowTitleChange: function(window, title) {} +}; + +function startup(data, reason) { + // Setup logging + Cu.import("chrome://topmenu/content/log4moz.js"); + var formatter = new Log4Moz.BasicFormatter(); + var root = Log4Moz.repository.rootLogger; + root.level = Log4Moz.Level.Debug; + + var capp = new Log4Moz.ConsoleAppender(formatter); + capp.level = Log4Moz.Level.Debug; + root.addAppender(capp); + + var log = Log4Moz.repository.getLogger("topmenu.Bootstrap"); + + // Load module + Cu.import("chrome://topmenu/content/topmenuservice.js"); + + // Load into any existing windows + var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); + var windows = wm.getEnumerator(null); + while (windows.hasMoreElements()) { + var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + loadIntoWindow(domWindow); + } + + // Load into any new windows + wm.addListener(windowListener); +} + +function shutdown(data, reason) { + // When the application is shutting down we normally don't have to clean + // up any UI changes made + if (reason === APP_SHUTDOWN) + return; + + var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); + + // Stop listening for new windows + wm.removeListener(windowListener); + + // Unload from any existing windows + var windows = wm.getEnumerator(null); + while (windows.hasMoreElements()) { + var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow); + unloadFromWindow(domWindow); + } + + // Stop logging and unload modules + Cu.unload("chrome://topmenu/content/topmenuservice.js"); + Cu.unload("chrome://topmenu/content/log4moz.js"); +} + +function install(data, reason) { + +} + +function uninstall(data, reason) { + +} diff --git a/chrome.manifest b/chrome.manifest index 8c9f370..20e1d8e 100644 --- a/chrome.manifest +++ b/chrome.manifest @@ -1,7 +1 @@ content topmenu chrome/content/ - -overlay chrome://browser/content/browser.xul chrome://topmenu/content/overlay.xul - -overlay chrome://messenger/content/messenger.xul chrome://topmenu/content/overlay.xul -overlay chrome://messenger/content/messengercompose/messengercompose.xul chrome://topmenu/content/overlay.xul -overlay chrome://messenger/content/addressbook/addressbook.xul chrome://topmenu/content/overlay.xul diff --git a/chrome/content/gdk.js b/chrome/content/gdk.js index 25458ef..d70dfc3 100644 --- a/chrome/content/gdk.js +++ b/chrome/content/gdk.js @@ -32,6 +32,16 @@ function defines(lib) { this.GDK_KEY_Return = 0xff0d; this.GDK_KEY_Escape = 0xff1b; this.GDK_KEY_Delete = 0xffff; + this.GDK_KEY_Home = 0xff50; + this.GDK_KEY_Left = 0xff51; + this.GDK_KEY_Up = 0xff52; + this.GDK_KEY_Right = 0xff53; + this.GDK_KEY_Down = 0xff54; + this.GDK_KEY_Prior = 0xff55; + this.GDK_KEY_Page_Up = 0xff55; + this.GDK_KEY_Next = 0xff56; + this.GDK_KEY_Page_Down = 0xff56; + this.GDK_KEY_End = 0xff57; this.GDK_KEY_F1 = 0xffbe; this.GDK_KEY_F2 = 0xffbf; this.GDK_KEY_F3 = 0xffc0; @@ -45,16 +55,6 @@ function defines(lib) { this.GDK_KEY_F11 = 0xffc8; this.GDK_KEY_L1 = 0xffc8; this.GDK_KEY_F12 = 0xffc9; - this.GDK_KEY_Home = 0xff50; - this.GDK_KEY_Left = 0xff51; - this.GDK_KEY_Up = 0xff52; - this.GDK_KEY_Right = 0xff53; - this.GDK_KEY_Down = 0xff54; - this.GDK_KEY_Prior = 0xff55; - this.GDK_KEY_Page_Up = 0xff55; - this.GDK_KEY_Next = 0xff56; - this.GDK_KEY_Page_Down = 0xff56; - this.GDK_KEY_End = 0xff57; lib.lazy_bind("gdk_window_get_toplevel", this.GdkWindow.ptr, this.GdkWindow.ptr); diff --git a/chrome/content/overlay.js b/chrome/content/overlay.js deleted file mode 100644 index 29b9a07..0000000 --- a/chrome/content/overlay.js +++ /dev/null @@ -1,52 +0,0 @@ -"use strict"; - -var topmenu = { - logger : null, - proxy : null, - - setupLogging: function() { - Components.utils.import("chrome://topmenu/content/log4moz.js", topmenu); - var Log4Moz = this.Log4Moz; - var formatter = new Log4Moz.BasicFormatter(); - var root = Log4Moz.repository.rootLogger; - root.level = Log4Moz.Level.Warn; - - var capp = new Log4Moz.ConsoleAppender(formatter); - capp.level = Log4Moz.Level.Warn; - root.addAppender(capp); - - /* - var dapp = new Log4Moz.DumpAppender(formatter); - dapp.level = Log4Moz.Level.Debug; - root.addAppender(dapp); - */ - - this.logger = Log4Moz.repository.getLogger("topmenu"); - }, - - setupMenuProxy: function() { - Components.utils.import("chrome://topmenu/content/topmenuservice.js", topmenu); - this.proxy = topmenu.TopMenuService.createWindowProxy(window); - }, - - dispose: function() { - if (this.proxy) { - this.proxy.dispose(); - this.proxy = null; - } - }, - - onLoad: function() { - window.removeEventListener('load', topmenu.onLoad); - window.addEventListener('unload', topmenu.onUnload); - topmenu.setupLogging(); - topmenu.setupMenuProxy(); - }, - - onUnload: function() { - window.removeEventListener('unload', topmenu.onUnload); - topmenu.dispose(); - }, -} - -window.addEventListener('load', topmenu.onLoad); diff --git a/chrome/content/overlay.xul b/chrome/content/overlay.xul deleted file mode 100644 index 3e61323..0000000 --- a/chrome/content/overlay.xul +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/chrome/content/topmenu-client.js b/chrome/content/topmenu-client.js index a5d7ab2..af8b1d2 100644 --- a/chrome/content/topmenu-client.js +++ b/chrome/content/topmenu-client.js @@ -1,3 +1,5 @@ +"use strict"; + var EXPORTED_SYMBOLS = [ "topmenu_client" ]; const Cu = Components.utils; diff --git a/chrome/content/topmenuservice.js b/chrome/content/topmenuservice.js index 494ea3e..96d0e6c 100644 --- a/chrome/content/topmenuservice.js +++ b/chrome/content/topmenuservice.js @@ -1,3 +1,5 @@ +"use strict"; + var EXPORTED_SYMBOLS = ["TopMenuService"]; var Cu = Components.utils; @@ -42,13 +44,23 @@ function getTopLevelGdkWindow(w) { function createMutationObserver(window, proxy) { return new window.MutationObserver(function(mutations) { - mutations.forEach(function(mutation) { - proxy.handleMutation(mutation); - }); + try { + mutations.forEach(function(mutation) { + proxy.handleMutation(mutation); + }); + } catch (ex) { + log.warn("Exception processing menubar changes: " + ex); + } }); } function WindowProxy(window) { + var menubars = window.document.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'menubar'); + if (menubars.length === 0) { + // No menubars, give up constructing the object + return; + } + this.srcWindow = window; this.srcMenuBar = null; this.observer = null; @@ -106,6 +118,7 @@ function WindowProxy(window) { } } + // Initialize the Gtk widgets for this window this.accelGroup = gobject.ref_sink(gtk.gtk_accel_group_new()); this.appMenuBar = gobject.ref_sink(topmenu_client.topmenu_app_menu_bar_new()); @@ -113,48 +126,57 @@ function WindowProxy(window) { topmenu_client.topmenu_client_connect_window_widget(this.gdkWindow, this.appMenuBar); - var menubars = window.document.getElementsByTagNameNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'menubar'); - if (menubars.length > 0) { - this.srcMenuBar = menubars[0]; - this.observer = createMutationObserver(window, this); - this.observer.observe(this.srcMenuBar, { - childList: true, - attributes: true, - subtree: true - }); - - // Let's find currently added menu items and proxy them - this.srcMenuBar.topmenuData = { - gtkMenu: this.appMenuBar - } + // Connect to the first available menu bar + this.srcMenuBar = menubars[0]; + this.observer = createMutationObserver(window, this); + this.observer.observe(this.srcMenuBar, { + childList: true, + attributes: true, + subtree: true + }); - for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) { - this.addItem(item, this.srcMenuBar, -1); - } + // Let's find currently added menu items and proxy them + this.srcMenuBar.topmenuData = { + gtkMenu: this.appMenuBar + } - this.appMenu = this.buildAppMenu(); - if (this.appMenu) { - topmenu_client.topmenu_app_menu_bar_set_app_menu(this.appMenuBar, this.appMenu); - } + for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) { + this.addItem(item, this.srcMenuBar, -1); + } - window.document.addEventListener("keypress", this.callbacks.keypress); + this.appMenu = this.buildAppMenu(); + if (this.appMenu) { + topmenu_client.topmenu_app_menu_bar_set_app_menu(this.appMenuBar, this.appMenu); } + window.document.addEventListener("keypress", this.callbacks.keypress); + + // Connect to the appmenu monitor this.monitor = topmenu_client.topmenu_monitor_get_instance(); this.monitorConnectionId = gobject.signal_connect(this.monitor, "notify::available", this.callbacks.monitor_available, null); + + // This will hide the source menu bar if everything's OK. this.updateMenuBarVisibility(); } -WindowProxy.prototype.updateMenuBarVisibility = function() { - var topmenuAvailable = topmenu_client.topmenu_monitor_is_topmenu_available(this.monitor); +WindowProxy.prototype.setMenuBarVisibility = function(visible) { if (this.srcMenuBar) { - this.srcMenuBar.parentNode.parentNode.hidden = topmenuAvailable; + this.srcMenuBar.hidden = !visible; + // A hack for windows containing the menubar in a toolbar. + var toolbar = this.srcMenuBar.parentNode.parentNode; + if (toolbar && toolbar.tagName === 'toolbar') { + toolbar.hidden = !visible; + } } } -WindowProxy.prototype.buildAppMenuItem = function(itemId, stockId) -{ +WindowProxy.prototype.updateMenuBarVisibility = function() { + var topmenuAvailable = topmenu_client.topmenu_monitor_is_topmenu_available(this.monitor); + this.setMenuBarVisibility(!topmenuAvailable); +} + +WindowProxy.prototype.buildAppMenuItem = function(itemId, stockId) { var item = this.srcWindow.document.getElementById(itemId); if (item) { @@ -377,7 +399,7 @@ WindowProxy.prototype.addAccelerator = function(gtkItem, item) { if (keyItemMods) { keyItemMods = keyItemMods.split(/[\s,]+/); - for (i in keyItemMods) { + for (var i in keyItemMods) { var mod = keyItemMods[i]; if (mod === "accel") { /* Read platform default accelerator key from prefs */ @@ -496,9 +518,12 @@ WindowProxy.prototype.addItem = function(item, menu, position) { try { this.addAccelerator(gtkItem, item); } catch (ex) { - log.warn(ex); + log.warn("Exception during accelerator computation: " + ex); } } + } else if (item.tagName === "spacer") { + // Nothing to do + return; } else { log.warn("Unknown tagName: " + item.tagName); return; @@ -624,7 +649,7 @@ WindowProxy.prototype.updateItem = function(item, changedAttr) { } WindowProxy.prototype.updateMenu = function(menu, prevItem, removedItems, addedItems) { - var position, i, node; + var position, i; var popup = menu.firstChild; if (removedItems.length > 0) { @@ -637,7 +662,7 @@ WindowProxy.prototype.updateMenu = function(menu, prevItem, removedItems, addedI if (prevItem) { position = -1; i = 2; - for (node = popup.firstChild; node; node = node.nextSibling, i++) { + for (var node = popup.firstChild; node; node = node.nextSibling, i++) { if (node === prevItem) { position = i; break; @@ -753,6 +778,11 @@ WindowProxy.prototype.fakeCommandEvent = function(item) { item.dispatchEvent(commandEvent); } +WindowProxy.prototype.unload = function() { + this.setMenuBarVisibility(true); + topmenu_client.topmenu_client_disconnect_window(this.gdkWindow); +} + WindowProxy.prototype.dispose = function() { window.document.removeEventListener("keypress", this.callbacks.keypress); if (this.monitor) { diff --git a/chrome/content/vkgdkmap.js b/chrome/content/vkgdkmap.js index 35a8fc5..088a212 100644 --- a/chrome/content/vkgdkmap.js +++ b/chrome/content/vkgdkmap.js @@ -1,13 +1,20 @@ +"use strict"; + var EXPORTED_SYMBOLS = [ "vkgdkmap" ]; const Cu = Components.utils; Cu.import("chrome://topmenu/content/gdk.js"); -/* Hardcoding DOM_VK_* constants, how ugly. */ -/* But how to get a window scope from here? Need to access KeyEvent. */ +var vkgdkmap = { + VK_TAB : gdk.GDK_KEY_Tab, + VK_RETURN : gdk.GDK_KEY_Return, + + VK_HOME : gdk.GDK_KEY_Home, + VK_END : gdk.GDK_KEY_End, + + VK_DELETE : gdk.GDK_KEY_Delete, -vkgdkmap = { VK_F1 : gdk.GDK_KEY_F1, VK_F2 : gdk.GDK_KEY_F2, VK_F3 : gdk.GDK_KEY_F3, @@ -20,8 +27,4 @@ vkgdkmap = { VK_F10 : gdk.GDK_KEY_F10, VK_F11 : gdk.GDK_KEY_F11, VK_F12 : gdk.GDK_KEY_F12, - - VK_TAB : gdk.GDK_KEY_Tab, - VK_DELETE : gdk.GDK_KEY_Delete - } diff --git a/install.rdf b/install.rdf index 11d48d3..3787168 100644 --- a/install.rdf +++ b/install.rdf @@ -3,8 +3,9 @@ xmlns:em="http://www.mozilla.org/2004/em-rdf#"> topmenu.mozilla@javispedro.com - 0.1 + 0.2 2 + true diff --git a/topmenu-mozilla.files b/topmenu-mozilla.files index 65ebee1..799baeb 100644 --- a/topmenu-mozilla.files +++ b/topmenu-mozilla.files @@ -1,8 +1,6 @@ chrome.manifest install.rdf Makefile -chrome/content/overlay.xul -chrome/content/overlay.js chrome/content/log4moz.js chrome/content/ctypes-utils.js chrome/content/topmenuservice.js @@ -13,3 +11,4 @@ chrome/content/gtk.js chrome/content/gdk.js chrome/content/gdk-pixbuf.js chrome/content/vkgdkmap.js +bootstrap.js -- cgit v1.2.3