mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 617528 Part 2 - Core implementation r=smaug
--HG-- rename : content/html/content/test/test_bug418756.html => content/html/content/test/test_checked.html
This commit is contained in:
parent
07b3728d4c
commit
a298beaa2e
@ -36,6 +36,7 @@
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
<menuseparator id="page-menu-separator"/>
|
||||
<menuitem id="spell-no-suggestions"
|
||||
disabled="true"
|
||||
label="&spellNoSuggestions.label;"/>
|
||||
|
@ -216,6 +216,12 @@ XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
|
||||
"nsICrashReporter");
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
|
||||
return new tmp.PageMenu();
|
||||
});
|
||||
|
||||
/**
|
||||
* We can avoid adding multiple load event listeners and save some time by adding
|
||||
* one listener that calls all real handlers.
|
||||
|
@ -273,10 +273,10 @@
|
||||
oncommand="BrowserFullScreen();"/>
|
||||
</menupopup>
|
||||
|
||||
<menupopup id="contentAreaContextMenu"
|
||||
<menupopup id="contentAreaContextMenu" pagemenu="start"
|
||||
onpopupshowing="if (event.target != this)
|
||||
return true;
|
||||
gContextMenu = new nsContextMenu(this, gBrowser);
|
||||
gContextMenu = new nsContextMenu(this, gBrowser, event.shiftKey);
|
||||
if (gContextMenu.shouldDisplay)
|
||||
updateEditUIVisibility();
|
||||
return gContextMenu.shouldDisplay;"
|
||||
|
@ -61,14 +61,14 @@
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
function nsContextMenu(aXulMenu, aBrowser) {
|
||||
function nsContextMenu(aXulMenu, aBrowser, aIsShift) {
|
||||
this.shouldDisplay = true;
|
||||
this.initMenu(aBrowser);
|
||||
this.initMenu(aBrowser, aXulMenu, aIsShift);
|
||||
}
|
||||
|
||||
// Prototype for nsContextMenu "class."
|
||||
nsContextMenu.prototype = {
|
||||
initMenu: function CM_initMenu(aBrowser) {
|
||||
initMenu: function CM_initMenu(aBrowser, aXulMenu, aIsShift) {
|
||||
// Get contextual info.
|
||||
this.setTarget(document.popupNode, document.popupRangeParent,
|
||||
document.popupRangeOffset);
|
||||
@ -76,6 +76,12 @@ nsContextMenu.prototype = {
|
||||
return;
|
||||
|
||||
this.browser = aBrowser;
|
||||
|
||||
this.hasPageMenu = false;
|
||||
if (!aIsShift) {
|
||||
this.hasPageMenu = PageMenu.init(this.target, aXulMenu);
|
||||
}
|
||||
|
||||
this.isFrameImage = document.getElementById("isFrameImage");
|
||||
this.ellipsis = "\u2026";
|
||||
try {
|
||||
@ -90,6 +96,7 @@ nsContextMenu.prototype = {
|
||||
},
|
||||
|
||||
initItems: function CM_initItems() {
|
||||
this.initPageMenuSeparator();
|
||||
this.initOpenItems();
|
||||
this.initNavigationItems();
|
||||
this.initViewItems();
|
||||
@ -100,6 +107,10 @@ nsContextMenu.prototype = {
|
||||
this.initMediaPlayerItems();
|
||||
},
|
||||
|
||||
initPageMenuSeparator: function CM_initPageMenuSeparator() {
|
||||
this.showItem("page-menu-separator", this.hasPageMenu);
|
||||
},
|
||||
|
||||
initOpenItems: function CM_initOpenItems() {
|
||||
var isMailtoInternal = false;
|
||||
if (this.onMailtoLink) {
|
||||
|
@ -21,6 +21,40 @@ Browser context menu subtest.
|
||||
<textarea id="test-textarea">chssseesbbbie</textarea> <!-- a weird word which generates only one suggestion -->
|
||||
<div id="test-contenteditable" contenteditable="true">chssseefsbbbie</div> <!-- a more weird word which generates no suggestions -->
|
||||
<input id="test-input-spellcheck" type="text" spellcheck="true" autofocus value="prodkjfgigrty"> <!-- this one also generates one suggestion -->
|
||||
<div contextmenu="myMenu">
|
||||
<p id="test-pagemenu" hopeless="true">I've got a context menu!</p>
|
||||
<menu id="myMenu" type="context">
|
||||
<menuitem label="Plain item" onclick="document.getElementById('test-pagemenu').removeAttribute('hopeless');"></menuitem>
|
||||
<menuitem label="Disabled item" disabled></menuitem>
|
||||
<menu>
|
||||
<menuitem type="checkbox" label="Checkbox" checked></menuitem>
|
||||
</menu>
|
||||
<menu>
|
||||
<menuitem type="radio" label="Radio1" checked></menuitem>
|
||||
<menuitem type="radio" label="Radio2"></menuitem>
|
||||
<menuitem type="radio" label="Radio3"></menuitem>
|
||||
</menu>
|
||||
<menu>
|
||||
<menuitem label="Item w/ icon" icon="favicon.ico"></menuitem>
|
||||
<menuitem label="Item w/ bad icon" icon="data://www.mozilla.org/favicon.ico"></menuitem>
|
||||
</menu>
|
||||
<menu label="Submenu">
|
||||
<menuitem type="radio" label="Radio1" radiogroup="rg"></menuitem>
|
||||
<menuitem type="radio" label="Radio2" checked radiogroup="rg"></menuitem>
|
||||
<menuitem type="radio" label="Radio3" radiogroup="rg"></menuitem>
|
||||
<menu>
|
||||
<menuitem type="checkbox" label="Checkbox"></menuitem>
|
||||
</menu>
|
||||
</menu>
|
||||
<menu hidden>
|
||||
<menuitem label="Bogus item"></menuitem>
|
||||
</menu>
|
||||
<menu>
|
||||
</menu>
|
||||
<menuitem label="Hidden item" hidden></menuitem>
|
||||
<menuitem></menuitem>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -24,11 +24,11 @@ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
function openContextMenuFor(element) {
|
||||
function openContextMenuFor(element, shiftkey) {
|
||||
// Context menu should be closed before we open it again.
|
||||
is(contextMenu.state, "closed", "checking if popup is closed");
|
||||
|
||||
var eventDetails = { type : "contextmenu", button : 2 };
|
||||
var eventDetails = { type : "contextmenu", button : 2, shiftKey : shiftkey };
|
||||
synthesizeMouse(element, 2, 2, eventDetails, element.ownerDocument.defaultView);
|
||||
}
|
||||
|
||||
@ -50,7 +50,15 @@ function executeCopyCommand(command, expectedValue)
|
||||
is(input.value, expectedValue, "paste for command " + command);
|
||||
}
|
||||
|
||||
function getVisibleMenuItems(aMenu) {
|
||||
function invokeItemAction(ident)
|
||||
{
|
||||
var item = contextMenu.getElementsByAttribute("ident", ident)[0];
|
||||
ok(item, "Got generated XUL menu item");
|
||||
item.doCommand();
|
||||
is(pagemenu.hasAttribute("hopeless"), false, "attribute got removed");
|
||||
}
|
||||
|
||||
function getVisibleMenuItems(aMenu, aData) {
|
||||
var items = [];
|
||||
var accessKeys = {};
|
||||
for (var i = 0; i < aMenu.childNodes.length; i++) {
|
||||
@ -62,10 +70,14 @@ function getVisibleMenuItems(aMenu) {
|
||||
if (key)
|
||||
key = key.toLowerCase();
|
||||
|
||||
var isGenerated = item.hasAttribute("generated");
|
||||
|
||||
if (item.nodeName == "menuitem") {
|
||||
var isSpellSuggestion = item.className == "spell-suggestion";
|
||||
if (isSpellSuggestion) {
|
||||
is(item.id, "", "child menuitem #" + i + " is a spelling suggestion");
|
||||
} else if (isGenerated) {
|
||||
is(item.id, "", "child menuitem #" + i + " is a generated item");
|
||||
} else {
|
||||
ok(item.id, "child menuitem #" + i + " has an ID");
|
||||
}
|
||||
@ -74,6 +86,8 @@ function getVisibleMenuItems(aMenu) {
|
||||
if (isSpellSuggestion) {
|
||||
is(key, "", "Spell suggestions shouldn't have an access key");
|
||||
items.push("*" + label);
|
||||
} else if (isGenerated) {
|
||||
items.push("+" + label);
|
||||
} else if (item.id.indexOf("spell-check-dictionary-") != 0 &&
|
||||
item.id != "spell-no-suggestions") {
|
||||
ok(key, "menuitem " + item.id + " has an access key");
|
||||
@ -82,21 +96,35 @@ function getVisibleMenuItems(aMenu) {
|
||||
else
|
||||
accessKeys[key] = item.id;
|
||||
}
|
||||
if (!isSpellSuggestion) {
|
||||
if (!isSpellSuggestion && !isGenerated) {
|
||||
items.push(item.id);
|
||||
}
|
||||
items.push(!item.disabled);
|
||||
if (isGenerated) {
|
||||
var p = {};
|
||||
p.type = item.getAttribute("type");
|
||||
p.icon = item.getAttribute("image");
|
||||
p.checked = item.hasAttribute("checked");
|
||||
p.disabled = item.hasAttribute("disabled");
|
||||
items.push(p);
|
||||
} else {
|
||||
items.push(!item.disabled);
|
||||
}
|
||||
} else if (item.nodeName == "menuseparator") {
|
||||
ok(true, "--- seperator id is " + item.id);
|
||||
items.push("---");
|
||||
items.push(null);
|
||||
} else if (item.nodeName == "menu") {
|
||||
if (isGenerated) {
|
||||
item.id = "generated-submenu-" + aData.generatedSubmenuId++;
|
||||
}
|
||||
ok(item.id, "child menu #" + i + " has an ID");
|
||||
ok(key, "menu has an access key");
|
||||
if (accessKeys[key])
|
||||
ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]);
|
||||
else
|
||||
accessKeys[key] = item.id;
|
||||
if (!isGenerated) {
|
||||
ok(key, "menu has an access key");
|
||||
if (accessKeys[key])
|
||||
ok(false, "menu " + item.id + " has same accesskey as " + accessKeys[key]);
|
||||
else
|
||||
accessKeys[key] = item.id;
|
||||
}
|
||||
items.push(item.id);
|
||||
items.push(!item.disabled);
|
||||
// Add a dummy item to that the indexes in checkMenu are the same
|
||||
@ -113,7 +141,8 @@ function getVisibleMenuItems(aMenu) {
|
||||
|
||||
function checkContextMenu(expectedItems) {
|
||||
is(contextMenu.state, "open", "checking if popup is open");
|
||||
checkMenu(contextMenu, expectedItems);
|
||||
var data = { generatedSubmenuId: 1 };
|
||||
checkMenu(contextMenu, expectedItems, data);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -129,8 +158,8 @@ function checkContextMenu(expectedItems) {
|
||||
* "lol", false] // item disabled
|
||||
*
|
||||
*/
|
||||
function checkMenu(menu, expectedItems) {
|
||||
var actualItems = getVisibleMenuItems(menu);
|
||||
function checkMenu(menu, expectedItems, data) {
|
||||
var actualItems = getVisibleMenuItems(menu, data);
|
||||
//ok(false, "Items are: " + actualItems);
|
||||
for (var i = 0; i < expectedItems.length; i+=2) {
|
||||
var actualItem = actualItems[i];
|
||||
@ -142,11 +171,40 @@ function checkMenu(menu, expectedItems) {
|
||||
var menuID = expectedItems[i - 2]; // The last item was the menu ID.
|
||||
var submenu = menu.getElementsByAttribute("id", menuID)[0];
|
||||
ok(submenu && submenu.nodeName == "menu", "got expected submenu element");
|
||||
checkMenu(submenu.menupopup, expectedItem);
|
||||
checkMenu(submenu.menupopup, expectedItem, data);
|
||||
} else {
|
||||
is(actualItem, expectedItem,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") name");
|
||||
if (expectedEnabled != null)
|
||||
|
||||
if (typeof expectedEnabled == "object" && expectedEnabled != null ||
|
||||
typeof actualEnabled == "object" && actualEnabled != null) {
|
||||
|
||||
ok(!(actualEnabled == null), "actualEnabled is not null");
|
||||
ok(!(expectedEnabled == null), "expectedEnabled is not null");
|
||||
is(typeof actualEnabled, typeof expectedEnabled, "checking types");
|
||||
|
||||
if (typeof actualEnabled != typeof expectedEnabled ||
|
||||
actualEnabled == null || expectedEnabled == null)
|
||||
continue;
|
||||
|
||||
is(actualEnabled.type, expectedEnabled.type,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") type attr value");
|
||||
var icon = actualEnabled.icon;
|
||||
if (icon) {
|
||||
var tmp = "";
|
||||
var j = icon.length - 1;
|
||||
while (j && icon[j] != "/") {
|
||||
tmp = icon[j--] + tmp;
|
||||
}
|
||||
icon = tmp;
|
||||
}
|
||||
is(icon, expectedEnabled.icon,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") icon attr value");
|
||||
is(actualEnabled.checked, expectedEnabled.checked,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") has checked attr");
|
||||
is(actualEnabled.disabled, expectedEnabled.disabled,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") has disabled attr");
|
||||
} else if (expectedEnabled != null)
|
||||
is(actualEnabled, expectedEnabled,
|
||||
"checking item #" + i/2 + " (" + expectedItem + ") enabled state");
|
||||
}
|
||||
@ -408,9 +466,70 @@ function runTest(testNum) {
|
||||
openContextMenuFor(link); // Invoke context menu for next test.
|
||||
break;
|
||||
|
||||
case 15:
|
||||
case 15:
|
||||
executeCopyCommand("cmd_copyLink", "http://mozilla.com/");
|
||||
closeContextMenu();
|
||||
openContextMenuFor(pagemenu); // Invoke context menu for next test.
|
||||
break;
|
||||
|
||||
case 16:
|
||||
// Context menu for element with assigned content context menu
|
||||
checkContextMenu(["+Plain item", {type: "", icon: "", checked: false, disabled: false},
|
||||
"+Disabled item", {type: "", icon: "", checked: false, disabled: true},
|
||||
"---", null,
|
||||
"+Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false},
|
||||
"---", null,
|
||||
"+Radio1", {type: "checkbox", icon: "", checked: true, disabled: false},
|
||||
"+Radio2", {type: "checkbox", icon: "", checked: false, disabled: false},
|
||||
"+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false},
|
||||
"---", null,
|
||||
"+Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false},
|
||||
"+Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false},
|
||||
"---", null,
|
||||
"generated-submenu-1", true,
|
||||
["+Radio1", {type: "checkbox", icon: "", checked: false, disabled: false},
|
||||
"+Radio2", {type: "checkbox", icon: "", checked: true, disabled: false},
|
||||
"+Radio3", {type: "checkbox", icon: "", checked: false, disabled: false},
|
||||
"---", null,
|
||||
"+Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}], null,
|
||||
"---", null,
|
||||
"context-back", false,
|
||||
"context-forward", false,
|
||||
"context-reload", true,
|
||||
"context-stop", false,
|
||||
"---", null,
|
||||
"context-bookmarkpage", true,
|
||||
"context-savepage", true,
|
||||
"context-sendpage", true,
|
||||
"---", null,
|
||||
"context-viewbgimage", false,
|
||||
"context-selectall", true,
|
||||
"---", null,
|
||||
"context-viewsource", true,
|
||||
"context-viewinfo", true]);
|
||||
|
||||
invokeItemAction("0");
|
||||
closeContextMenu();
|
||||
openContextMenuFor(pagemenu, true); // Invoke context menu for next test.
|
||||
break;
|
||||
|
||||
case 17:
|
||||
// Context menu for element with assigned content context menu
|
||||
// The shift key should bypass content context menu processing
|
||||
checkContextMenu(["context-back", false,
|
||||
"context-forward", false,
|
||||
"context-reload", true,
|
||||
"context-stop", false,
|
||||
"---", null,
|
||||
"context-bookmarkpage", true,
|
||||
"context-savepage", true,
|
||||
"context-sendpage", true,
|
||||
"---", null,
|
||||
"context-viewbgimage", false,
|
||||
"context-selectall", true,
|
||||
"---", null,
|
||||
"context-viewsource", true,
|
||||
"context-viewinfo", true]);
|
||||
|
||||
subwindow.close();
|
||||
SimpleTest.finish();
|
||||
@ -437,7 +556,7 @@ function runTest(testNum) {
|
||||
var testNum = 1;
|
||||
var subwindow, chromeWin, contextMenu;
|
||||
var text, link, mailto, input, img, canvas, video_ok, video_bad, video_bad2,
|
||||
iframe, textarea, contenteditable, inputspell;
|
||||
iframe, textarea, contenteditable, inputspell, pagemenu;
|
||||
|
||||
function startTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
@ -470,6 +589,7 @@ function startTest() {
|
||||
textarea = subwindow.document.getElementById("test-textarea");
|
||||
contenteditable = subwindow.document.getElementById("test-contenteditable");
|
||||
inputspell = subwindow.document.getElementById("test-input-spellcheck");
|
||||
pagemenu = subwindow.document.getElementById("test-pagemenu");
|
||||
|
||||
contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false);
|
||||
runTest(1);
|
||||
|
@ -80,10 +80,10 @@
|
||||
|
||||
<popupset id="mainPopupSet">
|
||||
<tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
|
||||
<menupopup id="contentAreaContextMenu"
|
||||
<menupopup id="contentAreaContextMenu" pagemenu="start"
|
||||
onpopupshowing="if (event.target != this)
|
||||
return true;
|
||||
gContextMenu = new nsContextMenu(this, getPanelBrowser());
|
||||
gContextMenu = new nsContextMenu(this, getPanelBrowser(), event.shiftKey);
|
||||
if (gContextMenu.shouldDisplay)
|
||||
document.popupNode = this.triggerNode;
|
||||
return gContextMenu.shouldDisplay;"
|
||||
|
@ -256,6 +256,7 @@
|
||||
@BINPATH@/components/xpcom_xpti.xpt
|
||||
@BINPATH@/components/xpconnect.xpt
|
||||
@BINPATH@/components/xulapp.xpt
|
||||
@BINPATH@/components/xul.xpt
|
||||
@BINPATH@/components/xuldoc.xpt
|
||||
@BINPATH@/components/xultmpl.xpt
|
||||
@BINPATH@/components/zipwriter.xpt
|
||||
|
@ -567,6 +567,7 @@ GK_ATOM(menuButton, "menu-button")
|
||||
GK_ATOM(menuitem, "menuitem")
|
||||
GK_ATOM(menulist, "menulist")
|
||||
GK_ATOM(menupopup, "menupopup")
|
||||
GK_ATOM(menuseparator, "menuseparator")
|
||||
GK_ATOM(message, "message")
|
||||
GK_ATOM(meta, "meta")
|
||||
GK_ATOM(meter, "meter")
|
||||
@ -723,6 +724,7 @@ GK_ATOM(onresize, "onresize")
|
||||
GK_ATOM(onscroll, "onscroll")
|
||||
GK_ATOM(onselect, "onselect")
|
||||
GK_ATOM(onset, "onset")
|
||||
GK_ATOM(onshow, "onshow")
|
||||
GK_ATOM(onsubmit, "onsubmit")
|
||||
GK_ATOM(ontext, "ontext")
|
||||
GK_ATOM(ontouchstart, "ontouchstart")
|
||||
|
@ -285,8 +285,10 @@ EVENT(select,
|
||||
NS_FORM_SELECTED,
|
||||
EventNameType_HTMLXUL,
|
||||
NS_EVENT)
|
||||
// Not supported yet
|
||||
// EVENT(show)
|
||||
EVENT(show,
|
||||
NS_SHOW_EVENT,
|
||||
EventNameType_HTML,
|
||||
NS_EVENT)
|
||||
EVENT(stalled,
|
||||
NS_STALLED,
|
||||
EventNameType_HTML,
|
||||
|
@ -77,7 +77,7 @@ static const char* const sEventNames[] = {
|
||||
"DOMAttrModified", "DOMCharacterDataModified",
|
||||
"DOMActivate", "DOMFocusIn", "DOMFocusOut",
|
||||
"pageshow", "pagehide", "DOMMouseScroll", "MozMousePixelScroll",
|
||||
"offline", "online", "copy", "cut", "paste", "open", "message",
|
||||
"offline", "online", "copy", "cut", "paste", "open", "message", "show",
|
||||
"SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
|
||||
"SVGZoom",
|
||||
#ifdef MOZ_SMIL
|
||||
@ -1257,6 +1257,8 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType)
|
||||
return sEventNames[eDOMEvents_open];
|
||||
case NS_MESSAGE:
|
||||
return sEventNames[eDOMEvents_message];
|
||||
case NS_SHOW_EVENT:
|
||||
return sEventNames[eDOMEvents_show];
|
||||
case NS_SVG_LOAD:
|
||||
return sEventNames[eDOMEvents_SVGLoad];
|
||||
case NS_SVG_UNLOAD:
|
||||
|
@ -138,6 +138,7 @@ public:
|
||||
eDOMEvents_paste,
|
||||
eDOMEvents_open,
|
||||
eDOMEvents_message,
|
||||
eDOMEvents_show,
|
||||
eDOMEvents_SVGLoad,
|
||||
eDOMEvents_SVGUnload,
|
||||
eDOMEvents_SVGAbort,
|
||||
|
@ -48,6 +48,8 @@ XPIDL_MODULE = content_html
|
||||
XPIDLSRCS = \
|
||||
nsIFormSubmitObserver.idl \
|
||||
nsIPhonetic.idl \
|
||||
nsIHTMLMenu.idl \
|
||||
nsIMenuBuilder.idl \
|
||||
$(NULL)
|
||||
|
||||
EXPORTS = \
|
||||
|
73
content/html/content/public/nsIHTMLMenu.idl
Normal file
73
content/html/content/public/nsIHTMLMenu.idl
Normal file
@ -0,0 +1,73 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIMenuBuilder;
|
||||
|
||||
/**
|
||||
* A private interface.
|
||||
* All methods throw NS_ERROR_DOM_SECURITY_ERR if the caller is not chrome.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(d3d068d8-e223-4228-ba39-4d6df21ba616)]
|
||||
interface nsIHTMLMenu : nsISupports
|
||||
{
|
||||
/**
|
||||
* Creates and dispatches a trusted event named "show".
|
||||
* The event is not cancelable and does not bubble.
|
||||
* See http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus
|
||||
*/
|
||||
void sendShowEvent();
|
||||
|
||||
/**
|
||||
* Creates a native menu builder. The builder type is dependent on menu type.
|
||||
* Currently, it returns nsXULContextMenuBuilder for context menus.
|
||||
* Toolbar menus are not yet supported (the method returns null).
|
||||
*/
|
||||
nsIMenuBuilder createBuilder();
|
||||
|
||||
/*
|
||||
* Builds a menu by iterating over menu children.
|
||||
* See http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#building-menus-and-toolbars
|
||||
* The caller can use a native builder by calling createBuilder() or provide
|
||||
* a custom builder that implements the nsIMenuBuilder interface.
|
||||
* A custom builder can be used for example to build native context menus
|
||||
* that are not defined using <menupopup>.
|
||||
*/
|
||||
void build(in nsIMenuBuilder aBuilder);
|
||||
|
||||
};
|
83
content/html/content/public/nsIMenuBuilder.idl
Normal file
83
content/html/content/public/nsIMenuBuilder.idl
Normal file
@ -0,0 +1,83 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMHTMLMenuItemElement;
|
||||
|
||||
/**
|
||||
* An interface used to construct native toolbar or context menus from <menu>
|
||||
*/
|
||||
|
||||
[scriptable, uuid(12724737-f7db-43b4-94ab-708a7b86e115)]
|
||||
interface nsIMenuBuilder : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Create the top level menu or a submenu. The implementation should create
|
||||
* a new context for this menu, so all subsequent methods will add new items
|
||||
* to this newly created menu.
|
||||
*/
|
||||
void openContainer(in DOMString aLabel);
|
||||
|
||||
/**
|
||||
* Add a new menu item. All menu item details can be obtained from
|
||||
* the element. This method is not called for hidden elements or elements
|
||||
* with no or empty label. The icon should be loaded only if aCanLoadIcon
|
||||
* is true.
|
||||
*/
|
||||
void addItemFor(in nsIDOMHTMLMenuItemElement aElement,
|
||||
in boolean aCanLoadIcon);
|
||||
|
||||
/**
|
||||
* Create a new separator.
|
||||
*/
|
||||
void addSeparator();
|
||||
|
||||
/**
|
||||
* Remove last added separator.
|
||||
* Sometimes it's needed to remove last added separator, otherwise it's not
|
||||
* possible to implement the postprocessing in one pass.
|
||||
* See http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#building-menus-and-toolbars
|
||||
*/
|
||||
void undoAddSeparator();
|
||||
|
||||
/**
|
||||
* Set the context to the parent menu.
|
||||
*/
|
||||
void closeContainer();
|
||||
|
||||
};
|
@ -82,6 +82,8 @@ CPPSRCS = \
|
||||
nsHTMLLegendElement.cpp \
|
||||
nsHTMLLinkElement.cpp \
|
||||
nsHTMLMapElement.cpp \
|
||||
nsHTMLMenuElement.cpp \
|
||||
nsHTMLMenuItemElement.cpp \
|
||||
nsHTMLMetaElement.cpp \
|
||||
nsHTMLModElement.cpp \
|
||||
nsHTMLObjectElement.cpp \
|
||||
@ -134,6 +136,7 @@ INCLUDES += \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(srcdir)/../../../xbl/src \
|
||||
-I$(srcdir)/../../../xul/content/src \
|
||||
-I$(srcdir)/../../../../layout/forms \
|
||||
-I$(srcdir)/../../../../layout/style \
|
||||
-I$(srcdir)/../../../../layout/tables \
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "nsIDOMAttr.h"
|
||||
#include "nsIDOMDocumentFragment.h"
|
||||
#include "nsIDOMNSHTMLElement.h"
|
||||
#include "nsIDOMHTMLMenuElement.h"
|
||||
#include "nsIDOMElementCSSInlineStyle.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
@ -115,6 +116,7 @@
|
||||
#include "nsITextControlElement.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsHTMLFieldSetElement.h"
|
||||
#include "nsHTMLMenuElement.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
@ -2451,6 +2453,28 @@ nsGenericHTMLElement::GetIsContentEditable(PRBool* aContentEditable)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu)
|
||||
{
|
||||
*aContextMenu = nsnull;
|
||||
|
||||
nsAutoString value;
|
||||
GetAttr(kNameSpaceID_None, nsGkAtoms::contextmenu, value);
|
||||
|
||||
if (value.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
nsRefPtr<nsHTMLMenuElement> element =
|
||||
nsHTMLMenuElement::FromContent(doc->GetElementById(value));
|
||||
element.forget(aContextMenu);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_INT_ATTR(nsGenericHTMLFrameElement, TabIndex, tabindex)
|
||||
|
@ -66,6 +66,7 @@ struct nsRect;
|
||||
struct nsSize;
|
||||
class nsHTMLFormElement;
|
||||
class nsIDOMDOMStringMap;
|
||||
class nsIDOMHTMLMenuElement;
|
||||
|
||||
typedef nsMappedAttributeElement nsGenericHTMLElementBase;
|
||||
|
||||
@ -161,6 +162,7 @@ public:
|
||||
nsresult GetDataset(nsIDOMDOMStringMap** aDataset);
|
||||
// Callback for destructor of of dataset to ensure to null out weak pointer.
|
||||
nsresult ClearDataset();
|
||||
nsresult GetContextMenu(nsIDOMHTMLMenuElement** aContextMenu);
|
||||
|
||||
// Implementation for nsIContent
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
@ -533,6 +535,11 @@ public:
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
|
||||
}
|
||||
|
||||
PRBool IsHidden() const
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::hidden);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Add/remove this element to the documents name cache
|
||||
@ -1471,22 +1478,22 @@ protected:
|
||||
NS_INTERFACE_TABLE_ENTRY(_class, _i10) \
|
||||
NS_OFFSET_AND_INTERFACE_TABLE_END
|
||||
|
||||
/* Use this macro to declare functions that forward the behavior of this interface to another object.
|
||||
This macro doesn't forward Focus or Click because sometimes elements will want to override them. */
|
||||
#define NS_FORWARD_NSIDOMHTMLELEMENT_NOFOCUSCLICK(_to) \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetId(nsAString & aId) { return _to GetId(aId); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetId(const nsAString & aId) { return _to SetId(aId); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetTitle(nsAString & aTitle) { return _to GetTitle(aTitle); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetTitle(const nsAString & aTitle) { return _to SetTitle(aTitle); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetLang(nsAString & aLang) { return _to GetLang(aLang); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetLang(const nsAString & aLang) { return _to SetLang(aLang); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetDir(nsAString & aDir) { return _to GetDir(aDir); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetDir(const nsAString & aDir) { return _to SetDir(aDir); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetClassName(nsAString & aClassName) { return _to GetClassName(aClassName); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetClassName(const nsAString & aClassName) { return _to SetClassName(aClassName); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetAccessKey(nsAString & aAccessKey) { return _to GetAccessKey(aAccessKey); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetAccessKey(const nsAString & aAccessKey) { return _to SetAccessKey(aAccessKey); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetAccessKeyLabel(nsAString & aLabel) { return _to GetAccessKeyLabel(aLabel); } \
|
||||
/* Use this macro to declare functions that forward the behavior of this interface to another object.
|
||||
This macro doesn't forward Focus or Click because sometimes elements will want to override them. */
|
||||
#define NS_FORWARD_NSIDOMHTMLELEMENT_NOFOCUSCLICK(_to) \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetId(nsAString & aId) { return _to GetId(aId); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetId(const nsAString & aId) { return _to SetId(aId); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetTitle(nsAString & aTitle) { return _to GetTitle(aTitle); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetTitle(const nsAString & aTitle) { return _to SetTitle(aTitle); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetLang(nsAString & aLang) { return _to GetLang(aLang); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetLang(const nsAString & aLang) { return _to SetLang(aLang); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetDir(nsAString & aDir) { return _to GetDir(aDir); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetDir(const nsAString & aDir) { return _to SetDir(aDir); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetClassName(nsAString & aClassName) { return _to GetClassName(aClassName); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetClassName(const nsAString & aClassName) { return _to SetClassName(aClassName); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetAccessKey(nsAString & aAccessKey) { return _to GetAccessKey(aAccessKey); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD SetAccessKey(const nsAString & aAccessKey) { return _to SetAccessKey(aAccessKey); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD GetAccessKeyLabel(nsAString & aLabel) { return _to GetAccessKeyLabel(aLabel); } \
|
||||
NS_SCRIPTABLE NS_IMETHOD Blur(void) { return _to Blur(); }
|
||||
|
||||
/**
|
||||
@ -1563,6 +1570,8 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(Label)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Legend)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Link)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Map)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Menu)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(MenuItem)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Meta)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(Object)
|
||||
NS_DECLARE_NS_NEW_HTML_ELEMENT(OptGroup)
|
||||
|
289
content/html/content/src/nsHTMLMenuElement.cpp
Normal file
289
content/html/content/src/nsHTMLMenuElement.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMNSHTMLElement.h"
|
||||
#include "nsIDOMHTMLMenuItemElement.h"
|
||||
#include "nsXULContextMenuBuilder.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsHTMLMenuItemElement.h"
|
||||
#include "nsHTMLMenuElement.h"
|
||||
|
||||
enum MenuType
|
||||
{
|
||||
MENU_TYPE_CONTEXT = 1,
|
||||
MENU_TYPE_TOOLBAR,
|
||||
MENU_TYPE_LIST
|
||||
};
|
||||
|
||||
static const nsAttrValue::EnumTable kMenuTypeTable[] = {
|
||||
{ "context", MENU_TYPE_CONTEXT },
|
||||
{ "toolbar", MENU_TYPE_TOOLBAR },
|
||||
{ "list", MENU_TYPE_LIST },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const nsAttrValue::EnumTable* kMenuDefaultType =
|
||||
&kMenuTypeTable[2];
|
||||
|
||||
enum SeparatorType
|
||||
{
|
||||
ST_TRUE_INIT = -1,
|
||||
ST_FALSE = 0,
|
||||
ST_TRUE = 1
|
||||
};
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Menu)
|
||||
|
||||
|
||||
nsHTMLMenuElement::nsHTMLMenuElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo), mType(MENU_TYPE_LIST)
|
||||
{
|
||||
}
|
||||
|
||||
nsHTMLMenuElement::~nsHTMLMenuElement()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLMenuElement, nsGenericElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLMenuElement, nsGenericElement)
|
||||
|
||||
|
||||
DOMCI_NODE_DATA(HTMLMenuElement, nsHTMLMenuElement)
|
||||
|
||||
// QueryInterface implementation for nsHTMLMenuElement
|
||||
NS_INTERFACE_TABLE_HEAD(nsHTMLMenuElement)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLMenuElement,
|
||||
nsIDOMHTMLMenuElement,
|
||||
nsIHTMLMenu)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLMenuElement,
|
||||
nsGenericHTMLElement)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLMenuElement)
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE(nsHTMLMenuElement)
|
||||
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLMenuElement, Compact, compact)
|
||||
NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLMenuElement, Type, type,
|
||||
kMenuDefaultType->tag)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLMenuElement, Label, label)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLMenuElement::SendShowEvent()
|
||||
{
|
||||
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
|
||||
|
||||
nsCOMPtr<nsIDocument> document = GetCurrentDoc();
|
||||
if (!document) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsEvent event(PR_TRUE, NS_SHOW_EVENT);
|
||||
event.flags |= NS_EVENT_FLAG_CANT_CANCEL | NS_EVENT_FLAG_CANT_BUBBLE;
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = document->GetShell();
|
||||
if (!shell) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<nsPresContext> presContext = shell->GetPresContext();
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
|
||||
&event, nsnull, &status);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLMenuElement::CreateBuilder(nsIMenuBuilder** _retval)
|
||||
{
|
||||
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
|
||||
|
||||
*_retval = nsnull;
|
||||
|
||||
if (mType == MENU_TYPE_CONTEXT) {
|
||||
NS_ADDREF(*_retval = new nsXULContextMenuBuilder());
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
|
||||
{
|
||||
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
|
||||
|
||||
if (!aBuilder) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
BuildSubmenu(EmptyString(), this, aBuilder);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
PRBool
|
||||
nsHTMLMenuElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) {
|
||||
PRBool success = aResult.ParseEnumValue(aValue, kMenuTypeTable,
|
||||
PR_FALSE);
|
||||
if (success) {
|
||||
mType = aResult.GetEnumValue();
|
||||
} else {
|
||||
mType = kMenuDefaultType->value;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
||||
aResult);
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuElement::BuildSubmenu(const nsAString& aLabel,
|
||||
nsIContent* aContent,
|
||||
nsIMenuBuilder* aBuilder)
|
||||
{
|
||||
aBuilder->OpenContainer(aLabel);
|
||||
|
||||
PRInt8 separator = ST_TRUE_INIT;
|
||||
TraverseContent(aContent, aBuilder, separator);
|
||||
|
||||
if (separator == ST_TRUE) {
|
||||
aBuilder->UndoAddSeparator();
|
||||
}
|
||||
|
||||
aBuilder->CloseContainer();
|
||||
}
|
||||
|
||||
// static
|
||||
PRBool
|
||||
nsHTMLMenuElement::CanLoadIcon(nsIContent* aContent, const nsAString& aIcon)
|
||||
{
|
||||
if (aIcon.IsEmpty()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsIDocument* doc = aContent->GetOwnerDoc();
|
||||
if (!doc) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI = aContent->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aIcon, doc,
|
||||
baseURI);
|
||||
|
||||
if (!uri) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
return nsContentUtils::CanLoadImage(uri, aContent, doc,
|
||||
aContent->NodePrincipal());
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuElement::TraverseContent(nsIContent* aContent,
|
||||
nsIMenuBuilder* aBuilder,
|
||||
PRInt8& aSeparator)
|
||||
{
|
||||
nsCOMPtr<nsIContent> child;
|
||||
for (child = aContent->GetFirstChild(); child;
|
||||
child = child->GetNextSibling()) {
|
||||
nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(child);
|
||||
if (!element) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsIAtom* tag = child->Tag();
|
||||
|
||||
if (tag == nsGkAtoms::menuitem) {
|
||||
nsHTMLMenuItemElement* menuitem =
|
||||
nsHTMLMenuItemElement::FromContent(child);
|
||||
|
||||
if (menuitem->IsHidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString label;
|
||||
menuitem->GetLabel(label);
|
||||
if (label.IsEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAutoString icon;
|
||||
menuitem->GetIcon(icon);
|
||||
|
||||
aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon));
|
||||
|
||||
aSeparator = ST_FALSE;
|
||||
} else if (tag == nsGkAtoms::menu && !element->IsHidden()) {
|
||||
if (child->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) {
|
||||
nsAutoString label;
|
||||
child->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
|
||||
|
||||
BuildSubmenu(label, child, aBuilder);
|
||||
|
||||
aSeparator = ST_FALSE;
|
||||
} else {
|
||||
AddSeparator(aBuilder, aSeparator);
|
||||
|
||||
TraverseContent(child, aBuilder, aSeparator);
|
||||
|
||||
AddSeparator(aBuilder, aSeparator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
nsHTMLMenuElement::AddSeparator(nsIMenuBuilder* aBuilder, PRInt8& aSeparator)
|
||||
{
|
||||
if (aSeparator) {
|
||||
return;
|
||||
}
|
||||
|
||||
aBuilder->AddSeparator();
|
||||
aSeparator = ST_TRUE;
|
||||
}
|
101
content/html/content/src/nsHTMLMenuElement.h
Normal file
101
content/html/content/src/nsHTMLMenuElement.h
Normal file
@ -0,0 +1,101 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMHTMLMenuElement.h"
|
||||
#include "nsIHTMLMenu.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsIDOMNSHTMLElement.h"
|
||||
|
||||
class nsHTMLMenuElement : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLMenuElement,
|
||||
public nsIHTMLMenu
|
||||
{
|
||||
public:
|
||||
nsHTMLMenuElement(already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
virtual ~nsHTMLMenuElement();
|
||||
|
||||
/** Typesafe, non-refcounting cast from nsIContent. Cheaper than QI. **/
|
||||
static nsHTMLMenuElement* FromContent(nsIContent* aContent)
|
||||
{
|
||||
if (aContent && aContent->IsHTML(nsGkAtoms::menu))
|
||||
return static_cast<nsHTMLMenuElement*>(aContent);
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIDOMNode
|
||||
NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMElement
|
||||
NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMHTMLElement
|
||||
NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMHTMLMenuElement
|
||||
NS_DECL_NSIDOMHTMLMENUELEMENT
|
||||
|
||||
// nsIHTMLMenu
|
||||
NS_DECL_NSIHTMLMENU
|
||||
|
||||
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult);
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
|
||||
PRUint8 GetType() const { return mType; }
|
||||
|
||||
protected:
|
||||
static PRBool CanLoadIcon(nsIContent* aContent, const nsAString& aIcon);
|
||||
|
||||
void BuildSubmenu(const nsAString& aLabel,
|
||||
nsIContent* aContent,
|
||||
nsIMenuBuilder* aBuilder);
|
||||
|
||||
void TraverseContent(nsIContent* aContent,
|
||||
nsIMenuBuilder* aBuilder,
|
||||
PRInt8& aSeparator);
|
||||
|
||||
void AddSeparator(nsIMenuBuilder* aBuilder, PRInt8& aSeparator);
|
||||
|
||||
PRUint8 mType;
|
||||
};
|
514
content/html/content/src/nsHTMLMenuItemElement.cpp
Normal file
514
content/html/content/src/nsHTMLMenuItemElement.cpp
Normal file
@ -0,0 +1,514 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
#include "nsHTMLMenuItemElement.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
// First bits are needed for the menuitem type.
|
||||
#define NS_CHECKED_IS_TOGGLED (1 << 2)
|
||||
#define NS_ORIGINAL_CHECKED_VALUE (1 << 3)
|
||||
#define NS_MENUITEM_TYPE(bits) ((bits) & ~( \
|
||||
NS_CHECKED_IS_TOGGLED | NS_ORIGINAL_CHECKED_VALUE))
|
||||
|
||||
enum CmdType
|
||||
{
|
||||
CMD_TYPE_MENUITEM = 1,
|
||||
CMD_TYPE_CHECKBOX,
|
||||
CMD_TYPE_RADIO
|
||||
};
|
||||
|
||||
static const nsAttrValue::EnumTable kMenuItemTypeTable[] = {
|
||||
{ "menuitem", CMD_TYPE_MENUITEM },
|
||||
{ "checkbox", CMD_TYPE_CHECKBOX },
|
||||
{ "radio", CMD_TYPE_RADIO },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const nsAttrValue::EnumTable* kMenuItemDefaultType =
|
||||
&kMenuItemTypeTable[0];
|
||||
|
||||
// A base class inherited by all radio visitors.
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
Visitor() { }
|
||||
virtual ~Visitor() { }
|
||||
|
||||
/**
|
||||
* Visit a node in the tree. This is meant to be called on all radios in a
|
||||
* group, sequentially. If the method returns false then the iteration is
|
||||
* stopped.
|
||||
*/
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem) = 0;
|
||||
};
|
||||
|
||||
// Find the selected radio, see GetSelectedRadio().
|
||||
class GetCheckedVisitor : public Visitor
|
||||
{
|
||||
public:
|
||||
GetCheckedVisitor(nsHTMLMenuItemElement** aResult)
|
||||
: mResult(aResult)
|
||||
{ }
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem)
|
||||
{
|
||||
if (aMenuItem->IsChecked()) {
|
||||
*mResult = aMenuItem;
|
||||
return PR_FALSE;
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
protected:
|
||||
nsHTMLMenuItemElement** mResult;
|
||||
};
|
||||
|
||||
// Deselect all radios except the one passed to the constructor.
|
||||
class ClearCheckedVisitor : public Visitor
|
||||
{
|
||||
public:
|
||||
ClearCheckedVisitor(nsHTMLMenuItemElement* aExcludeMenuItem)
|
||||
: mExcludeMenuItem(aExcludeMenuItem)
|
||||
{ }
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem)
|
||||
{
|
||||
if (aMenuItem != mExcludeMenuItem && aMenuItem->IsChecked()) {
|
||||
aMenuItem->ClearChecked();
|
||||
}
|
||||
return PR_TRUE;
|
||||
}
|
||||
protected:
|
||||
nsHTMLMenuItemElement* mExcludeMenuItem;
|
||||
};
|
||||
|
||||
// Get current value of the checked dirty flag. The same value is stored on all
|
||||
// radios in the group, so we need to check only the first one.
|
||||
class GetCheckedDirtyVisitor : public Visitor
|
||||
{
|
||||
public:
|
||||
GetCheckedDirtyVisitor(PRBool* aCheckedDirty,
|
||||
nsHTMLMenuItemElement* aExcludeMenuItem)
|
||||
: mCheckedDirty(aCheckedDirty),
|
||||
mExcludeMenuItem(aExcludeMenuItem)
|
||||
{ }
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem)
|
||||
{
|
||||
if (aMenuItem == mExcludeMenuItem) {
|
||||
return PR_TRUE;
|
||||
}
|
||||
*mCheckedDirty = aMenuItem->IsCheckedDirty();
|
||||
return PR_FALSE;
|
||||
}
|
||||
protected:
|
||||
PRBool* mCheckedDirty;
|
||||
nsHTMLMenuItemElement* mExcludeMenuItem;
|
||||
};
|
||||
|
||||
// Set checked dirty to true on all radios in the group.
|
||||
class SetCheckedDirtyVisitor : public Visitor
|
||||
{
|
||||
public:
|
||||
SetCheckedDirtyVisitor()
|
||||
{ }
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem)
|
||||
{
|
||||
aMenuItem->SetCheckedDirty();
|
||||
return PR_TRUE;
|
||||
}
|
||||
};
|
||||
|
||||
// A helper visitor that is used to combine two operations (visitors) to avoid
|
||||
// iterating over radios twice.
|
||||
class CombinedVisitor : public Visitor
|
||||
{
|
||||
public:
|
||||
CombinedVisitor(Visitor* aVisitor1, Visitor* aVisitor2)
|
||||
: mVisitor1(aVisitor1), mVisitor2(aVisitor2),
|
||||
mContinue1(PR_TRUE), mContinue2(PR_TRUE)
|
||||
{ }
|
||||
virtual PRBool Visit(nsHTMLMenuItemElement* aMenuItem)
|
||||
{
|
||||
if (mContinue1) {
|
||||
mContinue1 = mVisitor1->Visit(aMenuItem);
|
||||
}
|
||||
if (mContinue2) {
|
||||
mContinue2 = mVisitor2->Visit(aMenuItem);
|
||||
}
|
||||
return mContinue1 || mContinue2;
|
||||
}
|
||||
protected:
|
||||
Visitor* mVisitor1;
|
||||
Visitor* mVisitor2;
|
||||
PRPackedBool mContinue1;
|
||||
PRPackedBool mContinue2;
|
||||
};
|
||||
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(MenuItem)
|
||||
|
||||
nsHTMLMenuItemElement::nsHTMLMenuItemElement(
|
||||
already_AddRefed<nsINodeInfo> aNodeInfo, FromParser aFromParser)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mType(kMenuItemDefaultType->value),
|
||||
mParserCreating(false),
|
||||
mShouldInitChecked(false),
|
||||
mCheckedDirty(false),
|
||||
mChecked(false)
|
||||
{
|
||||
mParserCreating = aFromParser;
|
||||
}
|
||||
|
||||
nsHTMLMenuItemElement::~nsHTMLMenuItemElement()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLMenuItemElement, nsGenericElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLMenuItemElement, nsGenericElement)
|
||||
|
||||
|
||||
DOMCI_NODE_DATA(HTMLMenuItemElement, nsHTMLMenuItemElement)
|
||||
|
||||
// QueryInterface implementation for nsHTMLMenuItemElement
|
||||
NS_INTERFACE_TABLE_HEAD(nsHTMLMenuItemElement)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE2(nsHTMLMenuItemElement,
|
||||
nsIDOMHTMLCommandElement,
|
||||
nsIDOMHTMLMenuItemElement)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLMenuItemElement,
|
||||
nsGenericHTMLElement)
|
||||
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLMenuItemElement)
|
||||
|
||||
//NS_IMPL_ELEMENT_CLONE(nsHTMLMenuItemElement)
|
||||
nsresult
|
||||
nsHTMLMenuItemElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
|
||||
{
|
||||
*aResult = nsnull;
|
||||
nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
|
||||
nsHTMLMenuItemElement *it = new nsHTMLMenuItemElement(ni.forget(),
|
||||
NOT_FROM_PARSER);
|
||||
if (!it) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINode> kungFuDeathGrip = it;
|
||||
nsresult rv = CopyInnerTo(it);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
switch (mType) {
|
||||
case CMD_TYPE_CHECKBOX:
|
||||
case CMD_TYPE_RADIO:
|
||||
if (mCheckedDirty) {
|
||||
// We no longer have our original checked state. Set our
|
||||
// checked state on the clone.
|
||||
it->mCheckedDirty = true;
|
||||
it->mChecked = mChecked;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
kungFuDeathGrip.swap(*aResult);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLMenuItemElement, Type, type,
|
||||
kMenuItemDefaultType->tag)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLMenuItemElement, Label, label)
|
||||
NS_IMPL_URI_ATTR(nsHTMLMenuItemElement, Icon, icon)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLMenuItemElement, Disabled, disabled)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLMenuItemElement, DefaultChecked, checked)
|
||||
//NS_IMPL_BOOL_ATTR(nsHTMLMenuItemElement, Checked, checked)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLMenuItemElement, Radiogroup, radiogroup)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLMenuItemElement::GetChecked(PRBool* aChecked)
|
||||
{
|
||||
*aChecked = mChecked;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLMenuItemElement::SetChecked(PRBool aChecked)
|
||||
{
|
||||
PRBool checkedChanged = mChecked != aChecked;
|
||||
|
||||
mChecked = aChecked;
|
||||
|
||||
if (mType == CMD_TYPE_RADIO) {
|
||||
if (checkedChanged) {
|
||||
if (mCheckedDirty) {
|
||||
ClearCheckedVisitor visitor(this);
|
||||
WalkRadioGroup(&visitor);
|
||||
} else {
|
||||
ClearCheckedVisitor visitor1(this);
|
||||
SetCheckedDirtyVisitor visitor2;
|
||||
CombinedVisitor visitor(&visitor1, &visitor2);
|
||||
WalkRadioGroup(&visitor);
|
||||
}
|
||||
} else if (!mCheckedDirty) {
|
||||
SetCheckedDirtyVisitor visitor;
|
||||
WalkRadioGroup(&visitor);
|
||||
}
|
||||
} else {
|
||||
mCheckedDirty = true;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLMenuItemElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
||||
{
|
||||
if (aVisitor.mEvent->message == NS_MOUSE_CLICK) {
|
||||
|
||||
PRBool originalCheckedValue = PR_FALSE;
|
||||
switch (mType) {
|
||||
case CMD_TYPE_CHECKBOX:
|
||||
originalCheckedValue = mChecked;
|
||||
SetChecked(!originalCheckedValue);
|
||||
aVisitor.mItemFlags |= NS_CHECKED_IS_TOGGLED;
|
||||
break;
|
||||
case CMD_TYPE_RADIO:
|
||||
nsCOMPtr<nsIDOMHTMLMenuItemElement> selectedRadio = GetSelectedRadio();
|
||||
aVisitor.mItemData = selectedRadio;
|
||||
|
||||
originalCheckedValue = mChecked;
|
||||
if (!originalCheckedValue) {
|
||||
SetChecked(PR_TRUE);
|
||||
aVisitor.mItemFlags |= NS_CHECKED_IS_TOGGLED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (originalCheckedValue) {
|
||||
aVisitor.mItemFlags |= NS_ORIGINAL_CHECKED_VALUE;
|
||||
}
|
||||
|
||||
// We must cache type because mType may change during JS event.
|
||||
aVisitor.mItemFlags |= mType;
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::PreHandleEvent(aVisitor);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLMenuItemElement::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
|
||||
{
|
||||
// Check to see if the event was cancelled.
|
||||
if (aVisitor.mEvent->message == NS_MOUSE_CLICK &&
|
||||
aVisitor.mItemFlags & NS_CHECKED_IS_TOGGLED &&
|
||||
aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
PRBool originalCheckedValue =
|
||||
!!(aVisitor.mItemFlags & NS_ORIGINAL_CHECKED_VALUE);
|
||||
PRUint8 oldType = NS_MENUITEM_TYPE(aVisitor.mItemFlags);
|
||||
|
||||
nsCOMPtr<nsIDOMHTMLMenuItemElement> selectedRadio =
|
||||
do_QueryInterface(aVisitor.mItemData);
|
||||
if (selectedRadio) {
|
||||
selectedRadio->SetChecked(PR_TRUE);
|
||||
if (mType != CMD_TYPE_RADIO) {
|
||||
SetChecked(PR_FALSE);
|
||||
}
|
||||
} else if (oldType == CMD_TYPE_CHECKBOX) {
|
||||
SetChecked(originalCheckedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLMenuItemElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
PRBool aCompileEventHandlers)
|
||||
{
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && aDocument && mType == CMD_TYPE_RADIO) {
|
||||
AddedToRadioGroup();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLMenuItemElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::type) {
|
||||
PRBool success = aResult.ParseEnumValue(aValue, kMenuItemTypeTable,
|
||||
PR_FALSE);
|
||||
if (success) {
|
||||
mType = aResult.GetEnumValue();
|
||||
} else {
|
||||
mType = kMenuItemDefaultType->value;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
if (aAttribute == nsGkAtoms::radiogroup) {
|
||||
aResult.ParseAtom(aValue);
|
||||
return PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
||||
aResult);
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuItemElement::DoneCreatingElement()
|
||||
{
|
||||
mParserCreating = false;
|
||||
|
||||
if (mShouldInitChecked) {
|
||||
InitChecked();
|
||||
mShouldInitChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLMenuItemElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
||||
const nsAString* aValue, PRBool aNotify)
|
||||
{
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if ((aName == nsGkAtoms::radiogroup || aName == nsGkAtoms::type) &&
|
||||
mType == CMD_TYPE_RADIO &&
|
||||
!mParserCreating) {
|
||||
if (IsInDoc() && GetParent()) {
|
||||
AddedToRadioGroup();
|
||||
}
|
||||
}
|
||||
|
||||
// Checked must be set no matter what type of menuitem it is, since
|
||||
// GetChecked() must reflect the new value
|
||||
if (aName == nsGkAtoms::checked &&
|
||||
!mCheckedDirty) {
|
||||
if (mParserCreating) {
|
||||
mShouldInitChecked = true;
|
||||
} else {
|
||||
InitChecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
|
||||
aNotify);
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuItemElement::WalkRadioGroup(Visitor* aVisitor)
|
||||
{
|
||||
nsIContent* parent = GetParent();
|
||||
if (!parent) {
|
||||
aVisitor->Visit(this);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAttrInfo info1(GetAttrInfo(kNameSpaceID_None,
|
||||
nsGkAtoms::radiogroup));
|
||||
PRBool info1Empty = !info1.mValue || info1.mValue->IsEmptyString();
|
||||
|
||||
for (nsIContent* cur = parent->GetFirstChild();
|
||||
cur;
|
||||
cur = cur->GetNextSibling()) {
|
||||
nsHTMLMenuItemElement* menuitem = nsHTMLMenuItemElement::FromContent(cur);
|
||||
|
||||
if (!menuitem || menuitem->GetType() != CMD_TYPE_RADIO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsAttrInfo info2(menuitem->GetAttrInfo(kNameSpaceID_None,
|
||||
nsGkAtoms::radiogroup));
|
||||
PRBool info2Empty = !info2.mValue || info2.mValue->IsEmptyString();
|
||||
|
||||
if (info1Empty != info2Empty ||
|
||||
info1.mValue && info2.mValue && !info1.mValue->Equals(*info2.mValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aVisitor->Visit(menuitem)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsHTMLMenuItemElement*
|
||||
nsHTMLMenuItemElement::GetSelectedRadio()
|
||||
{
|
||||
nsHTMLMenuItemElement* result = nsnull;
|
||||
|
||||
GetCheckedVisitor visitor(&result);
|
||||
WalkRadioGroup(&visitor);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuItemElement::AddedToRadioGroup()
|
||||
{
|
||||
PRBool checkedDirty = mCheckedDirty;
|
||||
if (mChecked) {
|
||||
ClearCheckedVisitor visitor1(this);
|
||||
GetCheckedDirtyVisitor visitor2(&checkedDirty, this);
|
||||
CombinedVisitor visitor(&visitor1, &visitor2);
|
||||
WalkRadioGroup(&visitor);
|
||||
} else {
|
||||
GetCheckedDirtyVisitor visitor(&checkedDirty, this);
|
||||
WalkRadioGroup(&visitor);
|
||||
}
|
||||
mCheckedDirty = checkedDirty;
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLMenuItemElement::InitChecked()
|
||||
{
|
||||
PRBool defaultChecked;
|
||||
GetDefaultChecked(&defaultChecked);
|
||||
mChecked = defaultChecked;
|
||||
if (mType == CMD_TYPE_RADIO) {
|
||||
ClearCheckedVisitor visitor(this);
|
||||
WalkRadioGroup(&visitor);
|
||||
}
|
||||
}
|
127
content/html/content/src/nsHTMLMenuItemElement.h
Normal file
127
content/html/content/src/nsHTMLMenuItemElement.h
Normal file
@ -0,0 +1,127 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMHTMLMenuItemElement.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
|
||||
class Visitor;
|
||||
|
||||
class nsHTMLMenuItemElement : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLMenuItemElement
|
||||
{
|
||||
public:
|
||||
nsHTMLMenuItemElement(already_AddRefed<nsINodeInfo> aNodeInfo,
|
||||
mozilla::dom::FromParser aFromParser);
|
||||
virtual ~nsHTMLMenuItemElement();
|
||||
|
||||
/** Typesafe, non-refcounting cast from nsIContent. Cheaper than QI. **/
|
||||
static nsHTMLMenuItemElement* FromContent(nsIContent* aContent)
|
||||
{
|
||||
if (aContent && aContent->IsHTML(nsGkAtoms::menuitem)) {
|
||||
return static_cast<nsHTMLMenuItemElement*>(aContent);
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIDOMNode
|
||||
NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMElement
|
||||
NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMHTMLElement
|
||||
NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
|
||||
|
||||
// nsIDOMHTMLCommandElement
|
||||
NS_DECL_NSIDOMHTMLCOMMANDELEMENT
|
||||
|
||||
// nsIDOMHTMLMenuItemElement
|
||||
NS_DECL_NSIDOMHTMLMENUITEMELEMENT
|
||||
|
||||
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
|
||||
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
|
||||
|
||||
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
PRBool aCompileEventHandlers);
|
||||
|
||||
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult);
|
||||
|
||||
virtual void DoneCreatingElement();
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
|
||||
PRUint8 GetType() const { return mType; }
|
||||
|
||||
/**
|
||||
* Syntax sugar to make it easier to check for checked and checked dirty
|
||||
*/
|
||||
PRBool IsChecked() const { return mChecked; }
|
||||
PRBool IsCheckedDirty() const { return mCheckedDirty; }
|
||||
|
||||
protected:
|
||||
virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
const nsAString* aValue, PRBool aNotify);
|
||||
|
||||
void WalkRadioGroup(Visitor* aVisitor);
|
||||
|
||||
nsHTMLMenuItemElement* GetSelectedRadio();
|
||||
|
||||
void AddedToRadioGroup();
|
||||
|
||||
void InitChecked();
|
||||
|
||||
friend class ClearCheckedVisitor;
|
||||
friend class SetCheckedDirtyVisitor;
|
||||
|
||||
void ClearChecked() { mChecked = false; }
|
||||
void SetCheckedDirty() { mCheckedDirty = true; }
|
||||
|
||||
private:
|
||||
PRUint8 mType : 2;
|
||||
bool mParserCreating : 1;
|
||||
bool mShouldInitChecked : 1;
|
||||
bool mCheckedDirty : 1;
|
||||
bool mChecked : 1;
|
||||
};
|
@ -38,7 +38,6 @@
|
||||
#include "nsIDOMHTMLParamElement.h"
|
||||
#include "nsIDOMHTMLBaseElement.h"
|
||||
#include "nsIDOMHTMLDirectoryElement.h"
|
||||
#include "nsIDOMHTMLMenuElement.h"
|
||||
#include "nsIDOMHTMLQuoteElement.h"
|
||||
#include "nsIDOMHTMLHeadElement.h"
|
||||
#include "nsIDOMHTMLHtmlElement.h"
|
||||
@ -59,7 +58,6 @@ class nsHTMLSharedElement : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLParamElement,
|
||||
public nsIDOMHTMLBaseElement,
|
||||
public nsIDOMHTMLDirectoryElement,
|
||||
public nsIDOMHTMLMenuElement,
|
||||
public nsIDOMHTMLQuoteElement,
|
||||
public nsIDOMHTMLHeadElement,
|
||||
public nsIDOMHTMLHtmlElement
|
||||
@ -89,9 +87,6 @@ public:
|
||||
// nsIDOMHTMLDirectoryElement
|
||||
NS_DECL_NSIDOMHTMLDIRECTORYELEMENT
|
||||
|
||||
// nsIDOMHTMLMenuElement
|
||||
// Same as directoryelement
|
||||
|
||||
// nsIDOMHTMLQuoteElement
|
||||
NS_DECL_NSIDOMHTMLQUOTEELEMENT
|
||||
|
||||
@ -157,7 +152,6 @@ NS_IMPL_RELEASE_INHERITED(nsHTMLSharedElement, nsGenericElement)
|
||||
DOMCI_DATA(HTMLParamElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLBaseElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLDirectoryElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLMenuElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLQuoteElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLHeadElement, nsHTMLSharedElement)
|
||||
DOMCI_DATA(HTMLHtmlElement, nsHTMLSharedElement)
|
||||
@ -174,9 +168,6 @@ nsHTMLSharedElement::GetClassInfoInternal()
|
||||
if (mNodeInfo->Equals(nsGkAtoms::dir)) {
|
||||
return NS_GetDOMClassInfoInstance(eDOMClassInfo_HTMLDirectoryElement_id);
|
||||
}
|
||||
if (mNodeInfo->Equals(nsGkAtoms::menu)) {
|
||||
return NS_GetDOMClassInfoInstance(eDOMClassInfo_HTMLMenuElement_id);
|
||||
}
|
||||
if (mNodeInfo->Equals(nsGkAtoms::q)) {
|
||||
return NS_GetDOMClassInfoInstance(eDOMClassInfo_HTMLQuoteElement_id);
|
||||
}
|
||||
@ -203,7 +194,6 @@ NS_INTERFACE_TABLE_HEAD(nsHTMLSharedElement)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLParamElement, param)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLBaseElement, base)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLDirectoryElement, dir)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLMenuElement, menu)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLQuoteElement, q)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLQuoteElement, blockquote)
|
||||
NS_INTERFACE_MAP_ENTRY_IF_TAG(nsIDOMHTMLHeadElement, head)
|
||||
@ -224,9 +214,6 @@ NS_IMPL_STRING_ATTR(nsHTMLSharedElement, ValueType, valuetype)
|
||||
// nsIDOMHTMLDirectoryElement
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLSharedElement, Compact, compact)
|
||||
|
||||
// nsIDOMHTMLMenuElement
|
||||
//NS_IMPL_BOOL_ATTR(nsHTMLSharedElement, Compact, compact)
|
||||
|
||||
// nsIDOMHTMLQuoteElement
|
||||
NS_IMPL_URI_ATTR(nsHTMLSharedElement, Cite, cite)
|
||||
|
||||
@ -275,8 +262,7 @@ nsHTMLSharedElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsAttrValue& aResult)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None &&
|
||||
(mNodeInfo->Equals(nsGkAtoms::dir) ||
|
||||
mNodeInfo->Equals(nsGkAtoms::menu))) {
|
||||
mNodeInfo->Equals(nsGkAtoms::dir)) {
|
||||
if (aAttribute == nsGkAtoms::type) {
|
||||
return aResult.ParseEnumValue(aValue, kListTypeTable, PR_FALSE);
|
||||
}
|
||||
@ -290,7 +276,7 @@ nsHTMLSharedElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
}
|
||||
|
||||
static void
|
||||
DirectoryMenuMapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
DirectoryMapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
nsRuleData* aData)
|
||||
{
|
||||
if (aData->mSIDs & NS_STYLE_INHERIT_BIT(List)) {
|
||||
@ -486,8 +472,8 @@ nsHTMLSharedElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
nsMapRuleToAttributesFunc
|
||||
nsHTMLSharedElement::GetAttributeMappingFunction() const
|
||||
{
|
||||
if (mNodeInfo->Equals(nsGkAtoms::dir) || mNodeInfo->Equals(nsGkAtoms::menu)) {
|
||||
return &DirectoryMenuMapAttributesIntoRule;
|
||||
if (mNodeInfo->Equals(nsGkAtoms::dir)) {
|
||||
return &DirectoryMapAttributesIntoRule;
|
||||
}
|
||||
|
||||
return nsGenericHTMLElement::GetAttributeMappingFunction();
|
||||
|
@ -146,7 +146,6 @@ _TEST_FILES = \
|
||||
test_formSubmission2.html \
|
||||
file_formSubmission_text.txt \
|
||||
file_formSubmission_img.jpg \
|
||||
test_bug418756.html \
|
||||
test_bug421640.html \
|
||||
test_bug424698.html \
|
||||
test_bug428135.xhtml \
|
||||
@ -280,6 +279,8 @@ _TEST_FILES = \
|
||||
test_bug674558.html \
|
||||
test_bug583533.html \
|
||||
test_restore_from_parser_fragment.html \
|
||||
test_bug617528.html \
|
||||
test_checked.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
74
content/html/content/test/test_bug617528.html
Normal file
74
content/html/content/test/test_bug617528.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=617528
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 617528</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=617528">Mozilla Bug 617528</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<menu>
|
||||
<menuitem id="checkbox" type="checkbox" label="Checkbox" checked></menuitem>
|
||||
<menuitem id="radio1" type="radio" label="Radio1" checked></menuitem>
|
||||
<menuitem id="radio2" type="radio" label="Radio2"></menuitem>
|
||||
</menu>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 617528 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
function click(element, preventDefault, checked) {
|
||||
function handleClick(event) {
|
||||
is(this.checked, checked,
|
||||
"checking .checked (" + this.id + ")");
|
||||
if (preventDefault)
|
||||
event.preventDefault();
|
||||
}
|
||||
element.addEventListener("click", handleClick);
|
||||
element.click();
|
||||
element.removeEventListener("click", handleClick);
|
||||
}
|
||||
|
||||
function verify(elements, data) {
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
var element = elements[i];
|
||||
is(element.checked, data[i*2],
|
||||
"checking .checked (" + element.id + ")");
|
||||
is(element.defaultChecked, data[i*2+1],
|
||||
'checking .defaultChecked (' + element.id + ")");
|
||||
}
|
||||
}
|
||||
|
||||
var checkbox = document.getElementById("checkbox");
|
||||
click(checkbox, false, false);
|
||||
verify([checkbox], [false, true]);
|
||||
|
||||
click(checkbox, true, true);
|
||||
verify([checkbox], [false, true]);
|
||||
|
||||
var radio1 = document.getElementById("radio1");
|
||||
var radio2 = document.getElementById("radio2");
|
||||
click(radio2, false, true);
|
||||
verify([radio1, radio2], [false, true,
|
||||
true, false]);
|
||||
|
||||
click(radio1, true, true);
|
||||
verify([radio1, radio2], [false, true,
|
||||
true, false]);
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -2,26 +2,41 @@
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418756
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=617528
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 418756</title>
|
||||
<title>Test for Bug 418756 and 617528</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418756">Mozilla Bug 418756</a>
|
||||
Mozilla bug
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418756">418756</a>
|
||||
and
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=617528">617528</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<form id="f1">
|
||||
</form>
|
||||
<form id="f2">
|
||||
</form>
|
||||
<menu id="m1">
|
||||
</menu>
|
||||
<menu id="m2">
|
||||
</menu>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript; version=1.7">
|
||||
|
||||
/** Test for Bug 418756 **/
|
||||
/** Test for Bug 418756 and 617528 **/
|
||||
let group1;
|
||||
let group2;
|
||||
let group3;
|
||||
|
||||
let tags = ["input", "menuitem"];
|
||||
for each (let tag in tags) {
|
||||
|
||||
function bounce(node) {
|
||||
let n = node.nextSibling;
|
||||
let p = node.parentNode;
|
||||
@ -50,13 +65,13 @@ let id = 0;
|
||||
|
||||
// type can be 'c' for 'checkbox' and 'r' for 'radio'
|
||||
function createNode(type, name, checked) {
|
||||
let node = document.createElement("input");
|
||||
let node = document.createElement(tag);
|
||||
node.setAttribute("type", typeMapper[type]);
|
||||
if (checked) {
|
||||
node.setAttribute("checked", "checked");
|
||||
}
|
||||
node.setAttribute("id", type + (++id));
|
||||
node.setAttribute("name", name);
|
||||
node.setAttribute(tag == "input" ? "name" : "radiogroup", name);
|
||||
createdNodes.push(node);
|
||||
return node;
|
||||
}
|
||||
@ -97,7 +112,7 @@ cleanup();
|
||||
// effect
|
||||
for each (let type in types) {
|
||||
let n = createNode(type, 'test1', true);
|
||||
$("f1").appendChild(n);
|
||||
$(tag == "input" ? "f1" : "m1").appendChild(n);
|
||||
n.checked = false;
|
||||
n.defaultChecked = false;
|
||||
bounce(n);
|
||||
@ -109,23 +124,23 @@ cleanup();
|
||||
|
||||
// Now check that playing with a single radio in a group affects all
|
||||
// other radios in the group (but not radios not in that group)
|
||||
let group1 = [ createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false) ];
|
||||
let group2 = [ createNode('r', 'g2', false),
|
||||
createNode('r', 'g2', false),
|
||||
createNode('r', 'g2', false) ];
|
||||
let group3 = [ createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false) ];
|
||||
group1 = [ createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false) ];
|
||||
group2 = [ createNode('r', 'g2', false),
|
||||
createNode('r', 'g2', false),
|
||||
createNode('r', 'g2', false) ];
|
||||
group3 = [ createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false),
|
||||
createNode('r', 'g1', false) ];
|
||||
for each (let g in group1) {
|
||||
$("f1").appendChild(g);
|
||||
$(tag == "input" ? "f1" : "m1").appendChild(g);
|
||||
}
|
||||
for each (let g in group2) {
|
||||
$("f1").appendChild(g);
|
||||
$(tag == "input" ? "f1" : "m1").appendChild(g);
|
||||
}
|
||||
for each (let g in group3) {
|
||||
$("f2").appendChild(g);
|
||||
$(tag == "input" ? "f2" : "m2").appendChild(g);
|
||||
}
|
||||
|
||||
for each (let n in [1, 2, 3]) {
|
||||
@ -335,6 +350,8 @@ for each (let n in [1, 2, 3]) {
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
@ -1021,7 +1021,10 @@ SinkContext::AddLeaf(const nsIParserNode& aNode)
|
||||
|
||||
case eHTMLTag_input:
|
||||
content->DoneCreatingElement();
|
||||
break;
|
||||
|
||||
case eHTMLTag_menuitem:
|
||||
content->DoneCreatingElement();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1070,7 +1070,8 @@ nsXMLContentSink::HandleStartElement(const PRUnichar *aName,
|
||||
// properly (eg form state restoration).
|
||||
if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
|
||||
if (nodeInfo->NameAtom() == nsGkAtoms::input ||
|
||||
nodeInfo->NameAtom() == nsGkAtoms::button) {
|
||||
nodeInfo->NameAtom() == nsGkAtoms::button ||
|
||||
nodeInfo->NameAtom() == nsGkAtoms::menuitem) {
|
||||
content->DoneCreatingElement();
|
||||
} else if (nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) {
|
||||
mCurrentHead = content;
|
||||
|
@ -343,7 +343,8 @@ txMozillaXMLOutput::endElement()
|
||||
}
|
||||
} else if (ns == kNameSpaceID_XHTML &&
|
||||
(localName == nsGkAtoms::input ||
|
||||
localName == nsGkAtoms::button)) {
|
||||
localName == nsGkAtoms::button ||
|
||||
localName == nsGkAtoms::menuitem)) {
|
||||
element->DoneCreatingElement();
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ VPATH = @srcdir@
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = xul
|
||||
PARALLEL_DIRS = src
|
||||
PARALLEL_DIRS = public src
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
PARALLEL_DIRS += test
|
||||
|
51
content/xul/content/public/Makefile.in
Normal file
51
content/xul/content/public/Makefile.in
Normal file
@ -0,0 +1,51 @@
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Foundation
|
||||
# Portions created by the Initial Developer are Copyright (C) 2011
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
DEPTH = ../../../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = xul
|
||||
|
||||
ifdef MOZ_XUL
|
||||
XPIDLSRCS = \
|
||||
nsIXULContextMenuBuilder.idl \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
73
content/xul/content/public/nsIXULContextMenuBuilder.idl
Normal file
73
content/xul/content/public/nsIXULContextMenuBuilder.idl
Normal file
@ -0,0 +1,73 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMDocumentFragment;
|
||||
|
||||
/**
|
||||
* An interface for initialization of XUL context menu builder
|
||||
* and for triggering of menuitem actions with assigned identifiers.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(f0c35053-14cc-4e23-a9db-f9a68fae8375)]
|
||||
interface nsIXULContextMenuBuilder : nsISupports
|
||||
{
|
||||
|
||||
/**
|
||||
* Initialize builder before building.
|
||||
*
|
||||
* @param aDocumentFragment the fragment that will be used to append top
|
||||
* level elements
|
||||
*
|
||||
* @param aGeneratedAttrName the name of the attribute that will be used
|
||||
* to mark elements as generated.
|
||||
*
|
||||
* @param aIdentAttrName the name of the attribute that will be used for
|
||||
* menuitem identification.
|
||||
*/
|
||||
void init(in nsIDOMDocumentFragment aDocumentFragment,
|
||||
in AString aGeneratedAttrName,
|
||||
in AString aIdentAttrName);
|
||||
|
||||
/**
|
||||
* Invoke the action of the menuitem with assigned identifier aIdent.
|
||||
*
|
||||
* @param aIdent the menuitem identifier
|
||||
*/
|
||||
void click(in DOMString aIdent);
|
||||
|
||||
};
|
@ -54,6 +54,7 @@ ifdef MOZ_XUL
|
||||
CPPSRCS += \
|
||||
nsXULElement.cpp \
|
||||
nsXULPopupListener.cpp \
|
||||
nsXULContextMenuBuilder.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
268
content/xul/content/src/nsXULContextMenuBuilder.cpp
Normal file
268
content/xul/content/src/nsXULContextMenuBuilder.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsContentCreatorFunctions.h"
|
||||
#include "nsIDOMHTMLElement.h"
|
||||
#include "nsIDOMHTMLMenuItemElement.h"
|
||||
#include "nsXULContextMenuBuilder.h"
|
||||
|
||||
|
||||
nsXULContextMenuBuilder::nsXULContextMenuBuilder()
|
||||
: mCurrentIdent(0)
|
||||
{
|
||||
}
|
||||
|
||||
nsXULContextMenuBuilder::~nsXULContextMenuBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULContextMenuBuilder)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULContextMenuBuilder)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFragment)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULContextMenuBuilder)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFragment)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCurrentNode)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mElements)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULContextMenuBuilder)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULContextMenuBuilder)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULContextMenuBuilder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIMenuBuilder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXULContextMenuBuilder)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMenuBuilder)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::OpenContainer(const nsAString& aLabel)
|
||||
{
|
||||
if (!mFragment) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!mCurrentNode) {
|
||||
mCurrentNode = mFragment;
|
||||
} else {
|
||||
nsCOMPtr<nsIContent> menu;
|
||||
nsresult rv = CreateElement(nsGkAtoms::menu, getter_AddRefs(menu));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
menu->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aLabel, PR_FALSE);
|
||||
|
||||
nsCOMPtr<nsIContent> menuPopup;
|
||||
rv = CreateElement(nsGkAtoms::menupopup, getter_AddRefs(menuPopup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = menu->AppendChildTo(menuPopup, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mCurrentNode->AppendChildTo(menu, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCurrentNode = menuPopup;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::AddItemFor(nsIDOMHTMLMenuItemElement* aElement,
|
||||
PRBool aCanLoadIcon)
|
||||
{
|
||||
if (!mFragment) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> menuitem;
|
||||
nsresult rv = CreateElement(nsGkAtoms::menuitem, getter_AddRefs(menuitem));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString type;
|
||||
aElement->GetType(type);
|
||||
if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
|
||||
// The menu is only temporary, so we don't need to handle
|
||||
// the radio type precisely.
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("checkbox"), PR_FALSE);
|
||||
PRBool checked;
|
||||
aElement->GetChecked(&checked);
|
||||
if (checked) {
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::checked,
|
||||
NS_LITERAL_STRING("true"), PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString label;
|
||||
aElement->GetLabel(label);
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, PR_FALSE);
|
||||
|
||||
nsAutoString icon;
|
||||
aElement->GetIcon(icon);
|
||||
if (!icon.IsEmpty()) {
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
|
||||
NS_LITERAL_STRING("menuitem-iconic"), PR_FALSE);
|
||||
if (aCanLoadIcon) {
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::image, icon, PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
PRBool disabled;
|
||||
aElement->GetDisabled(&disabled);
|
||||
if (disabled) {
|
||||
menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled,
|
||||
NS_LITERAL_STRING("true"), PR_FALSE);
|
||||
}
|
||||
|
||||
nsAutoString ident;
|
||||
ident.AppendInt(mCurrentIdent++);
|
||||
|
||||
menuitem->SetAttr(kNameSpaceID_None, mIdentAttr, ident, PR_FALSE);
|
||||
|
||||
rv = mCurrentNode->AppendChildTo(menuitem, PR_FALSE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mElements.AppendObject(aElement);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::AddSeparator()
|
||||
{
|
||||
if (!mFragment) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> menuseparator;
|
||||
nsresult rv = CreateElement(nsGkAtoms::menuseparator,
|
||||
getter_AddRefs(menuseparator));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mCurrentNode->AppendChildTo(menuseparator, PR_FALSE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::UndoAddSeparator()
|
||||
{
|
||||
if (!mFragment) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
PRUint32 count = mCurrentNode->GetChildCount();
|
||||
if (!count ||
|
||||
mCurrentNode->GetChildAt(count - 1)->Tag() != nsGkAtoms::menuseparator) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return mCurrentNode->RemoveChildAt(count - 1, PR_FALSE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::CloseContainer()
|
||||
{
|
||||
if (!mFragment) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (mCurrentNode == mFragment) {
|
||||
mCurrentNode = nsnull;
|
||||
} else {
|
||||
nsIContent* parent = mCurrentNode->GetParent();
|
||||
mCurrentNode = parent->GetParent();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::Init(nsIDOMDocumentFragment* aDocumentFragment,
|
||||
const nsAString& aGeneratedAttrName,
|
||||
const nsAString& aIdentAttrName)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDocumentFragment);
|
||||
|
||||
mFragment = do_QueryInterface(aDocumentFragment);
|
||||
mDocument = mFragment->GetOwnerDocument();
|
||||
mGeneratedAttr = do_GetAtom(aGeneratedAttrName);
|
||||
mIdentAttr = do_GetAtom(aIdentAttrName);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULContextMenuBuilder::Click(const nsAString& aIdent)
|
||||
{
|
||||
PRInt32 rv;
|
||||
PRInt32 idx = nsString(aIdent).ToInteger(&rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsCOMPtr<nsIDOMHTMLElement> element = mElements.SafeObjectAt(idx);
|
||||
if (element) {
|
||||
element->Click();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsXULContextMenuBuilder::CreateElement(nsIAtom* aTag, nsIContent** aResult)
|
||||
{
|
||||
*aResult = nsnull;
|
||||
|
||||
nsCOMPtr<nsINodeInfo> nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(
|
||||
aTag, nsnull, kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsresult rv = NS_NewElement(aResult, kNameSpaceID_XUL, nodeInfo.forget(),
|
||||
mozilla::dom::NOT_FROM_PARSER);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
(*aResult)->SetAttr(kNameSpaceID_None, mGeneratedAttr, EmptyString(),
|
||||
PR_FALSE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
71
content/xul/content/src/nsXULContextMenuBuilder.h
Normal file
71
content/xul/content/src/nsXULContextMenuBuilder.h
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIMenuBuilder.h"
|
||||
#include "nsIXULContextMenuBuilder.h"
|
||||
#include "nsIDOMDocumentFragment.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
class nsXULContextMenuBuilder : public nsIMenuBuilder,
|
||||
public nsIXULContextMenuBuilder
|
||||
{
|
||||
public:
|
||||
nsXULContextMenuBuilder();
|
||||
virtual ~nsXULContextMenuBuilder();
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULContextMenuBuilder,
|
||||
nsIMenuBuilder)
|
||||
NS_DECL_NSIMENUBUILDER
|
||||
|
||||
NS_DECL_NSIXULCONTEXTMENUBUILDER
|
||||
|
||||
protected:
|
||||
nsresult CreateElement(nsIAtom* aTag, nsIContent** aResult);
|
||||
|
||||
nsCOMPtr<nsIContent> mFragment;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsIAtom> mGeneratedAttr;
|
||||
nsCOMPtr<nsIAtom> mIdentAttr;
|
||||
|
||||
nsCOMPtr<nsIContent> mCurrentNode;
|
||||
PRInt32 mCurrentIdent;
|
||||
|
||||
nsCOMArray<nsIDOMHTMLElement> mElements;
|
||||
};
|
@ -285,6 +285,7 @@
|
||||
#include "nsIDOMHTMLLinkElement.h"
|
||||
#include "nsIDOMHTMLMapElement.h"
|
||||
#include "nsIDOMHTMLMenuElement.h"
|
||||
#include "nsIDOMHTMLMenuItemElement.h"
|
||||
#include "nsIDOMHTMLMetaElement.h"
|
||||
#include "nsIDOMHTMLModElement.h"
|
||||
#include "nsIDOMHTMLOListElement.h"
|
||||
@ -836,6 +837,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
||||
ELEMENT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(HTMLMenuElement, nsElementSH,
|
||||
ELEMENT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(HTMLMenuItemElement, nsElementSH,
|
||||
ELEMENT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(HTMLMetaElement, nsElementSH,
|
||||
ELEMENT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(HTMLModElement, nsElementSH,
|
||||
@ -2695,6 +2698,11 @@ nsDOMClassInfo::Init()
|
||||
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(HTMLMenuItemElement, nsIDOMHTMLMenuItemElement)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLMenuItemElement)
|
||||
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(HTMLMetaElement, nsIDOMHTMLMetaElement)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLMetaElement)
|
||||
DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
|
||||
@ -7493,6 +7501,7 @@ nsEventReceiverSH::ReallyIsEventName(jsid id, jschar aFirstChar)
|
||||
id == sOnratechange_id);
|
||||
case 's' :
|
||||
return (id == sOnscroll_id ||
|
||||
id == sOnshow_id ||
|
||||
id == sOnselect_id ||
|
||||
id == sOnsubmit_id ||
|
||||
id == sOnseeked_id ||
|
||||
|
@ -120,6 +120,7 @@ DOMCI_CLASS(HTMLLegendElement)
|
||||
DOMCI_CLASS(HTMLLinkElement)
|
||||
DOMCI_CLASS(HTMLMapElement)
|
||||
DOMCI_CLASS(HTMLMenuElement)
|
||||
DOMCI_CLASS(HTMLMenuItemElement)
|
||||
DOMCI_CLASS(HTMLMetaElement)
|
||||
DOMCI_CLASS(HTMLModElement)
|
||||
DOMCI_CLASS(HTMLOListElement)
|
||||
|
@ -55,6 +55,7 @@ SDK_XPIDLSRCS = \
|
||||
nsIDOMHTMLBodyElement.idl \
|
||||
nsIDOMHTMLButtonElement.idl \
|
||||
nsIDOMHTMLCollection.idl \
|
||||
nsIDOMHTMLCommandElement.idl \
|
||||
nsIDOMHTMLDataListElement.idl \
|
||||
nsIDOMHTMLDListElement.idl \
|
||||
nsIDOMHTMLDirectoryElement.idl \
|
||||
@ -80,6 +81,7 @@ SDK_XPIDLSRCS = \
|
||||
nsIDOMHTMLLinkElement.idl \
|
||||
nsIDOMHTMLMapElement.idl \
|
||||
nsIDOMHTMLMenuElement.idl \
|
||||
nsIDOMHTMLMenuItemElement.idl \
|
||||
nsIDOMHTMLMetaElement.idl \
|
||||
nsIDOMHTMLModElement.idl \
|
||||
nsIDOMHTMLOListElement.idl \
|
||||
|
59
dom/interfaces/html/nsIDOMHTMLCommandElement.idl
Normal file
59
dom/interfaces/html/nsIDOMHTMLCommandElement.idl
Normal file
@ -0,0 +1,59 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMHTMLElement.idl"
|
||||
|
||||
/**
|
||||
* The nsIDOMHTMLCommandElement interface is the interface to a HTML
|
||||
* <command> element.
|
||||
*
|
||||
* For more information on this interface, please see
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#the-command-element
|
||||
*
|
||||
* @status UNDER_DEVELOPMENT
|
||||
*/
|
||||
|
||||
[scriptable, uuid(df4a19b4-81f1-412e-a971-fcbe7312a9b6)]
|
||||
interface nsIDOMHTMLCommandElement : nsIDOMHTMLElement
|
||||
{
|
||||
attribute DOMString type;
|
||||
attribute DOMString label;
|
||||
attribute DOMString icon;
|
||||
attribute boolean disabled;
|
||||
attribute boolean defaultChecked;
|
||||
attribute boolean checked;
|
||||
attribute DOMString radiogroup;
|
||||
};
|
@ -50,8 +50,11 @@
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/
|
||||
*/
|
||||
|
||||
[scriptable, uuid(318d9314-f97b-4b7e-96ff-95f0cb203fdf)]
|
||||
[scriptable, uuid(43aa6818-f67f-420c-a400-59a2668e9fe5)]
|
||||
interface nsIDOMHTMLMenuElement : nsIDOMHTMLElement
|
||||
{
|
||||
attribute boolean compact;
|
||||
|
||||
attribute DOMString type;
|
||||
attribute DOMString label;
|
||||
};
|
||||
|
49
dom/interfaces/html/nsIDOMHTMLMenuItemElement.idl
Normal file
49
dom/interfaces/html/nsIDOMHTMLMenuItemElement.idl
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMHTMLCommandElement.idl"
|
||||
|
||||
/**
|
||||
* The nsIDOMHTMLMenuItemElement interface is the interface to a HTML
|
||||
* <menuitem> element.
|
||||
*
|
||||
* @status UNDER_DEVELOPMENT
|
||||
*/
|
||||
|
||||
[scriptable, uuid(613f28ee-01f5-42dc-8224-161f85f0f20b)]
|
||||
interface nsIDOMHTMLMenuItemElement : nsIDOMHTMLCommandElement
|
||||
{
|
||||
};
|
@ -39,8 +39,9 @@
|
||||
#include "domstubs.idl"
|
||||
|
||||
interface nsIDOMDOMStringMap;
|
||||
interface nsIDOMHTMLMenuElement;
|
||||
|
||||
[scriptable, uuid(4012e9a9-f6fb-48b3-9a80-b096c1dcb5ba)]
|
||||
[scriptable, uuid(0c3b4b63-30b2-4c93-906d-f983ee9af584)]
|
||||
interface nsIDOMNSHTMLElement : nsISupports
|
||||
{
|
||||
readonly attribute long offsetTop;
|
||||
@ -71,7 +72,8 @@ interface nsIDOMNSHTMLElement : nsISupports
|
||||
|
||||
[optional_argc] void scrollIntoView([optional] in boolean top);
|
||||
|
||||
readonly attribute nsIDOMHTMLMenuElement contextMenu;
|
||||
attribute boolean spellcheck;
|
||||
|
||||
|
||||
readonly attribute nsIDOMDOMStringMap dataset;
|
||||
};
|
||||
|
@ -147,6 +147,7 @@ EDITOR_ATOM(legend, "legend")
|
||||
EDITOR_ATOM(li, "li")
|
||||
EDITOR_ATOM(map, "map")
|
||||
EDITOR_ATOM(mark, "mark")
|
||||
EDITOR_ATOM(menuitem, "menuitem")
|
||||
EDITOR_ATOM(mozdirty, "_moz_dirty")
|
||||
EDITOR_ATOM(mozEditorBogusNode, "_moz_editor_bogus_node")
|
||||
EDITOR_ATOM(name, "name")
|
||||
|
@ -662,7 +662,8 @@ static const nsElementInfo kElements[eHTMLTag_userdefined] = {
|
||||
ELEM(map, PR_TRUE, PR_TRUE, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT),
|
||||
ELEM(mark, PR_TRUE, PR_TRUE, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
|
||||
ELEM(marquee, PR_FALSE, PR_FALSE, GROUP_NONE, GROUP_NONE),
|
||||
ELEM(menu, PR_TRUE, PR_FALSE, GROUP_BLOCK, GROUP_LI),
|
||||
ELEM(menu, PR_TRUE, PR_TRUE, GROUP_BLOCK, GROUP_LI | GROUP_FLOW_ELEMENT),
|
||||
ELEM(menuitem, PR_FALSE, PR_FALSE, GROUP_NONE, GROUP_NONE),
|
||||
ELEM(meta, PR_FALSE, PR_FALSE, GROUP_HEAD_CONTENT, GROUP_NONE),
|
||||
ELEM(multicol, PR_FALSE, PR_FALSE, GROUP_NONE, GROUP_NONE),
|
||||
ELEM(nav, PR_TRUE, PR_TRUE, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
|
||||
|
@ -213,6 +213,7 @@ members = [
|
||||
# but it is also present in other objects where it isn't shadowed.
|
||||
# Quick stubs handle the shadowing the same as XPConnect.
|
||||
'nsIDOMHTMLCollection.length',
|
||||
'nsIDOMHTMLCommandElement.*',
|
||||
'nsIDOMHTMLDocument.body',
|
||||
'nsIDOMHTMLDocument.getElementsByName',
|
||||
'nsIDOMHTMLDocument.anchors',
|
||||
@ -261,6 +262,8 @@ members = [
|
||||
'nsIDOMHTMLInputElement.selectionDirection',
|
||||
'nsIDOMHTMLInputElement.setSelectionRange',
|
||||
'nsIDOMHTMLLinkElement.disabled',
|
||||
'nsIDOMHTMLMenuElement.*',
|
||||
'nsIDOMHTMLMenuItemElement.*',
|
||||
'nsIDOMHTMLOptionElement.index',
|
||||
'nsIDOMHTMLOptionElement.selected',
|
||||
'nsIDOMHTMLOptionElement.form',
|
||||
|
@ -573,6 +573,10 @@ ul, menu, dir {
|
||||
-moz-padding-start: 40px;
|
||||
}
|
||||
|
||||
menu[type="context"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
|
@ -269,6 +269,7 @@
|
||||
@BINPATH@/components/xpcom_xpti.xpt
|
||||
@BINPATH@/components/xpconnect.xpt
|
||||
@BINPATH@/components/xulapp.xpt
|
||||
@BINPATH@/components/xul.xpt
|
||||
@BINPATH@/components/xuldoc.xpt
|
||||
@BINPATH@/components/xultmpl.xpt
|
||||
@BINPATH@/components/zipwriter.xpt
|
||||
|
@ -544,7 +544,8 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
|
||||
return;
|
||||
}
|
||||
if (aName == nsHtml5Atoms::input ||
|
||||
aName == nsHtml5Atoms::button) {
|
||||
aName == nsHtml5Atoms::button ||
|
||||
aName == nsHtml5Atoms::menuitem) {
|
||||
if (!formPointer) {
|
||||
// If form inputs don't belong to a form, their state preservation
|
||||
// won't work right without an append notification flush at this
|
||||
|
@ -139,7 +139,8 @@ HTML_HTMLELEMENT_TAG(listing)
|
||||
HTML_TAG(map, Map)
|
||||
HTML_HTMLELEMENT_TAG(mark)
|
||||
HTML_TAG(marquee, Div)
|
||||
HTML_TAG(menu, Shared)
|
||||
HTML_TAG(menu, Menu)
|
||||
HTML_TAG(menuitem, MenuItem)
|
||||
HTML_TAG(meta, Meta)
|
||||
HTML_TAG(multicol, Span)
|
||||
HTML_HTMLELEMENT_TAG(nav)
|
||||
|
@ -856,6 +856,15 @@ const nsHTMLElement gHTMLElements[] = {
|
||||
/*special props, prop-range*/ 0,kDefaultPropRange,
|
||||
/*special parents,kids*/ 0,&gULKids,
|
||||
},
|
||||
{
|
||||
/*tag*/ eHTMLTag_menuitem,
|
||||
/*req-parent excl-parent*/ eHTMLTag_unknown,eHTMLTag_unknown,
|
||||
/*rootnodes,endrootnodes*/ &gRootTags,&gRootTags,
|
||||
/*autoclose starttags and endtags*/ 0,0,0,0,
|
||||
/*parent,incl,exclgroups*/ kFlowEntity, kNone, kNone,
|
||||
/*special props, prop-range*/ kNonContainer,kDefaultPropRange,
|
||||
/*special parents,kids*/ 0,0,
|
||||
},
|
||||
{
|
||||
/*tag*/ eHTMLTag_meta,
|
||||
/*req-parent excl-parent*/ eHTMLTag_unknown,eHTMLTag_unknown,
|
||||
|
@ -195,6 +195,8 @@ static const PRUnichar sHTMLTagUnicodeName_marquee[] =
|
||||
{'m', 'a', 'r', 'q', 'u', 'e', 'e', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_menu[] =
|
||||
{'m', 'e', 'n', 'u', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_menuitem[] =
|
||||
{'m', 'e', 'n', 'u', 'i', 't', 'e', 'm', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_meta[] =
|
||||
{'m', 'e', 't', 'a', '\0'};
|
||||
static const PRUnichar sHTMLTagUnicodeName_multicol[] =
|
||||
|
@ -93,6 +93,7 @@ EXTRA_PP_JS_MODULES = \
|
||||
LightweightThemeConsumer.jsm \
|
||||
Services.jsm \
|
||||
WindowDraggingUtils.jsm \
|
||||
PageMenu.jsm \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
171
toolkit/content/PageMenu.jsm
Normal file
171
toolkit/content/PageMenu.jsm
Normal file
@ -0,0 +1,171 @@
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is Mozilla code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Foundation
|
||||
# Portions created by the Initial Developer are Copyright (C) 2011
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the LGPL or the GPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
let EXPORTED_SYMBOLS = ["PageMenu"];
|
||||
|
||||
function PageMenu() {
|
||||
}
|
||||
|
||||
PageMenu.prototype = {
|
||||
PAGEMENU_ATTR: "pagemenu",
|
||||
GENERATED_ATTR: "generated",
|
||||
IDENT_ATTR: "ident",
|
||||
|
||||
popup: null,
|
||||
builder: null,
|
||||
|
||||
init: function(aTarget, aPopup) {
|
||||
var pageMenu = null;
|
||||
var target = aTarget;
|
||||
while (target) {
|
||||
var contextMenu = target.contextMenu;
|
||||
if (contextMenu) {
|
||||
pageMenu = contextMenu;
|
||||
break;
|
||||
}
|
||||
target = target.parentNode;
|
||||
}
|
||||
|
||||
if (!pageMenu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var insertionPoint = this.getInsertionPoint(aPopup);
|
||||
if (!insertionPoint) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu);
|
||||
pageMenu.sendShowEvent();
|
||||
// the show event is not cancelable, so no need to check a result here
|
||||
|
||||
var fragment = aPopup.ownerDocument.createDocumentFragment();
|
||||
|
||||
var builder = pageMenu.createBuilder();
|
||||
if (!builder) {
|
||||
return false;
|
||||
}
|
||||
builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder);
|
||||
builder.init(fragment, this.GENERATED_ATTR, this.IDENT_ATTR);
|
||||
|
||||
pageMenu.build(builder);
|
||||
|
||||
var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR);
|
||||
if (pos == "end") {
|
||||
insertionPoint.appendChild(fragment);
|
||||
} else {
|
||||
insertionPoint.insertBefore(fragment,
|
||||
insertionPoint.firstChild);
|
||||
}
|
||||
|
||||
this.builder = builder;
|
||||
this.popup = aPopup;
|
||||
|
||||
this.popup.addEventListener("command", this);
|
||||
this.popup.addEventListener("popuphidden", this);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
var type = event.type;
|
||||
var target = event.target;
|
||||
if (type == "command" && target.hasAttribute(this.GENERATED_ATTR)) {
|
||||
this.builder.click(target.getAttribute(this.IDENT_ATTR));
|
||||
} else if (type == "popuphidden" && this.popup == target) {
|
||||
this.removeGeneratedContent(this.popup);
|
||||
|
||||
this.popup.removeEventListener("popuphidden", this);
|
||||
this.popup.removeEventListener("command", this);
|
||||
|
||||
this.popup = null;
|
||||
this.builder = null;
|
||||
}
|
||||
},
|
||||
|
||||
getImmediateChild: function(element, tag) {
|
||||
var child = element.firstChild;
|
||||
while (child) {
|
||||
if (child.localName == tag) {
|
||||
return child;
|
||||
}
|
||||
child = child.nextSibling;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getInsertionPoint: function(aPopup) {
|
||||
if (aPopup.hasAttribute(this.PAGEMENU_ATTR))
|
||||
return aPopup;
|
||||
|
||||
var element = aPopup.firstChild;
|
||||
while (element) {
|
||||
if (element.localName == "menu") {
|
||||
var popup = this.getImmediateChild(element, "menupopup");
|
||||
if (popup) {
|
||||
var result = this.getInsertionPoint(popup);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
element = element.nextSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
removeGeneratedContent: function(aPopup) {
|
||||
var ungenerated = [];
|
||||
ungenerated.push(aPopup);
|
||||
|
||||
var count;
|
||||
while (0 != (count = ungenerated.length)) {
|
||||
var last = count - 1;
|
||||
var element = ungenerated[last];
|
||||
ungenerated.splice(last, 1);
|
||||
|
||||
var i = element.childNodes.length;
|
||||
while (i-- > 0) {
|
||||
var child = element.childNodes[i];
|
||||
if (!child.hasAttribute(this.GENERATED_ATTR)) {
|
||||
ungenerated.push(child);
|
||||
continue;
|
||||
}
|
||||
element.removeChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -536,6 +536,8 @@ class nsHashKey;
|
||||
#define NS_DEVICE_ORIENTATION (NS_DEVICE_ORIENTATION_START)
|
||||
#define NS_DEVICE_MOTION (NS_DEVICE_ORIENTATION_START+1)
|
||||
|
||||
#define NS_SHOW_EVENT 5000
|
||||
|
||||
/**
|
||||
* Return status for event processors, nsEventStatus, is defined in
|
||||
* nsEvent.h.
|
||||
|
Loading…
Reference in New Issue
Block a user