michael@0: var lastElement; michael@0: michael@0: function openContextMenuFor(element, shiftkey, waitForSpellCheck) { michael@0: // Context menu should be closed before we open it again. michael@0: is(SpecialPowers.wrap(contextMenu).state, "closed", "checking if popup is closed"); michael@0: michael@0: if (lastElement) michael@0: lastElement.blur(); michael@0: element.focus(); michael@0: michael@0: // Some elements need time to focus and spellcheck before any tests are michael@0: // run on them. michael@0: function actuallyOpenContextMenuFor() { michael@0: lastElement = element; michael@0: var eventDetails = { type : "contextmenu", button : 2, shiftKey : shiftkey }; michael@0: synthesizeMouse(element, 2, 2, eventDetails, element.ownerDocument.defaultView); michael@0: } michael@0: michael@0: if (waitForSpellCheck) michael@0: onSpellCheck(element, actuallyOpenContextMenuFor); michael@0: else michael@0: actuallyOpenContextMenuFor(); michael@0: } michael@0: michael@0: function closeContextMenu() { michael@0: contextMenu.hidePopup(); michael@0: } michael@0: michael@0: function getVisibleMenuItems(aMenu, aData) { michael@0: var items = []; michael@0: var accessKeys = {}; michael@0: for (var i = 0; i < aMenu.childNodes.length; i++) { michael@0: var item = aMenu.childNodes[i]; michael@0: if (item.hidden) michael@0: continue; michael@0: michael@0: var key = item.accessKey; michael@0: if (key) michael@0: key = key.toLowerCase(); michael@0: michael@0: var isGenerated = item.hasAttribute("generateditemid"); michael@0: michael@0: if (item.nodeName == "menuitem") { michael@0: var isSpellSuggestion = item.className == "spell-suggestion"; michael@0: if (isSpellSuggestion) { michael@0: is(item.id, "", "child menuitem #" + i + " is a spelling suggestion"); michael@0: } else if (isGenerated) { michael@0: is(item.id, "", "child menuitem #" + i + " is a generated item"); michael@0: } else { michael@0: ok(item.id, "child menuitem #" + i + " has an ID"); michael@0: } michael@0: var label = item.getAttribute("label"); michael@0: ok(label.length, "menuitem " + item.id + " has a label"); michael@0: if (isSpellSuggestion) { michael@0: is(key, "", "Spell suggestions shouldn't have an access key"); michael@0: items.push("*" + label); michael@0: } else if (isGenerated) { michael@0: items.push("+" + label); michael@0: } else if (item.id.indexOf("spell-check-dictionary-") != 0 && michael@0: item.id != "spell-no-suggestions" && michael@0: item.id != "spell-add-dictionaries-main") { michael@0: ok(key, "menuitem " + item.id + " has an access key"); michael@0: if (accessKeys[key]) michael@0: ok(false, "menuitem " + item.id + " has same accesskey as " + accessKeys[key]); michael@0: else michael@0: accessKeys[key] = item.id; michael@0: } michael@0: if (!isSpellSuggestion && !isGenerated) { michael@0: items.push(item.id); michael@0: } michael@0: if (isGenerated) { michael@0: var p = {}; michael@0: p.type = item.getAttribute("type"); michael@0: p.icon = item.getAttribute("image"); michael@0: p.checked = item.hasAttribute("checked"); michael@0: p.disabled = item.hasAttribute("disabled"); michael@0: items.push(p); michael@0: } else { michael@0: items.push(!item.disabled); michael@0: } michael@0: } else if (item.nodeName == "menuseparator") { michael@0: ok(true, "--- seperator id is " + item.id); michael@0: items.push("---"); michael@0: items.push(null); michael@0: } else if (item.nodeName == "menu") { michael@0: if (isGenerated) { michael@0: item.id = "generated-submenu-" + aData.generatedSubmenuId++; michael@0: } michael@0: ok(item.id, "child menu #" + i + " has an ID"); michael@0: if (!isGenerated) { michael@0: ok(key, "menu has an access key"); michael@0: if (accessKeys[key]) michael@0: ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]); michael@0: else michael@0: accessKeys[key] = item.id; michael@0: } michael@0: items.push(item.id); michael@0: items.push(!item.disabled); michael@0: // Add a dummy item to that the indexes in checkMenu are the same michael@0: // for expectedItems and actualItems. michael@0: items.push([]); michael@0: items.push(null); michael@0: } else { michael@0: ok(false, "child #" + i + " of menu ID " + aMenu.id + michael@0: " has an unknown type (" + item.nodeName + ")"); michael@0: } michael@0: } michael@0: return items; michael@0: } michael@0: michael@0: function checkContextMenu(expectedItems) { michael@0: is(contextMenu.state, "open", "checking if popup is open"); michael@0: var data = { generatedSubmenuId: 1 }; michael@0: checkMenu(contextMenu, expectedItems, data); michael@0: } michael@0: michael@0: /* michael@0: * checkMenu - checks to see if the specified contains the michael@0: * expected items and state. michael@0: * expectedItems is a array of (1) item IDs and (2) a boolean specifying if michael@0: * the item is enabled or not (or null to ignore it). Submenus can be checked michael@0: * by providing a nested array entry after the expected ID. michael@0: * For example: ["blah", true, // item enabled michael@0: * "submenu", null, // submenu michael@0: * ["sub1", true, // submenu contents michael@0: * "sub2", false], null, // submenu contents michael@0: * "lol", false] // item disabled michael@0: * michael@0: */ michael@0: function checkMenu(menu, expectedItems, data) { michael@0: var actualItems = getVisibleMenuItems(menu, data); michael@0: //ok(false, "Items are: " + actualItems); michael@0: for (var i = 0; i < expectedItems.length; i+=2) { michael@0: var actualItem = actualItems[i]; michael@0: var actualEnabled = actualItems[i + 1]; michael@0: var expectedItem = expectedItems[i]; michael@0: var expectedEnabled = expectedItems[i + 1]; michael@0: if (expectedItem instanceof Array) { michael@0: ok(true, "Checking submenu..."); michael@0: var menuID = expectedItems[i - 2]; // The last item was the menu ID. michael@0: var submenu = menu.getElementsByAttribute("id", menuID)[0]; michael@0: ok(submenu, "got a submenu element of id='" + menuID + "'"); michael@0: if (submenu) { michael@0: is(submenu.nodeName, "menu", "submenu element of id='" + menuID + michael@0: "' has expected nodeName"); michael@0: checkMenu(submenu.menupopup, expectedItem, data); michael@0: } michael@0: } else { michael@0: is(actualItem, expectedItem, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") name"); michael@0: michael@0: if (typeof expectedEnabled == "object" && expectedEnabled != null || michael@0: typeof actualEnabled == "object" && actualEnabled != null) { michael@0: michael@0: ok(!(actualEnabled == null), "actualEnabled is not null"); michael@0: ok(!(expectedEnabled == null), "expectedEnabled is not null"); michael@0: is(typeof actualEnabled, typeof expectedEnabled, "checking types"); michael@0: michael@0: if (typeof actualEnabled != typeof expectedEnabled || michael@0: actualEnabled == null || expectedEnabled == null) michael@0: continue; michael@0: michael@0: is(actualEnabled.type, expectedEnabled.type, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") type attr value"); michael@0: var icon = actualEnabled.icon; michael@0: if (icon) { michael@0: var tmp = ""; michael@0: var j = icon.length - 1; michael@0: while (j && icon[j] != "/") { michael@0: tmp = icon[j--] + tmp; michael@0: } michael@0: icon = tmp; michael@0: } michael@0: is(icon, expectedEnabled.icon, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") icon attr value"); michael@0: is(actualEnabled.checked, expectedEnabled.checked, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") has checked attr"); michael@0: is(actualEnabled.disabled, expectedEnabled.disabled, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") has disabled attr"); michael@0: } else if (expectedEnabled != null) michael@0: is(actualEnabled, expectedEnabled, michael@0: "checking item #" + i/2 + " (" + expectedItem + ") enabled state"); michael@0: } michael@0: } michael@0: // Could find unexpected extra items at the end... michael@0: is(actualItems.length, expectedItems.length, "checking expected number of menu entries"); michael@0: }