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:
Jan Varga 2011-08-08 19:31:32 +02:00
parent 07b3728d4c
commit a298beaa2e
54 changed files with 2363 additions and 89 deletions

View File

@ -36,6 +36,7 @@
#
# ***** END LICENSE BLOCK *****
<menuseparator id="page-menu-separator"/>
<menuitem id="spell-no-suggestions"
disabled="true"
label="&spellNoSuggestions.label;"/>

View File

@ -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.

View File

@ -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;"

View File

@ -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) {

View File

@ -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>

View File

@ -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);

View File

@ -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;"

View File

@ -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

View File

@ -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")

View File

@ -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,

View File

@ -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:

View File

@ -138,6 +138,7 @@ public:
eDOMEvents_paste,
eDOMEvents_open,
eDOMEvents_message,
eDOMEvents_show,
eDOMEvents_SVGLoad,
eDOMEvents_SVGUnload,
eDOMEvents_SVGAbort,

View File

@ -48,6 +48,8 @@ XPIDL_MODULE = content_html
XPIDLSRCS = \
nsIFormSubmitObserver.idl \
nsIPhonetic.idl \
nsIHTMLMenu.idl \
nsIMenuBuilder.idl \
$(NULL)
EXPORTS = \

View 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);
};

View 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();
};

View File

@ -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 \

View File

@ -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)

View File

@ -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)

View 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;
}

View 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;
};

View 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);
}
}

View 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;
};

View File

@ -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();

View File

@ -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)

View 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>

View File

@ -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>

View File

@ -1021,7 +1021,10 @@ SinkContext::AddLeaf(const nsIParserNode& aNode)
case eHTMLTag_input:
content->DoneCreatingElement();
break;
case eHTMLTag_menuitem:
content->DoneCreatingElement();
break;
default:

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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

View 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

View 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);
};

View File

@ -54,6 +54,7 @@ ifdef MOZ_XUL
CPPSRCS += \
nsXULElement.cpp \
nsXULPopupListener.cpp \
nsXULContextMenuBuilder.cpp \
$(NULL)
endif

View 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;
}

View 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;
};

View File

@ -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 ||

View File

@ -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)

View File

@ -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 \

View 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;
};

View File

@ -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;
};

View 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
{
};

View File

@ -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;
};

View File

@ -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")

View File

@ -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),

View File

@ -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',

View File

@ -573,6 +573,10 @@ ul, menu, dir {
-moz-padding-start: 40px;
}
menu[type="context"] {
display: none !important;
}
ol {
display: block;
list-style-type: decimal;

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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[] =

View File

@ -93,6 +93,7 @@ EXTRA_PP_JS_MODULES = \
LightweightThemeConsumer.jsm \
Services.jsm \
WindowDraggingUtils.jsm \
PageMenu.jsm \
$(NULL)
include $(topsrcdir)/config/rules.mk

View 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);
}
}
}
}

View File

@ -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.