Line data Source code
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/. */
6 :
7 : #include "mozilla/dom/HTMLMenuElement.h"
8 :
9 : #include "mozilla/BasicEvents.h"
10 : #include "mozilla/EventDispatcher.h"
11 : #include "mozilla/dom/HTMLMenuElementBinding.h"
12 : #include "mozilla/dom/HTMLMenuItemElement.h"
13 : #include "nsIMenuBuilder.h"
14 : #include "nsAttrValueInlines.h"
15 : #include "nsContentUtils.h"
16 : #include "nsIURI.h"
17 :
18 : #define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1"
19 :
20 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Menu)
21 :
22 : namespace mozilla {
23 : namespace dom {
24 :
25 : enum MenuType : uint8_t
26 : {
27 : MENU_TYPE_CONTEXT = 1,
28 : MENU_TYPE_TOOLBAR
29 : };
30 :
31 : static const nsAttrValue::EnumTable kMenuTypeTable[] = {
32 : { "context", MENU_TYPE_CONTEXT },
33 : { "toolbar", MENU_TYPE_TOOLBAR },
34 : { nullptr, 0 }
35 : };
36 :
37 : static const nsAttrValue::EnumTable* kMenuDefaultType =
38 : &kMenuTypeTable[1];
39 :
40 : enum SeparatorType
41 : {
42 : ST_TRUE_INIT = -1,
43 : ST_FALSE = 0,
44 : ST_TRUE = 1
45 : };
46 :
47 :
48 :
49 0 : HTMLMenuElement::HTMLMenuElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
50 0 : : nsGenericHTMLElement(aNodeInfo), mType(MENU_TYPE_TOOLBAR)
51 : {
52 0 : }
53 :
54 0 : HTMLMenuElement::~HTMLMenuElement()
55 : {
56 0 : }
57 :
58 0 : NS_IMPL_ISUPPORTS_INHERITED(HTMLMenuElement, nsGenericHTMLElement,
59 : nsIDOMHTMLMenuElement)
60 :
61 0 : NS_IMPL_ELEMENT_CLONE(HTMLMenuElement)
62 :
63 0 : NS_IMPL_BOOL_ATTR(HTMLMenuElement, Compact, compact)
64 0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMenuElement, Type, type,
65 : kMenuDefaultType->tag)
66 0 : NS_IMPL_STRING_ATTR(HTMLMenuElement, Label, label)
67 :
68 :
69 : void
70 0 : HTMLMenuElement::SendShowEvent()
71 : {
72 0 : nsCOMPtr<nsIDocument> document = GetComposedDoc();
73 0 : if (!document) {
74 0 : return;
75 : }
76 :
77 0 : WidgetEvent event(true, eShow);
78 0 : event.mFlags.mBubbles = false;
79 0 : event.mFlags.mCancelable = false;
80 :
81 0 : nsCOMPtr<nsIPresShell> shell = document->GetShell();
82 0 : if (!shell) {
83 0 : return;
84 : }
85 :
86 0 : RefPtr<nsPresContext> presContext = shell->GetPresContext();
87 0 : nsEventStatus status = nsEventStatus_eIgnore;
88 0 : EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
89 0 : &event, nullptr, &status);
90 : }
91 :
92 : already_AddRefed<nsIMenuBuilder>
93 0 : HTMLMenuElement::CreateBuilder()
94 : {
95 0 : if (mType != MENU_TYPE_CONTEXT) {
96 0 : return nullptr;
97 : }
98 :
99 0 : nsCOMPtr<nsIMenuBuilder> builder = do_CreateInstance(HTMLMENUBUILDER_CONTRACTID);
100 0 : NS_WARNING_ASSERTION(builder, "No builder available");
101 0 : return builder.forget();
102 : }
103 :
104 : void
105 0 : HTMLMenuElement::Build(nsIMenuBuilder* aBuilder)
106 : {
107 0 : if (!aBuilder) {
108 0 : return;
109 : }
110 :
111 0 : BuildSubmenu(EmptyString(), this, aBuilder);
112 : }
113 :
114 : nsresult
115 0 : HTMLMenuElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
116 : const nsAttrValue* aValue,
117 : const nsAttrValue* aOldValue, bool aNotify)
118 : {
119 0 : if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::type) {
120 0 : if (aValue) {
121 0 : mType = aValue->GetEnumValue();
122 : } else {
123 0 : mType = kMenuDefaultType->value;
124 : }
125 : }
126 :
127 0 : return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
128 0 : aOldValue, aNotify);
129 : }
130 :
131 : bool
132 0 : HTMLMenuElement::ParseAttribute(int32_t aNamespaceID,
133 : nsIAtom* aAttribute,
134 : const nsAString& aValue,
135 : nsAttrValue& aResult)
136 : {
137 0 : if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::type) {
138 0 : return aResult.ParseEnumValue(aValue, kMenuTypeTable, false,
139 0 : kMenuDefaultType);
140 : }
141 :
142 0 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
143 0 : aResult);
144 : }
145 :
146 : void
147 0 : HTMLMenuElement::BuildSubmenu(const nsAString& aLabel,
148 : nsIContent* aContent,
149 : nsIMenuBuilder* aBuilder)
150 : {
151 0 : aBuilder->OpenContainer(aLabel);
152 :
153 0 : int8_t separator = ST_TRUE_INIT;
154 0 : TraverseContent(aContent, aBuilder, separator);
155 :
156 0 : if (separator == ST_TRUE) {
157 0 : aBuilder->UndoAddSeparator();
158 : }
159 :
160 0 : aBuilder->CloseContainer();
161 0 : }
162 :
163 : // static
164 : bool
165 0 : HTMLMenuElement::CanLoadIcon(nsIContent* aContent, const nsAString& aIcon)
166 : {
167 0 : if (aIcon.IsEmpty()) {
168 0 : return false;
169 : }
170 :
171 0 : nsIDocument* doc = aContent->OwnerDoc();
172 :
173 0 : nsCOMPtr<nsIURI> baseURI = aContent->GetBaseURI();
174 0 : nsCOMPtr<nsIURI> uri;
175 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), aIcon, doc,
176 0 : baseURI);
177 :
178 0 : if (!uri) {
179 0 : return false;
180 : }
181 :
182 0 : return nsContentUtils::CanLoadImage(uri, aContent, doc,
183 0 : aContent->NodePrincipal());
184 : }
185 :
186 : void
187 0 : HTMLMenuElement::TraverseContent(nsIContent* aContent,
188 : nsIMenuBuilder* aBuilder,
189 : int8_t& aSeparator)
190 : {
191 0 : nsCOMPtr<nsIContent> child;
192 0 : for (child = aContent->GetFirstChild(); child;
193 0 : child = child->GetNextSibling()) {
194 0 : nsGenericHTMLElement* element = nsGenericHTMLElement::FromContent(child);
195 0 : if (!element) {
196 0 : continue;
197 : }
198 :
199 0 : if (child->IsHTMLElement(nsGkAtoms::menuitem)) {
200 0 : HTMLMenuItemElement* menuitem = HTMLMenuItemElement::FromContent(child);
201 :
202 0 : if (menuitem->IsHidden()) {
203 0 : continue;
204 : }
205 :
206 0 : nsAutoString label;
207 0 : menuitem->GetLabel(label);
208 0 : if (label.IsEmpty()) {
209 0 : continue;
210 : }
211 :
212 0 : nsAutoString icon;
213 0 : menuitem->GetIcon(icon);
214 :
215 0 : aBuilder->AddItemFor(menuitem, CanLoadIcon(child, icon));
216 :
217 0 : aSeparator = ST_FALSE;
218 0 : } else if (child->IsHTMLElement(nsGkAtoms::hr)) {
219 0 : aBuilder->AddSeparator();
220 0 : } else if (child->IsHTMLElement(nsGkAtoms::menu) && !element->IsHidden()) {
221 0 : if (child->HasAttr(kNameSpaceID_None, nsGkAtoms::label)) {
222 0 : nsAutoString label;
223 0 : child->GetAttr(kNameSpaceID_None, nsGkAtoms::label, label);
224 :
225 0 : BuildSubmenu(label, child, aBuilder);
226 :
227 0 : aSeparator = ST_FALSE;
228 : } else {
229 0 : AddSeparator(aBuilder, aSeparator);
230 :
231 0 : TraverseContent(child, aBuilder, aSeparator);
232 :
233 0 : AddSeparator(aBuilder, aSeparator);
234 : }
235 : }
236 : }
237 0 : }
238 :
239 : inline void
240 0 : HTMLMenuElement::AddSeparator(nsIMenuBuilder* aBuilder, int8_t& aSeparator)
241 : {
242 0 : if (aSeparator) {
243 0 : return;
244 : }
245 :
246 0 : aBuilder->AddSeparator();
247 0 : aSeparator = ST_TRUE;
248 : }
249 :
250 : JSObject*
251 0 : HTMLMenuElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
252 : {
253 0 : return HTMLMenuElementBinding::Wrap(aCx, this, aGivenProto);
254 : }
255 :
256 : } // namespace dom
257 : } // namespace mozilla
|