From 2231b514e3646c6762818926b45b836e4263c0ea Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 28 Jan 2014 17:39:18 +0100 Subject: initial import --- chrome/content/topmenuservice.js | 792 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 792 insertions(+) create mode 100644 chrome/content/topmenuservice.js (limited to 'chrome/content/topmenuservice.js') diff --git a/chrome/content/topmenuservice.js b/chrome/content/topmenuservice.js new file mode 100644 index 0000000..ffafba8 --- /dev/null +++ b/chrome/content/topmenuservice.js @@ -0,0 +1,792 @@ +var EXPORTED_SYMBOLS = ["TopMenuService"]; + +var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; + +Cu.import("resource://gre/modules/ctypes.jsm"); +Cu.import("chrome://topmenu/content/log4moz.js"); +Cu.import("chrome://topmenu/content/glib.js"); +Cu.import("chrome://topmenu/content/gobject.js"); +Cu.import("chrome://topmenu/content/gdk.js"); +Cu.import("chrome://topmenu/content/gdk-pixbuf.js"); +Cu.import("chrome://topmenu/content/gtk.js"); +Cu.import("chrome://topmenu/content/topmenu-client.js"); +Cu.import("chrome://topmenu/content/vkgdkmap.js"); + +var log = Log4Moz.repository.getLogger("topmenu.TopMenuService"); + +/* Escape a string so that it is a valid regular expression literal. */ +function escapePreg(s) { + if (!s) return ""; + var regex = new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\-]', 'g'); + return s.replace(regex, "\\$&"); +} + +function getBaseWindowInterface(w) { + return w.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem) + .treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIBaseWindow); +} + +function getTopLevelGdkWindow(w) { + var baseWindow = getBaseWindowInterface(w); + var handle = baseWindow.nativeHandle; + var gdkWindow = new gdk.GdkWindow.ptr(ctypes.UInt64(handle)); + return gdk.gdk_window_get_toplevel(gdkWindow); +} + +function createMutationObserver(window, proxy) +{ + return new window.MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + proxy.handleMutation(mutation); + }); + }); +} + +function WindowProxy(window) { + this.srcWindow = window; + this.srcMenuBar = null; + this.observer = null; + this.appMenuBar = null; + this.gdkWindow = getTopLevelGdkWindow(window); + this.accelGroup = null; + + this.flags = {}; + this.updateFlags(); + + var self = this; + this.lastCallbackId = 0; + this.callbackItems = {}; + this.callbacks = { + activate : gtk.GtkMenuItemActivate.ptr(function (gtkItem, data) { + try { + var id = ctypes.cast(data, ctypes.uintptr_t).value; + var item = self.callbackItems[id]; + self.activateItem(item); + } catch (ex) { + Cu.reportError(ex); + log.error("Exception in callback: " + ex.toString()); + } + }), + select : gtk.GtkItemSelect.ptr(function (gtkItem, data) { + try { + var id = ctypes.cast(data, ctypes.uintptr_t).value; + var item = self.callbackItems[id]; + self.selectItem(item); + } catch (ex) { + Cu.reportError(ex); + log.error("Exception in callback: " + ex.toString()); + } + }), + deselect : gtk.GtkItemDeselect.ptr(function (gtkItem, data) { + try { + var id = ctypes.cast(data, ctypes.uintptr_t).value; + var item = self.callbackItems[id]; + self.deselectItem(item); + } catch (ex) { + Cu.reportError(ex); + log.error("Exception in callback: " + ex.toString()); + } + }), + monitor_available : gobject.GObjectNotifyCallback.ptr(function (obj, pspec, data) { + self.updateMenuBarVisibility(); + }), + keypress : function(e) { + try { + self.onKeyPress(e); + } catch (ex) { + Cu.reportError(ex); + log.error("Exception in callback: " + ex.toString()); + } + } + } + + this.accelGroup = gobject.ref_sink(gtk.gtk_accel_group_new()); + + this.appMenuBar = gobject.ref_sink(topmenu_client.topmenu_app_menu_bar_new()); + gtk.gtk_widget_show(this.appMenuBar); + + 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 + } + + for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) { + this.addItem(item, this.srcMenuBar, -1); + } + + 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); + } + + this.monitor = topmenu_client.topmenu_monitor_get_instance(); + this.monitorConnectionId = gobject.signal_connect(this.monitor, "notify::available", + this.callbacks.monitor_available, null); + this.updateMenuBarVisibility(); +} + +WindowProxy.prototype.updateMenuBarVisibility = function() { + var topmenuAvailable = topmenu_client.topmenu_monitor_is_topmenu_available(this.monitor); + if (this.srcMenuBar) { + this.srcMenuBar.parentNode.parentNode.hidden = topmenuAvailable; + } +} + +WindowProxy.prototype.buildAppMenuItem = function(itemId, stockId) +{ + var item = this.srcWindow.document.getElementById(itemId); + + if (item) { + var gtkItem = gtk.gtk_image_menu_item_new_from_stock(stockId, null); + gtk.gtk_widget_show(gtkItem); + + + var callbackId = this.lastCallbackId++; + this.callbackItems[callbackId] = item; + + gobject.signal_connect(gtkItem, 'activate', + this.callbacks.activate, + glib.gpointer(callbackId)); + + return gtkItem; + } else { + return null; + } +} + +WindowProxy.prototype.buildAppMenu = function() { + var menu = gobject.ref_sink(gtk.gtk_menu_new()); + var item; + + item = this.buildAppMenuItem('aboutName', 'gtk-about'); + if (item) { + gtk.gtk_menu_shell_append(menu, item); + } + + item = this.buildAppMenuItem('menu_preferences', 'gtk-preferences'); + if (item) { + gtk.gtk_menu_shell_append(menu, item); + } + + item = gtk.gtk_separator_menu_item_new(); + gtk.gtk_widget_show(item); + gtk.gtk_menu_shell_append(menu, item); + + item = this.buildAppMenuItem('menu_FileQuitItem', 'gtk-quit'); + if (item) { + gtk.gtk_menu_shell_append(menu, item); + } + + return menu; +} + +WindowProxy.prototype.handleMutation = function(mutation) { + var target = mutation.target; + + switch (mutation.type) { + case 'childList': + switch (target.tagName) { + case 'menupopup': + // Get the parent menu for this menupopup: + target = mutation.target.parentNode; + + if ('topmenuData' in target) { + this.updateMenu(target, mutation.previousSibling, mutation.removedNodes, mutation.addedNodes); + } else { + log.warn("I do not know about this menupoup"); + } + break; + case 'menubar': + // Changes in the root menu bar. + if ('topmenuData' in target) { + this.updateMenu(target, mutation.previousSibling, mutation.removedNodes, mutation.addedNodes); + } else { + log.warn("I do not know about this menubar"); + } + } + break; + case 'attributes': + switch (target.tagName) { + case 'menupopup': + case 'menuitem': + case 'menu': + if ('topmenuData' in target) { + this.updateItem(mutation.target, mutation.attributeName); + } + break; + case 'command': + if (mutation.attributeName === 'disabled' && 'topmenuData' in target) { + target.topmenuData.menuitems.forEach(function(item) { + this.updateItem(item, 'disabled'); + }, this); + } + } + break; + } +} + +WindowProxy.prototype.updateFlags = function() { + var prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService).getBranch(""); + this.flags.accelKey = prefs.getIntPref("ui.key.accelKey"); + this.flags.alwaysAppendAccessKeys = + prefs.getComplexValue("intl.menuitems.alwaysappendaccesskeys", + Ci.nsIPrefLocalizedString).data == "true"; + this.flags.insertSeparatorBeforeAccessKeys = + prefs.getComplexValue( + "intl.menuitems.insertseparatorbeforeaccesskeys", + Ci.nsIPrefLocalizedString).data == "true"; + this.flags.ellipsis = prefs.getComplexValue("intl.ellipsis", + Ci.nsIPrefLocalizedString).data; +} + +/* Convert from XUL mnemonic format to Gtk+ */ +/* https://developer.mozilla.org/En/XUL_Tutorial/Accesskey_display_rules */ +WindowProxy.prototype.createLabelWithAccessKey = function(label, accesskey) { + label = label.replace(/_/g, "__"); + if (accesskey) { + var regex = new RegExp(escapePreg(accesskey), "i"); + if (!this.flags.alwaysAppendAccessKeys && label.search(regex) >= 0) { + /* "Inline" access key (ex. "_File") */ + return label.replace(regex, "_$&"); + } else { + /* Appended access key (ex. "File (_A)") */ + if (label.slice(-this.flags.ellipsis.length) == this.flags.ellipsis) { + /* Label ends with ellipsis */ + return label.substring(0, label.length-this.flags.ellipsis.length) + + (this.flags.insertSeparatorBeforeAccessKeys ? " " : "") + + "(_" + accesskey.toUpperCase() + ")" + + this.flags.ellipsis; + } else { + return label + + (this.flags.insertSeparatorBeforeAccessKeys ? " " : "") + + "(_" + accesskey.toUpperCase() + ")"; + } + } + } else { + /* No access key */ + return label; + } +} + +WindowProxy.prototype.createImageWidgetFromImage = function(image, rect) { + var canvas = this.srcWindow.document.createElementNS("http://www.w3.org/1999/xhtml","canvas"); + var ctx = canvas.getContext('2d'); + + if (!rect[2]) rect[2] = image.width; + if (!rect[3]) rect[3] = image.height; + + ctx.drawImage(image, rect[0], rect[1], rect[2], rect[3], 0, 0, 16, 16); + + var img_data = ctx.getImageData(0,0, 16, 16); + var pix_data = ctypes.cast(ctypes.uint8_t.array()(img_data.data).address(), glib.guchar.ptr); + var pixbuf = gobject.ref_sink(gdk_pixbuf.gdk_pixbuf_new_from_data(pix_data, gdk_pixbuf.GDK_COLORSPACE_RGB, true, 8, 16, 16, 16 * 4, null, null)); + var pixbuf_copy = gobject.ref_sink(gdk_pixbuf.gdk_pixbuf_copy(pixbuf)); + + var gtkImage = gtk.gtk_image_new_from_pixbuf(pixbuf_copy); + + pixbuf.dispose(); + pixbuf_copy.dispose(); + + return gtkImage; +} + +WindowProxy.prototype.createImageWidget = function(item, style) { + var image_uri = item.getAttribute("image"); + var res; + + if (!image_uri) { + var style_regex = /url\("(.*?)"\)/; + var image_style = style.getPropertyValue("list-style-image"); + res = style_regex.exec(image_style); + if (res) { + image_uri = res[1]; + } + } + + if (image_uri) { + /* Test if it is a Gtk stock image first */ + var mozicon_regex = /moz-icon:\/\/stock\/(.*?)\?/; + res = mozicon_regex.exec(image_uri); + if (res) { + return gtk.gtk_image_new_from_stock(res[1], gtk.GTK_ICON_SIZE_MENU); + } + + /* Handle image clipping */ + var rect = [0, 0, 0, 0]; + var region = style.getPropertyValue("-moz-image-region"); + if (region && region !== "auto") { + var region_regex = /rect\((\d+)px, (\d+)px, (\d+)px, (\d+)px\)/; + var values = region_regex.exec(region); + if (values) { + rect[0] = parseInt(values[4]); /* Left */ + rect[1] = parseInt(values[1]); /* Top */ + rect[2] = parseInt(values[2]) - rect[0]; /* Right - Left*/ + rect[3] = parseInt(values[3]) - rect[1]; /* Bottom - Top */ + } + } + + var img_elem = new this.srcWindow.Image(); + img_elem.src = image_uri; + + if (img_elem.complete) { + return this.createImageWidgetFromImage(img_elem, rect); + } else { + // Set a handler to try again when the image is loaded + var self = this; + img_elem.onload = function() { + self.updateItem(item, "image"); + } + item.topmenuImgLoader = img_elem; + return "pending"; + } + } + + return null; +} + +WindowProxy.prototype.addAccelerator = function(gtkItem, item) { + var keyId = item.getAttribute("key"); + if (!keyId) return false; + var keyItem = this.srcWindow.document.getElementById(keyId); + if (!keyItem) return false; + + var keyItemMods = keyItem.getAttribute("modifiers"); + var gtkMods = 0; + + if (keyItemMods) { + keyItemMods = keyItemMods.split(/[\s,]+/); + for (i in keyItemMods) { + var mod = keyItemMods[i]; + if (mod === "accel") { + /* Read platform default accelerator key from prefs */ + switch (this.flags.accelKey) { + case this.srcWindow.KeyEvent.DOM_VK_SHIFT: + mod = "shift"; + break; + case this.srcWindow.KeyEvent.DOM_VK_CONTROL: + mod = "control"; + break; + case this.srcWindow.KeyEvent.DOM_VK_ALT: + mod = "alt"; + break; + case this.srcWindow.KeyEvent.DOM_VK_META: + mod = "meta"; + break; + } + } + switch (mod) { + case "shift": + gtkMods += gdk.GDK_SHIFT_MASK; + break; + case "alt": + gtkMods += gdk.GDK_ALT_MASK; + break; + case "meta": + gtkMods += gdk.GDK_META_MASK; + break; + case "control": + gtkMods += gdk.GDK_CONTROL_MASK; + break; + default: + continue; + } + } + } + + var keyItemKey = keyItem.getAttribute("key"); + var keyItemCode = keyItem.getAttribute("keycode"); + var gtkKey = 0; + + if (keyItemKey) { + gtkKey = gdk.gdk_unicode_to_keyval(keyItemKey.charCodeAt(0)); + } else if (keyItemCode) { + gtkKey = vkgdkmap[keyItemCode]; + if (!gtkKey) { + log.info("Keycode " + keyItemCode + " is unmapped"); + } + } + + if (gtkKey) { + gtk.gtk_widget_add_accelerator(gtkItem, "activate", this.accelGroup, + gtkKey, gtkMods, gtk.GTK_ACCEL_VISIBLE); + } +} + +WindowProxy.prototype.addItem = function(item, menu, position) { + var disabled = item.disabled; + var style = this.srcWindow.getComputedStyle(item, null); + var label = this.createLabelWithAccessKey(item.getAttribute('label'), item.accessKey); + var visible = !item.hidden && !item.collapsed && style.display !== 'none'; + var hasSubMenu = item.tagName === "menu" && item.firstChild + && item.firstChild.tagName === "menupopup"; + + var gtkItem; + if (item.tagName === "menuseparator") { + gtkItem = gobject.ref_sink(gtk.gtk_separator_menu_item_new()); + } else if (item.tagName === "menu") { + gtkItem = gobject.ref_sink(gtk.gtk_menu_item_new_with_mnemonic(label)); + } else if (item.tagName === "menuitem") { + var image = this.createImageWidget(item, style); + if (image) { + gtkItem = gobject.ref_sink(gtk.gtk_image_menu_item_new_with_mnemonic(label)); + if (image !== "pending") { + gtk.gtk_image_menu_item_set_image(gtkItem, image); + } + } else { + var type = item.getAttribute("type"); + var checked = item.getAttribute("checked") ? true : false; + switch (type) { + case "checkbox": + gtkItem = gobject.ref_sink(gtk.gtk_check_menu_item_new_with_mnemonic(label)); + gtk.gtk_check_menu_item_set_active(gtkItem, checked); + break; + case "radio": + gtkItem = gobject.ref_sink(gtk.gtk_check_menu_item_new_with_mnemonic(label)); + gtk.gtk_check_menu_item_set_draw_as_radio(gtkItem, true); + gtk.gtk_check_menu_item_set_active(gtkItem, checked); + break; + default: + gtkItem = gobject.ref_sink(gtk.gtk_menu_item_new_with_mnemonic(label)); + } + } + + var command = item.getAttribute('command'); + if (command) { + command = this.srcWindow.document.getElementById(command); + if (command) { + if (!('topmenuData' in command)) { + command.topmenuData = { + menuitems: [item] + }; + } else { + command.topmenuData.menuitems.push(item); + } + + this.observer.observe(command, {attributes: true}); + + if (command.getAttribute('disabled')) { + disabled = true; + } + } + } + + if (item.hasAttribute("key")) { + try { + this.addAccelerator(gtkItem, item); + } catch (ex) { + log.warn(ex); + } + } + } else { + log.warn("Unknown tagName: " + item.tagName); + return; + } + + gtk.gtk_widget_set_sensitive(gtkItem, !disabled); + gtk.gtk_widget_set_visible(gtkItem, visible); + + item.topmenuData = { + gtkItem : gtkItem, + callbackId: this.lastCallbackId++ + }; + this.callbackItems[item.topmenuData.callbackId] = item; + + if (hasSubMenu) { + var gtkSubMenu = this.addMenu(item); + gtk.gtk_menu_item_set_submenu(gtkItem, gtkSubMenu); + } + + var gtkMenu = menu.topmenuData.gtkMenu; + if (position >= 0) { + gtk.gtk_menu_shell_insert(gtkMenu, gtkItem, position); + } else { + gtk.gtk_menu_shell_append(gtkMenu, gtkItem); + } + + var conn_id; + if (hasSubMenu) { + conn_id = gobject.signal_connect(gtkItem, 'select', + this.callbacks.select, + glib.gpointer(item.topmenuData.callbackId)); + item.topmenuData.selectConnectionId = conn_id; + + conn_id = gobject.signal_connect(gtkItem, 'deselect', + this.callbacks.deselect, + glib.gpointer(item.topmenuData.callbackId)); + item.topmenuData.deselectConnectionId = conn_id; + } else { + conn_id = gobject.signal_connect(gtkItem, 'activate', + this.callbacks.activate, + glib.gpointer(item.topmenuData.callbackId)); + item.topmenuData.activateConnectionId = conn_id; + } + + return gtkItem; +} + +WindowProxy.prototype.addMenu = function(menu) { + var gtkMenu = gobject.ref_sink(gtk.gtk_menu_new()); + + menu.topmenuData.gtkMenu = gtkMenu; + + var popup = menu.firstChild; + for (var item = popup.firstChild; item; item = item.nextSibling) { + this.addItem(item, menu, -1); + } + + return gtkMenu; +} + +WindowProxy.prototype.removeItem = function(item) { + var hasSubMenu = item.tagName === "menu" && item.firstChild + && item.firstChild.tagName === "menupopup"; + + if (hasSubMenu) { + this.removeMenu(item); + } + + delete this.callbackItems[item.topmenuData.callbackId]; + + var gtkItem = item.topmenuData.gtkItem; + + gtk.gtk_widget_destroy(gtkItem); + gtkItem.forget(); + + delete item.topmenuData; +} + +WindowProxy.prototype.removeMenu = function(menu) { + var gtkMenu = menu.topmenuData.gtkMenu; + + gtk.gtk_widget_destroy(gtkMenu); + gtkMenu.forget(); +} + +WindowProxy.prototype.updateItem = function(item, changedAttr) { + var gtkItem = item.topmenuData.gtkItem; + var style = this.srcWindow.getComputedStyle(item, null); + + switch (changedAttr) { + case "accesskey": + case "label": + var label = this.createLabelWithAccessKey(item.getAttribute('label'), item.accessKey); + gtk.gtk_menu_item_set_label(gtkItem, label); + break; + case "disabled": + var disabled = item.disabled; + var command = item.getAttribute('command'); + if (!disabled && command) { + command = this.srcWindow.document.getElementById(command); + if (command) { + if (command.getAttribute('disabled')) { + disabled = true; + } + } + } + + gtk.gtk_widget_set_sensitive(gtkItem, !disabled); + break; + case "hidden": + case "collapsed": + var visible = !item.hidden && !item.collapsed && style.display !== 'none'; + gtk.gtk_widget_set_visible(gtkItem, visible); + break; + case "image": + var image = this.createImageWidget(item, style); + if (image !== 'pending') { + delete item.topmenuImgLoader; + gtk.gtk_image_menu_item_set_image(gtkItem, image); + } + break; + } +} + +WindowProxy.prototype.updateMenu = function(menu, prevItem, removedItems, addedItems) { + var position, i, node; + var popup = menu.firstChild; + + if (removedItems.length > 0) { + for (i = 0; i < removedItems.length; i++) { + this.removeItem(removedItems[i]); + } + } + + if (addedItems.length > 0) { + if (prevItem) { + position = -1; + i = 2; + for (node = popup.firstChild; node; node = node.nextSibling, i++) { + if (node === prevItem) { + position = i; + break; + } + } + if (position < 0) { + log.warn("prevItem node not found"); + position = 0; + } + } else { + position = 0; + } + + for (i = 0; i < addedItems.length; i++) { + this.addItem(addedItems[i], menu, position); + } + } +} + +WindowProxy.prototype.activateItem = function(item) { + this.fakeCommandEvent(item); +} + +WindowProxy.prototype.selectItem = function(item) { + var menu = item; + var popup = menu.firstChild; + if (popup.tagName !== 'menupopup') { + log.warn("Selected a menu without a menupopup"); + return; + } + + this.fakePopupEvent(popup, "popupshowing"); + menu.open = "true"; + + // A hack for Firefox and friends + if (menu.id === 'edit-menu' && 'gEditUIVisible' in this.srcWindow) { + this.srcWindow.gEditUIVisible = true; + this.srcWindow.goUpdateGlobalEditMenuItems(); + } + + // Let's update the label of every item at least. + for (var i = popup.firstChild; i; i = i.nextSibling) { + switch (i.tagName) { + case 'menu': + case 'menuitem': + this.updateItem(i, 'label'); + this.updateItem(i, 'hidden'); + break; + case 'menuseparator': + this.updateItem(i, 'hidden'); + break; + } + } + + this.fakePopupEvent(popup, "popupshown"); +} + +WindowProxy.prototype.deselectItem = function(item) { + var menu = item; + var popup = menu.firstChild; + if (popup.tagName !== 'menupopup') { + log.warn("Selected a menu without a menupopup"); + return; + } + + this.fakePopupEvent(popup, "popuphiding"); + menu.removeAttribute('open'); + + if (menu.id === 'edit-menu' && 'gEditUIVisible' in this.srcWindow) { + this.srcWindow.gEditUIVisible = false; + } + + this.fakePopupEvent(popup, "popuphidden"); +} + +WindowProxy.prototype.onKeyPress = function(event) { + if (!event.altKey) return; + if (!event.which) return; + + var char = String.fromCharCode(event.which).toLowerCase(); + if (!char) return; + + for (var item = this.srcMenuBar.firstChild; item; item = item.nextSibling) { + if ('topmenuData' in item && + item.getAttribute('accesskey').toLowerCase() === char) { + event.preventDefault(); + event.stopImmediatePropagation(); + gtk.gtk_widget_mnemonic_activate(item.topmenuData.gtkItem, false); + } + } +} + +WindowProxy.prototype.fakePopupEvent = function(popup, type) { + var window = this.srcWindow; + var popupEvent = window.document.createEvent("MouseEvent"); + popupEvent.initMouseEvent(type, true, true, window, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + popup.dispatchEvent(popupEvent); +} + +WindowProxy.prototype.fakeActiveEvent = function(item, type) { + var window = this.srcWindow; + var activeEvent = window.document.createEvent("Events"); + activeEvent.initEvent(type, true, false); + item.dispatchEvent(activeEvent); +} + +WindowProxy.prototype.fakeCommandEvent = function(item) { + var window = this.srcWindow; + var commandEvent = window.document.createEvent("XULCommandEvent"); + commandEvent.initCommandEvent("command", true, true, window, + 0, false, false, false, false, null); + item.dispatchEvent(commandEvent); +} + +WindowProxy.prototype.dispose = function() { + window.document.removeEventListener("keypress", this.callbacks.keypress); + if (this.monitor) { + if (this.monitorConnectionId) { + gobject.g_signal_handler_disconnect(this.monitor, this.monitorConnectionId); + } + this.monitorConnectionId = 0; + this.monitor = null; + } + if (this.observer) { + this.observer.disconnect(); + this.observer = null; + } + if (this.appMenuBar) { + this.appMenuBar.dispose(); + this.appMenuBar = null; + } + if (this.appMenu) { + this.appMenu.dispose(); + this.appMenu = null; + } + if (this.accelGroup) { + this.accelGroup.dispose(); + this.accelGroup = null; + } + + this.gdkWindow = null; + this.srcWindow = null; + this.menus = null; + this.items = null; +} + +var TopMenuService = { + createWindowProxy: function(window) { + return new WindowProxy(window); + } +} -- cgit v1.2.3