1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/HTMLMenuElement.h"
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/EventDispatcher.h"
11 #include "mozilla/dom/DocumentInlines.h"
12 #include "mozilla/dom/HTMLMenuElementBinding.h"
13 #include "mozilla/dom/HTMLMenuItemElement.h"
14 #include "nsIMenuBuilder.h"
15 #include "nsAttrValueInlines.h"
16 #include "nsContentUtils.h"
19 #define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1"
21 NS_IMPL_NS_NEW_HTML_ELEMENT(Menu
)
26 enum MenuType
: uint8_t { MENU_TYPE_CONTEXT
= 1, MENU_TYPE_TOOLBAR
};
28 static const nsAttrValue::EnumTable kMenuTypeTable
[] = {
29 {"context", MENU_TYPE_CONTEXT
},
30 {"toolbar", MENU_TYPE_TOOLBAR
},
33 static const nsAttrValue::EnumTable
* kMenuDefaultType
= &kMenuTypeTable
[1];
35 enum SeparatorType
{ ST_TRUE_INIT
= -1, ST_FALSE
= 0, ST_TRUE
= 1 };
37 HTMLMenuElement::HTMLMenuElement(
38 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
39 : nsGenericHTMLElement(std::move(aNodeInfo
)), mType(MENU_TYPE_TOOLBAR
) {}
41 HTMLMenuElement::~HTMLMenuElement() {}
43 NS_IMPL_ELEMENT_CLONE(HTMLMenuElement
)
45 void HTMLMenuElement::SendShowEvent() {
46 nsCOMPtr
<Document
> document
= GetComposedDoc();
51 WidgetEvent
event(true, eShow
);
52 event
.mFlags
.mBubbles
= false;
53 event
.mFlags
.mCancelable
= false;
55 RefPtr
<nsPresContext
> presContext
= document
->GetPresContext();
60 nsEventStatus status
= nsEventStatus_eIgnore
;
61 EventDispatcher::Dispatch(static_cast<nsIContent
*>(this), presContext
, &event
,
65 already_AddRefed
<nsIMenuBuilder
> HTMLMenuElement::CreateBuilder() {
66 if (mType
!= MENU_TYPE_CONTEXT
) {
70 nsCOMPtr
<nsIMenuBuilder
> builder
=
71 do_CreateInstance(HTMLMENUBUILDER_CONTRACTID
);
72 NS_WARNING_ASSERTION(builder
, "No builder available");
73 return builder
.forget();
76 void HTMLMenuElement::Build(nsIMenuBuilder
* aBuilder
) {
81 BuildSubmenu(EmptyString(), this, aBuilder
);
84 nsresult
HTMLMenuElement::AfterSetAttr(int32_t aNameSpaceID
, nsAtom
* aName
,
85 const nsAttrValue
* aValue
,
86 const nsAttrValue
* aOldValue
,
87 nsIPrincipal
* aSubjectPrincipal
,
89 if (aNameSpaceID
== kNameSpaceID_None
&& aName
== nsGkAtoms::type
) {
91 mType
= aValue
->GetEnumValue();
93 mType
= kMenuDefaultType
->value
;
97 return nsGenericHTMLElement::AfterSetAttr(
98 aNameSpaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
101 bool HTMLMenuElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
102 const nsAString
& aValue
,
103 nsIPrincipal
* aMaybeScriptedPrincipal
,
104 nsAttrValue
& aResult
) {
105 if (aNamespaceID
== kNameSpaceID_None
&& aAttribute
== nsGkAtoms::type
) {
106 return aResult
.ParseEnumValue(aValue
, kMenuTypeTable
, false,
110 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
111 aMaybeScriptedPrincipal
, aResult
);
114 void HTMLMenuElement::BuildSubmenu(const nsAString
& aLabel
,
115 nsIContent
* aContent
,
116 nsIMenuBuilder
* aBuilder
) {
117 aBuilder
->OpenContainer(aLabel
);
119 int8_t separator
= ST_TRUE_INIT
;
120 TraverseContent(aContent
, aBuilder
, separator
);
122 if (separator
== ST_TRUE
) {
123 aBuilder
->UndoAddSeparator();
126 aBuilder
->CloseContainer();
130 bool HTMLMenuElement::CanLoadIcon(nsIContent
* aContent
,
131 const nsAString
& aIcon
) {
132 if (aIcon
.IsEmpty()) {
136 Document
* doc
= aContent
->OwnerDoc();
138 nsCOMPtr
<nsIURI
> uri
;
139 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), aIcon
, doc
,
140 aContent
->GetBaseURI());
146 return nsContentUtils::CanLoadImage(uri
, aContent
, doc
,
147 aContent
->NodePrincipal());
150 void HTMLMenuElement::TraverseContent(nsIContent
* aContent
,
151 nsIMenuBuilder
* aBuilder
,
152 int8_t& aSeparator
) {
153 nsCOMPtr
<nsIContent
> child
;
154 for (child
= aContent
->GetFirstChild(); child
;
155 child
= child
->GetNextSibling()) {
156 nsGenericHTMLElement
* element
= nsGenericHTMLElement::FromNode(child
);
161 if (child
->IsHTMLElement(nsGkAtoms::menuitem
)) {
162 HTMLMenuItemElement
* menuitem
= HTMLMenuItemElement::FromNode(child
);
164 if (menuitem
->IsHidden()) {
169 menuitem
->GetLabel(label
);
170 if (label
.IsEmpty()) {
175 menuitem
->GetIcon(icon
);
177 aBuilder
->AddItemFor(menuitem
, CanLoadIcon(child
, icon
));
179 aSeparator
= ST_FALSE
;
180 } else if (child
->IsHTMLElement(nsGkAtoms::hr
)) {
181 aBuilder
->AddSeparator();
182 } else if (child
->IsHTMLElement(nsGkAtoms::menu
) && !element
->IsHidden()) {
183 if (child
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::label
)) {
185 child
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::label
, label
);
187 BuildSubmenu(label
, child
, aBuilder
);
189 aSeparator
= ST_FALSE
;
191 AddSeparator(aBuilder
, aSeparator
);
193 TraverseContent(child
, aBuilder
, aSeparator
);
195 AddSeparator(aBuilder
, aSeparator
);
201 inline void HTMLMenuElement::AddSeparator(nsIMenuBuilder
* aBuilder
,
202 int8_t& aSeparator
) {
207 aBuilder
->AddSeparator();
208 aSeparator
= ST_TRUE
;
211 JSObject
* HTMLMenuElement::WrapNode(JSContext
* aCx
,
212 JS::Handle
<JSObject
*> aGivenProto
) {
213 return HTMLMenuElement_Binding::Wrap(aCx
, this, aGivenProto
);
217 } // namespace mozilla