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 --- chrome/content/topmenuservice.js | 98 ++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 34 deletions(-) (limited to 'chrome/content/topmenuservice.js') 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) { -- cgit v1.2.3