diff options
Diffstat (limited to 'widget/tests/standalone_native_menu_window.xhtml')
-rw-r--r-- | widget/tests/standalone_native_menu_window.xhtml | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/widget/tests/standalone_native_menu_window.xhtml b/widget/tests/standalone_native_menu_window.xhtml new file mode 100644 index 0000000000..4155126ad5 --- /dev/null +++ b/widget/tests/standalone_native_menu_window.xhtml @@ -0,0 +1,374 @@ +<?xml version="1.0"?> + +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window id="StandaloneNativeMenuWindow" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + width="300" + height="300" + onload="onLoad();" + title="nsIStandaloneNativeMenu Test"> + + <command id="cmd_FooItem0" oncommand="gExecutedCommandID = 'cmd_FooItem0';"/> + <command id="cmd_FooItem1" oncommand="gExecutedCommandID = 'cmd_FooItem1';"/> + <command id="cmd_BarItem0" oncommand="gExecutedCommandID = 'cmd_BarItem0';"/> + <command id="cmd_BarItem1" oncommand="gExecutedCommandID = 'cmd_BarItem1';"/> + <command id="cmd_NewItem0" oncommand="gExecutedCommandID = 'cmd_NewItem0';"/> + <command id="cmd_NewItem1" oncommand="gExecutedCommandID = 'cmd_NewItem1';"/> + <command id="cmd_NewItem2" oncommand="gExecutedCommandID = 'cmd_NewItem2';"/> + <command id="cmd_NewItem3" oncommand="gExecutedCommandID = 'cmd_NewItem3';"/> + <command id="cmd_NewItem4" oncommand="gExecutedCommandID = 'cmd_NewItem4';"/> + <command id="cmd_NewItem5" oncommand="gExecutedCommandID = 'cmd_NewItem5';"/> + + <!-- We do not modify any menus or menu items defined here in testing. These + serve as a baseline structure for us to test after other modifications. + We add children to the menubar defined here and test by modifying those + children. --> + <popupset> + <menupopup id="standalonenativemenu"> + <menu id="foo" label="Foo"> + <menupopup> + <menuitem label="FooItem0" command="cmd_FooItem0"/> + <menuitem label="FooItem1" command="cmd_FooItem1"/> + <menuseparator/> + <menu label="Bar"> + <menupopup> + <menuitem label="BarItem0" command="cmd_BarItem0"/> + <menuitem label="BarItem1" command="cmd_BarItem1"/> + </menupopup> + </menu> + </menupopup> + </menu> + </menupopup> + </popupset> + + <script type="application/javascript"><![CDATA[ + + function ok(condition, message) { + window.arguments[0].SimpleTest.ok(condition, message); + } + + function is(a, b, message) { + window.arguments[0].SimpleTest.is(a, b, message); + } + + function isnot(a, b, message) { + window.arguments[0].SimpleTest.isnot(a, b, message); + } + + function todo(condition, message) { + window.arguments[0].SimpleTest.todo(condition, message); + } + + function todo_is(a, b, message) { + window.arguments[0].SimpleTest.todo_is(a, b, message); + } + + function todo_isnot(a, b, message) { + window.arguments[0].SimpleTest.todo_isnot(a, b, message); + } + + function onTestsFinished() { + window.close(); + window.arguments[0].SimpleTest.finish(); + } + + var gExecutedCommandID; + + // returns the executed command ID, or the empty string if nothing was executed + function activateItem(menu, location) { + try { + gExecutedCommandID = ""; + menu.menuWillOpen(); + menu.activateNativeMenuItemAt(location); + return gExecutedCommandID; + } + catch (e) { + // activateNativeMenuItemAt throws an exception if the item was not found + dump(e + "\n"); + return ""; + } + } + + function testItem(menu, location, targetID) { + var activatedCommandID = activateItem(menu, location); + return activatedCommandID != "" && activatedCommandID == targetID; + } + + var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + + function createXULMenuPopup() { + return document.createElementNS(XUL_NS, "menupopup"); + } + + function createXULMenu(aLabel) { + var item = document.createElementNS(XUL_NS, "menu"); + item.setAttribute("label", aLabel); + return item; + } + + function createXULMenuItem(aLabel, aCommandId) { + var item = document.createElementNS(XUL_NS, "menuitem"); + item.setAttribute("label", aLabel); + item.setAttribute("command", aCommandId); + return item; + } + + function runBaseMenuTests(menu) { + menu.forceUpdateNativeMenuAt("0|3"); + return testItem(menu, "0|0", "cmd_FooItem0") && + testItem(menu, "0|1", "cmd_FooItem1") && + testItem(menu, "0|3|0", "cmd_BarItem0") && + testItem(menu, "0|3|1", "cmd_BarItem1"); + } + + function createStandaloneNativeMenu(menuNode) { + try { + let menu = Cc["@mozilla.org/widget/standalonenativemenu;1"].createInstance(Ci.nsIStandaloneNativeMenu); + menu.init(menuNode); + return menu; + } catch (e) { + ok(false, "Failed creating nsIStandaloneNativeMenu instance"); + throw e; + } + } + + function runDetachedMenuTests(addMenupopupBeforeCreatingSNM) { + let menu = createXULMenu("Detached menu"); + menu.setAttribute("image", 'data:image/svg+xml,<svg%20xmlns="http://www.w3.org/2000/svg"%20width="32"%20height="32"><circle%20cx="16"%20cy="16"%20r="16"/></svg>'); + let menupopup = createXULMenuPopup(); + + let popupShowingFired = false; + let itemActivated = false; + + menupopup.addEventListener("popupshowing", function (e) { + popupShowingFired = true; + + let menuitem = document.createElementNS(XUL_NS, "menuitem"); + menuitem.setAttribute("label", "detached menu item"); + /* eslint-disable-next-line no-shadow */ + menuitem.addEventListener("command", function (e) { + itemActivated = true; + }) + menupopup.appendChild(menuitem); + }) + + // It shouldn't make a difference whether the menupopup is added to the + // menu element before or after we create the nsIStandaloneNativeMenu + // instance with it. We test both orders by calling this function twice + // with different values for addMenupopupBeforeCreatingSNM. + + var menuSNM = null; // the nsIStandaloneNativeMenu object for "menu" + if (addMenupopupBeforeCreatingSNM) { + menu.appendChild(menupopup); + menuSNM = createStandaloneNativeMenu(menu); + } else { + menuSNM = createStandaloneNativeMenu(menu); + menu.appendChild(menupopup); + } + + try { + ok(!popupShowingFired, "popupshowing shouldn't have fired before our call to menuWillOpen()"); + menuSNM.menuWillOpen(); + ok(popupShowingFired, "calling menuWillOpen() should have notified our popupshowing listener"); + + ok(!itemActivated, "our dynamically-added menuitem shouldn't have been activated yet"); + menuSNM.activateNativeMenuItemAt("0"); + ok(itemActivated, "the new menu item should have been activated now"); + } catch (ex) { + ok(false, "dynamic menu test failed: " + ex); + } + } + + function onLoad() { + var _delayedOnLoad = function() { + try { + + var menuNode = document.getElementById("standalonenativemenu"); + var menu = createStandaloneNativeMenu(menuNode); + + // First let's run the base menu tests. + ok(runBaseMenuTests(menu), "base tests #1"); + + // Set up some nodes that we'll use. + var newMenu0 = createXULMenu("NewMenu0"); + var newMenu1 = createXULMenu("NewMenu1"); + var newMenuPopup0 = createXULMenuPopup(); + var newMenuPopup1 = createXULMenuPopup(); + var newMenuItem0 = createXULMenuItem("NewMenuItem0", "cmd_NewItem0"); + var newMenuItem1 = createXULMenuItem("NewMenuItem1", "cmd_NewItem1"); + var newMenuItem2 = createXULMenuItem("NewMenuItem2", "cmd_NewItem2"); + var newMenuItem3 = createXULMenuItem("NewMenuItem3", "cmd_NewItem3"); + var newMenuItem4 = createXULMenuItem("NewMenuItem4", "cmd_NewItem4"); + var newMenuItem5 = createXULMenuItem("NewMenuItem5", "cmd_NewItem5"); + + // Create another submenu with hierarchy via DOM manipulation. + // ****************** + // * Foo * NewMenu0 * <- Menu bar + // ****************** + // **************** + // * NewMenuItem0 * <- NewMenu0 submenu + // **************** + // * NewMenuItem1 * + // **************** + // * NewMenuItem2 * + // ******************************* + // * NewMenu1 > * NewMenuItem3 * <- NewMenu1 submenu + // ******************************* + // * NewMenuItem4 * + // **************** + // * NewMenuItem5 * + // **************** + newMenu0.appendChild(newMenuPopup0); + newMenuPopup0.appendChild(newMenuItem0); + newMenuPopup0.appendChild(newMenuItem1); + newMenuPopup0.appendChild(newMenuItem2); + newMenuPopup0.appendChild(newMenu1); + newMenu1.appendChild(newMenuPopup1); + newMenuPopup1.appendChild(newMenuItem3); + newMenuPopup1.appendChild(newMenuItem4); + newMenuPopup1.appendChild(newMenuItem5); + //XXX - we have to append the menu to the top-level of the menu bar + // only after constructing it. If we append before construction, it is + // invalid because it has no children and we don't validate it if we add + // children later. + menuNode.appendChild(newMenu0); + menu.forceUpdateNativeMenuAt("1|3"); + // Run basic tests again. + ok(runBaseMenuTests(menu), "base tests #2"); + + // Error strings. + var sa = "Command handler(s) should have activated"; + var sna = "Command handler(s) should not have activated"; + + // Test middle items. + is(activateItem(menu, "1|1"), "cmd_NewItem1", "#1:" + sa); + is(activateItem(menu, "1|3|1"), "cmd_NewItem4", "#2:" + sa); + + // Hide newMenu0. + newMenu0.setAttribute("hidden", "true"); + ok(runBaseMenuTests(menu), "base tests #3: " + sa); // the base menu should still be unhidden + is(activateItem(menu, "1|0"), "", "#3:" + sna); + is(activateItem(menu, "1|1"), "", "#4:" + sna); + is(activateItem(menu, "1|2"), "", "#5:" + sna); + is(activateItem(menu, "1|3|0"), "", "#6:" + sna); + is(activateItem(menu, "1|3|1"), "", "#7:" + sna); + is(activateItem(menu, "1|3|2"), "", "#8:" + sna); + + // Show newMenu0. + newMenu0.setAttribute("hidden", "false"); + menu.forceUpdateNativeMenuAt("1|3"); + ok(runBaseMenuTests(menu), "base tests #4:" + sa); + is(activateItem(menu, "1|0"), "cmd_NewItem0", "#9:" + sa); + is(activateItem(menu, "1|1"), "cmd_NewItem1", "#10:" + sa); + is(activateItem(menu, "1|2"), "cmd_NewItem2", "#11:" + sa); + is(activateItem(menu, "1|3|0"), "cmd_NewItem3", "#12:" + sa); + is(activateItem(menu, "1|3|1"), "cmd_NewItem4", "#13:" + sa); + is(activateItem(menu, "1|3|2"), "cmd_NewItem5", "#14:" + sa); + + // Hide items. + newMenuItem1.setAttribute("hidden", "true"); + newMenuItem4.setAttribute("hidden", "true"); + menu.forceUpdateNativeMenuAt("1|2"); + ok(runBaseMenuTests(menu), "base tests #5:" + sa); + is(activateItem(menu, "1|0"), "cmd_NewItem0", "#15:" + sa); + is(activateItem(menu, "1|1"), "cmd_NewItem2", "#16:" + sa); + is(activateItem(menu, "1|2"), "", "#17:" + sna); + is(activateItem(menu, "1|2|0"), "cmd_NewItem3", "#18:" + sa); + is(activateItem(menu, "1|2|1"), "cmd_NewItem5", "#19:" + sa); + is(activateItem(menu, "1|2|2"), "", "#20:" + sna); + + // Show items. + newMenuItem1.setAttribute("hidden", "false"); + newMenuItem4.setAttribute("hidden", "false"); + //forceUpdateNativeMenuAt("1|3"); + ok(runBaseMenuTests(menu), "base tests #6:" + sa); + is(activateItem(menu, "1|0"), "cmd_NewItem0", "#21:" + sa); + is(activateItem(menu, "1|1"), "cmd_NewItem1", "#22:" + sa); + is(activateItem(menu, "1|2"), "cmd_NewItem2", "#23:" + sa); + is(activateItem(menu, "1|3|0"), "cmd_NewItem3", "#24:" + sa); + is(activateItem(menu, "1|3|1"), "cmd_NewItem4", "#25:" + sa); + is(activateItem(menu, "1|3|2"), "cmd_NewItem5", "#26:" + sa); + + // At this point in the tests the state of the menus has been returned + // to the originally diagramed state. + + // Remove menu. + menuNode.removeChild(newMenu0); + ok(runBaseMenuTests(menu), "base tests #7:" + sa); + is(activateItem(menu, "1|0"), "", "#27:" + sna); + is(activateItem(menu, "1|1"), "", "#28:" + sna); + is(activateItem(menu, "1|2"), "", "#29:" + sna); + is(activateItem(menu, "1|3|0"), "", "#30:" + sna); + is(activateItem(menu, "1|3|1"), "", "#31:" + sna); + is(activateItem(menu, "1|3|2"), "", "#32:" + sna); + // return state to original diagramed state + menuNode.appendChild(newMenu0); + + // The following is based on a similar test bug 447042 from the native + // menu bar test: Make sure that adding a menu node with no children + // to the menu bar and then adding another menu node with children works. + // In the menubar, root menus with no children are skipped - they're not + // visible in the menubar. + // Regular menus currently treat submenus without children differently: + // submenus without children *are* visible. + // We may want to change this in the future. + // After the mutation below we have the following root menu content: + // - [0] Foo (with submenu) + // - [1] tmpMenu0 (with empty submenu) + // - [2] NewMenu0 (with submenu) + // Since the empty tmpMenu0 item is not skipped, NewMenu0 has index 2, + // so we use "2|..." below, rather than the "1|..." that's used in the + // menubar test. + var tmpMenu0 = createXULMenu("tmpMenu0"); + menuNode.removeChild(newMenu0); + menuNode.appendChild(tmpMenu0); + menuNode.appendChild(newMenu0); + menu.forceUpdateNativeMenuAt("2|3"); + ok(runBaseMenuTests(menu), "base tests #8"); + is(activateItem(menu, "2|0"), "cmd_NewItem0", "#33:" + sa); + is(activateItem(menu, "2|1"), "cmd_NewItem1", "#34:" + sa); + is(activateItem(menu, "2|2"), "cmd_NewItem2", "#35:" + sa); + is(activateItem(menu, "2|3|0"), "cmd_NewItem3", "#36:" + sa); + is(activateItem(menu, "2|3|1"), "cmd_NewItem4", "#37:" + sa); + is(activateItem(menu, "2|3|2"), "cmd_NewItem5", "#38:" + sa); + // return state to original diagramed state + menuNode.removeChild(tmpMenu0); + + // This test is basically a crash test for bug 433858. + newMenuItem1.setAttribute("hidden", "true"); + newMenuItem2.setAttribute("hidden", "true"); + newMenu1.setAttribute("hidden", "true"); + menu.forceUpdateNativeMenuAt("1"); + newMenuItem1.setAttribute("hidden", "false"); + newMenuItem2.setAttribute("hidden", "false"); + newMenu1.setAttribute("hidden", "false"); + menu.forceUpdateNativeMenuAt("1"); + + // Check that "path components" which are out-of-range are not ignored. + // There are only two menu items in the root menu (with index 0 and 1), + // so index 2 is out of range. + is(activateItem(menu, "2|1|0"), "", "#39:" + sna); + + // Check that hiding and then un-hiding the root menu doesn't result in + // a cyclic native menu structure. + menuNode.setAttribute("collapsed", "true"); + menuNode.removeAttribute("collapsed"); + ok(runBaseMenuTests(menu), "base tests #9"); + + // Run tests where the menu nodes are not in the document's node tree. + runDetachedMenuTests(false); + runDetachedMenuTests(true); + + } catch (e) { + ok(false, "Caught an exception: " + e); + } finally { + onTestsFinished(); + } + } + + setTimeout(_delayedOnLoad, 1000); + } + + ]]></script> +</window> |